From 9e8d8ada470005521362770e0b24ec4b40722f6d Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Mon, 1 Jun 2020 19:04:41 -0400 Subject: [PATCH 001/133] emoji: Add spoon emoji with trigger `--o` --- .../net/runelite/client/plugins/emojis/Emoji.java | 1 + .../net/runelite/client/plugins/emojis/spoon.png | Bin 0 -> 120 bytes 2 files changed, 1 insertion(+) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/emojis/spoon.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/Emoji.java b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/Emoji.java index acee57ab0f..d4dfcc788d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/Emoji.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/Emoji.java @@ -91,6 +91,7 @@ enum Emoji GORILLA(":G"), PLEADING("(n_n)"), XD("Xd"), + SPOON("--o"), ; private static final Map emojiMap; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/emojis/spoon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/emojis/spoon.png new file mode 100644 index 0000000000000000000000000000000000000000..c838403711a6adfdc32576a74d6585c359cf2c56 GIT binary patch literal 120 zcmeAS@N?(olHy`uVBq!ia0vp@Ak4%JB>7u;-UBI~0G|-oSo29!mTjE5YAX;)O4v#Q z#TiS2{DK)Ap4~_Ta-=+6978y+Cns!3XK4DL@Q6W+QE2l)*9-?vhE`vW@3#UqrT|qj Nc)I$ztaD0e0sy@ literal 0 HcmV?d00001 From 61f732d3301a80d8aea8a56522726398d87b2a9d Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 30 Jan 2021 11:10:05 -0500 Subject: [PATCH 002/133] tile: add setter for ground object --- runelite-api/src/main/java/net/runelite/api/Tile.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/Tile.java b/runelite-api/src/main/java/net/runelite/api/Tile.java index be35c8c3d2..9c0565ea31 100644 --- a/runelite-api/src/main/java/net/runelite/api/Tile.java +++ b/runelite-api/src/main/java/net/runelite/api/Tile.java @@ -61,6 +61,13 @@ public interface Tile */ GroundObject getGroundObject(); + /** + * Sets the object on the ground layer of the tile. + * + * @param groundObject the ground object + */ + void setGroundObject(GroundObject groundObject); + /** * Gets the wall of the tile. * From 2b3ce15516c65703885c0ea5c74ae53971cd1bce Mon Sep 17 00:00:00 2001 From: Max Weber Date: Tue, 22 Dec 2020 12:00:31 -0700 Subject: [PATCH 003/133] don't use gson's reflection serialization on non RuneLite classes java >=16 disallows access to most private fields which makes these fail with the reflection type adapter --- .../net/runelite/http/api/RuneLiteAPI.java | 25 +++++- .../http/api/gson/ColorTypeAdapter.java | 80 ++++++++++++++++++ .../api/gson/IllegalReflectionExclusion.java | 58 +++++++++++++ .../http/api/gson/InstantTypeAdapter.java | 82 +++++++++++++++++++ .../http/api/ws/WebsocketGsonFactory.java | 4 +- .../http/api/gson/ColorTypeAdapterTest.java | 51 ++++++++++++ .../http/api/gson/InstantTypeAdapterTest.java | 49 +++++++++++ .../net/runelite/client/RuneLiteModule.java | 4 + .../client/account/SessionManager.java | 9 +- .../externalplugins/ExternalPluginClient.java | 11 ++- .../crowdsourcing/CrowdsourcingManager.java | 7 +- .../grandexchange/GrandExchangePlugin.java | 8 +- .../groundmarkers/GroundMarkerPlugin.java | 9 +- .../GroundMarkerSharingManager.java | 12 +-- .../ObjectIndicatorsPlugin.java | 8 +- .../screenmarkers/ScreenMarkerPlugin.java | 5 +- .../timetracking/clocks/ClockManager.java | 7 +- .../wiki/WikiSearchChatboxTextInput.java | 3 +- .../runelite/client/util/ImageCapture.java | 11 ++- 19 files changed, 402 insertions(+), 41 deletions(-) create mode 100644 http-api/src/main/java/net/runelite/http/api/gson/ColorTypeAdapter.java create mode 100644 http-api/src/main/java/net/runelite/http/api/gson/IllegalReflectionExclusion.java create mode 100644 http-api/src/main/java/net/runelite/http/api/gson/InstantTypeAdapter.java create mode 100644 http-api/src/test/java/net/runelite/http/api/gson/ColorTypeAdapterTest.java create mode 100644 http-api/src/test/java/net/runelite/http/api/gson/InstantTypeAdapterTest.java diff --git a/http-api/src/main/java/net/runelite/http/api/RuneLiteAPI.java b/http-api/src/main/java/net/runelite/http/api/RuneLiteAPI.java index 0b017518c8..dc483f35db 100644 --- a/http-api/src/main/java/net/runelite/http/api/RuneLiteAPI.java +++ b/http-api/src/main/java/net/runelite/http/api/RuneLiteAPI.java @@ -25,10 +25,16 @@ package net.runelite.http.api; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.awt.Color; import java.io.IOException; import java.io.InputStream; +import java.time.Instant; import java.util.Properties; import java.util.concurrent.TimeUnit; +import net.runelite.http.api.gson.ColorTypeAdapter; +import net.runelite.http.api.gson.InstantTypeAdapter; +import net.runelite.http.api.gson.IllegalReflectionExclusion; import okhttp3.HttpUrl; import okhttp3.Interceptor; import okhttp3.MediaType; @@ -46,7 +52,7 @@ public class RuneLiteAPI public static final String RUNELITE_MACHINEID = "RUNELITE-MACHINEID"; public static final OkHttpClient CLIENT; - public static final Gson GSON = new Gson(); + public static final Gson GSON; public static final MediaType JSON = MediaType.parse("application/json"); public static String userAgent; @@ -96,6 +102,23 @@ public class RuneLiteAPI } }) .build(); + + GsonBuilder gsonBuilder = new GsonBuilder(); + + gsonBuilder + .registerTypeAdapter(Instant.class, new InstantTypeAdapter()) + .registerTypeAdapter(Color.class, new ColorTypeAdapter()); + + boolean assertionsEnabled = false; + assert assertionsEnabled = true; + if (assertionsEnabled) + { + IllegalReflectionExclusion jbe = new IllegalReflectionExclusion(); + gsonBuilder.addSerializationExclusionStrategy(jbe); + gsonBuilder.addDeserializationExclusionStrategy(jbe); + } + + GSON = gsonBuilder.create(); } public static HttpUrl getSessionBase() diff --git a/http-api/src/main/java/net/runelite/http/api/gson/ColorTypeAdapter.java b/http-api/src/main/java/net/runelite/http/api/gson/ColorTypeAdapter.java new file mode 100644 index 0000000000..6f4b003df0 --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/gson/ColorTypeAdapter.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2020 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.http.api.gson; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.awt.Color; +import java.io.IOException; + +public class ColorTypeAdapter extends TypeAdapter +{ + @Override + public void write(JsonWriter out, Color value) throws IOException + { + if (value == null) + { + out.nullValue(); + return; + } + + int rgba = value.getRGB(); + out.beginObject() + .name("value") + .value(rgba) + .endObject(); + } + + @Override + public Color read(JsonReader in) throws IOException + { + switch (in.peek()) + { + case NULL: + in.nextNull(); + return null; + case BEGIN_OBJECT: + in.beginObject(); + double value = 0; + while (in.peek() != JsonToken.END_OBJECT) + { + switch (in.nextName()) + { + case "value": + value = in.nextDouble(); + break; + default: + in.skipValue(); + break; + } + } + in.endObject(); + return new Color((int) value, true); + } + return null; // throws + } +} diff --git a/http-api/src/main/java/net/runelite/http/api/gson/IllegalReflectionExclusion.java b/http-api/src/main/java/net/runelite/http/api/gson/IllegalReflectionExclusion.java new file mode 100644 index 0000000000..d7611cf83f --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/gson/IllegalReflectionExclusion.java @@ -0,0 +1,58 @@ +/* + * Copyright (c) 2020 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.http.api.gson; + +import com.google.gson.ExclusionStrategy; +import com.google.gson.FieldAttributes; +import java.lang.reflect.Modifier; + +public class IllegalReflectionExclusion implements ExclusionStrategy +{ + @Override + public boolean shouldSkipField(FieldAttributes f) + { + if (f.getDeclaringClass().getName().startsWith("net.runelite")) + { + return false; + } + + assert !Modifier.isPrivate(f.getDeclaringClass().getModifiers()) : "gsoning private class " + f.getDeclaringClass().getName(); + try + { + f.getDeclaringClass().getField(f.getName()); + } + catch (NoSuchFieldException e) + { + throw new AssertionError("gsoning private field " + f.getDeclaringClass() + "." + f.getName()); + } + return false; + } + + @Override + public boolean shouldSkipClass(Class clazz) + { + return false; + } +} diff --git a/http-api/src/main/java/net/runelite/http/api/gson/InstantTypeAdapter.java b/http-api/src/main/java/net/runelite/http/api/gson/InstantTypeAdapter.java new file mode 100644 index 0000000000..bf92af94b8 --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/gson/InstantTypeAdapter.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020 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.http.api.gson; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; +import java.io.IOException; +import java.time.Instant; + +// Just add water! +public class InstantTypeAdapter extends TypeAdapter +{ + @Override + public void write(JsonWriter out, Instant value) throws IOException + { + if (value == null) + { + out.nullValue(); + return; + } + + out.beginObject() + .name("seconds") + .value(value.getEpochSecond()) + .name("nanos") + .value(value.getNano()) + .endObject(); + } + + @Override + public Instant read(JsonReader in) throws IOException + { + if (in.peek() == JsonToken.NULL) + { + in.nextNull(); + return null; + } + + long seconds = 0; + int nanos = 0; + in.beginObject(); + while (in.peek() != JsonToken.END_OBJECT) + { + switch (in.nextName()) + { + case "nanos": + nanos = in.nextInt(); + break; + case "seconds": + seconds = in.nextLong(); + break; + } + } + in.endObject(); + + return Instant.ofEpochSecond(seconds, nanos); + } +} diff --git a/http-api/src/main/java/net/runelite/http/api/ws/WebsocketGsonFactory.java b/http-api/src/main/java/net/runelite/http/api/ws/WebsocketGsonFactory.java index 9d4e46e6ea..328d0aa1a7 100644 --- a/http-api/src/main/java/net/runelite/http/api/ws/WebsocketGsonFactory.java +++ b/http-api/src/main/java/net/runelite/http/api/ws/WebsocketGsonFactory.java @@ -25,11 +25,11 @@ package net.runelite.http.api.ws; import com.google.gson.Gson; -import com.google.gson.GsonBuilder; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; +import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.ws.messages.Handshake; import net.runelite.http.api.ws.messages.LoginResponse; import net.runelite.http.api.ws.messages.party.Join; @@ -76,7 +76,7 @@ public class WebsocketGsonFactory public static Gson build(final RuntimeTypeAdapterFactory factory) { - return new GsonBuilder() + return RuneLiteAPI.GSON.newBuilder() .registerTypeAdapterFactory(factory) .create(); } diff --git a/http-api/src/test/java/net/runelite/http/api/gson/ColorTypeAdapterTest.java b/http-api/src/test/java/net/runelite/http/api/gson/ColorTypeAdapterTest.java new file mode 100644 index 0000000000..d7f5e6fd4c --- /dev/null +++ b/http-api/src/test/java/net/runelite/http/api/gson/ColorTypeAdapterTest.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2021 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.http.api.gson; + +import java.awt.Color; +import net.runelite.http.api.RuneLiteAPI; +import org.junit.Assert; +import org.junit.Test; + +public class ColorTypeAdapterTest +{ + @Test + public void test() + { + test("null", null); + test("{\"value\":-13347208,\"falpha\":0.0}", new Color(0x12345678, false)); + test("{\"value\":305419896,\"falpha\":0.0}", new Color(0x12345678, true)); + test("{\"value\":-1.4221317E7,\"falpha\":0.0}", new Color(0xFF26FFFB, true)); + } + + private void test(String json, Color object) + { + Color parsed = RuneLiteAPI.GSON.fromJson(json, Color.class); + Assert.assertEquals(object, parsed); + String serialized = RuneLiteAPI.GSON.toJson(object); + Color roundTripped = RuneLiteAPI.GSON.fromJson(serialized, Color.class); + Assert.assertEquals(object, roundTripped); + } +} \ No newline at end of file diff --git a/http-api/src/test/java/net/runelite/http/api/gson/InstantTypeAdapterTest.java b/http-api/src/test/java/net/runelite/http/api/gson/InstantTypeAdapterTest.java new file mode 100644 index 0000000000..dab70a4f96 --- /dev/null +++ b/http-api/src/test/java/net/runelite/http/api/gson/InstantTypeAdapterTest.java @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2021 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.http.api.gson; + +import java.time.Instant; +import net.runelite.http.api.RuneLiteAPI; +import org.junit.Assert; +import org.junit.Test; + +public class InstantTypeAdapterTest +{ + @Test + public void test() + { + test("null", null); + test("{\"seconds\":1609538310,\"nanos\":291698903}", Instant.ofEpochSecond(1609538310, 291698903)); + } + + private void test(String json, Instant object) + { + Instant parsed = RuneLiteAPI.GSON.fromJson(json, Instant.class); + Assert.assertEquals(object, parsed); + String serialized = RuneLiteAPI.GSON.toJson(object); + Instant roundTripped = RuneLiteAPI.GSON.fromJson(serialized, Instant.class); + Assert.assertEquals(object, roundTripped); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java index 3376c62f77..8c80c67a37 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java @@ -24,6 +24,7 @@ */ package net.runelite.client; +import com.google.gson.Gson; import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.name.Names; @@ -51,6 +52,7 @@ import net.runelite.client.plugins.PluginManager; import net.runelite.client.task.Scheduler; import net.runelite.client.util.DeferredEventBus; import net.runelite.client.util.ExecutorServiceExceptionLogger; +import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.chat.ChatClient; import okhttp3.OkHttpClient; @@ -86,6 +88,8 @@ public class RuneLiteModule extends AbstractModule bind(PluginManager.class); bind(SessionManager.class); + bind(Gson.class).toInstance(RuneLiteAPI.GSON); + bind(Callbacks.class).to(Hooks.class); bind(EventBus.class) diff --git a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java index cadf0b4562..4b6bf1674e 100644 --- a/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java +++ b/runelite-client/src/main/java/net/runelite/client/account/SessionManager.java @@ -64,6 +64,7 @@ public class SessionManager private final WSClient wsClient; private final File sessionFile; private final AccountClient accountClient; + private final Gson gson; @Inject private SessionManager( @@ -71,13 +72,15 @@ public class SessionManager ConfigManager configManager, EventBus eventBus, WSClient wsClient, - OkHttpClient okHttpClient) + OkHttpClient okHttpClient, + Gson gson) { this.configManager = configManager; this.eventBus = eventBus; this.wsClient = wsClient; this.sessionFile = sessionfile; this.accountClient = new AccountClient(okHttpClient); + this.gson = gson; eventBus.register(this); } @@ -94,7 +97,7 @@ public class SessionManager try (FileInputStream in = new FileInputStream(sessionFile)) { - session = new Gson().fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), AccountSession.class); + session = gson.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), AccountSession.class); log.debug("Loaded session for {}", session.getUsername()); } @@ -124,7 +127,7 @@ public class SessionManager try (Writer fw = new OutputStreamWriter(new FileOutputStream(sessionFile), StandardCharsets.UTF_8)) { - new Gson().toJson(accountSession, fw); + gson.toJson(accountSession, fw); log.debug("Saved session to {}", sessionFile); } diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java index e6bda75032..ccf4b9fad3 100644 --- a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClient.java @@ -25,6 +25,7 @@ package net.runelite.client.externalplugins; import com.google.common.reflect.TypeToken; +import com.google.gson.Gson; import com.google.gson.JsonSyntaxException; import java.awt.image.BufferedImage; import java.io.ByteArrayInputStream; @@ -59,11 +60,13 @@ import okio.BufferedSource; public class ExternalPluginClient { private final OkHttpClient okHttpClient; + private final Gson gson; @Inject - private ExternalPluginClient(OkHttpClient okHttpClient) + private ExternalPluginClient(OkHttpClient okHttpClient, Gson gson) { this.okHttpClient = okHttpClient; + this.gson = gson; } public List downloadManifest() throws IOException, VerificationException @@ -94,7 +97,7 @@ public class ExternalPluginClient throw new VerificationException("Unable to verify external plugin manifest"); } - return RuneLiteAPI.GSON.fromJson(new String(data, StandardCharsets.UTF_8), + return gson.fromJson(new String(data, StandardCharsets.UTF_8), new TypeToken>() { }.getType()); @@ -156,7 +159,7 @@ public class ExternalPluginClient Request request = new Request.Builder() .url(url) - .post(RequestBody.create(RuneLiteAPI.JSON, RuneLiteAPI.GSON.toJson(plugins))) + .post(RequestBody.create(RuneLiteAPI.JSON, gson.toJson(plugins))) .build(); okHttpClient.newCall(request).enqueue(new Callback() @@ -190,7 +193,7 @@ public class ExternalPluginClient } // CHECKSTYLE:OFF - return RuneLiteAPI.GSON.fromJson(new InputStreamReader(res.body().byteStream()), new TypeToken>(){}.getType()); + return gson.fromJson(new InputStreamReader(res.body().byteStream()), new TypeToken>(){}.getType()); // CHECKSTYLE:ON } catch (JsonSyntaxException ex) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingManager.java index 661071a786..09d737c10f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/crowdsourcing/CrowdsourcingManager.java @@ -32,7 +32,6 @@ import java.util.List; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; -import net.runelite.http.api.RuneLiteAPI; import okhttp3.Call; import okhttp3.Callback; import okhttp3.MediaType; @@ -47,11 +46,13 @@ public class CrowdsourcingManager { private static final String CROWDSOURCING_BASE = "https://crowdsource.runescape.wiki/runelite"; private static final MediaType JSON = MediaType.parse("application/json; charset=utf-8"); - private static final Gson GSON = RuneLiteAPI.GSON; @Inject private OkHttpClient okHttpClient; + @Inject + private Gson gson; + private List data = new ArrayList<>(); public void storeEvent(Object event) @@ -77,7 +78,7 @@ public class CrowdsourcingManager Request r = new Request.Builder() .url(CROWDSOURCING_BASE) - .post(RequestBody.create(JSON, GSON.toJson(temp))) + .post(RequestBody.create(JSON, gson.toJson(temp))) .build(); okHttpClient.newCall(r).enqueue(new Callback() 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 d28f4b0323..f3d9b513b3 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 @@ -128,7 +128,6 @@ public class GrandExchangePlugin extends Plugin private static final String BUY_LIMIT_GE_TEXT = "
Buy limit: "; private static final String BUY_LIMIT_KEY = "buylimit"; - private static final Gson GSON = new Gson(); private static final Duration BUY_LIMIT_RESET = Duration.ofHours(4); static final String SEARCH_GRAND_EXCHANGE = "Search Grand Exchange"; @@ -183,6 +182,9 @@ public class GrandExchangePlugin extends Plugin @Inject private ConfigManager configManager; + @Inject + private Gson gson; + private Widget grandExchangeText; private Widget grandExchangeItem; private String grandExchangeExamine; @@ -253,12 +255,12 @@ public class GrandExchangePlugin extends Plugin { return null; } - return GSON.fromJson(offer, SavedOffer.class); + return gson.fromJson(offer, SavedOffer.class); } private void setOffer(int slot, SavedOffer offer) { - configManager.setRSProfileConfiguration("geoffer", Integer.toString(slot), GSON.toJson(offer)); + configManager.setRSProfileConfiguration("geoffer", Integer.toString(slot), gson.toJson(offer)); } private void deleteOffer(int slot) 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 c1e1774931..cfb6171941 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 @@ -73,8 +73,6 @@ public class GroundMarkerPlugin extends Plugin private static final String WALK_HERE = "Walk here"; private static final String REGION_PREFIX = "region_"; - private static final Gson GSON = new Gson(); - @Getter(AccessLevel.PACKAGE) private final List points = new ArrayList<>(); @@ -105,6 +103,9 @@ public class GroundMarkerPlugin extends Plugin @Inject private GroundMarkerSharingManager sharingManager; + @Inject + private Gson gson; + void savePoints(int regionId, Collection points) { if (points == null || points.isEmpty()) @@ -113,7 +114,7 @@ public class GroundMarkerPlugin extends Plugin return; } - String json = GSON.toJson(points); + String json = gson.toJson(points); configManager.setConfiguration(CONFIG_GROUP, REGION_PREFIX + regionId, json); } @@ -126,7 +127,7 @@ public class GroundMarkerPlugin extends Plugin } // CHECKSTYLE:OFF - return GSON.fromJson(json, new TypeToken>(){}.getType()); + return gson.fromJson(json, new TypeToken>(){}.getType()); // CHECKSTYLE:ON } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerSharingManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerSharingManager.java index 8899a81f0d..91c0e800ac 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerSharingManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/groundmarkers/GroundMarkerSharingManager.java @@ -53,7 +53,6 @@ import net.runelite.client.eventbus.Subscribe; import net.runelite.client.game.chatbox.ChatboxPanelManager; import net.runelite.client.menus.MenuManager; import net.runelite.client.menus.WidgetMenuOption; -import net.runelite.http.api.RuneLiteAPI; @Slf4j class GroundMarkerSharingManager @@ -61,22 +60,23 @@ class GroundMarkerSharingManager private static final WidgetMenuOption EXPORT_MARKERS_OPTION = new WidgetMenuOption("Export", "Ground Markers", WORLD_MAP_OPTION); private static final WidgetMenuOption IMPORT_MARKERS_OPTION = new WidgetMenuOption("Import", "Ground Markers", WORLD_MAP_OPTION); - private static final Gson GSON = RuneLiteAPI.GSON; - private final GroundMarkerPlugin plugin; private final Client client; private final MenuManager menuManager; private final ChatMessageManager chatMessageManager; private final ChatboxPanelManager chatboxPanelManager; + private final Gson gson; @Inject - private GroundMarkerSharingManager(GroundMarkerPlugin plugin, Client client, MenuManager menuManager, ChatMessageManager chatMessageManager, ChatboxPanelManager chatboxPanelManager) + private GroundMarkerSharingManager(GroundMarkerPlugin plugin, Client client, MenuManager menuManager, + ChatMessageManager chatMessageManager, ChatboxPanelManager chatboxPanelManager, Gson gson) { this.plugin = plugin; this.client = client; this.menuManager = menuManager; this.chatMessageManager = chatMessageManager; this.chatboxPanelManager = chatboxPanelManager; + this.gson = gson; } void addMenuOptions() @@ -135,7 +135,7 @@ class GroundMarkerSharingManager return; } - final String exportDump = GSON.toJson(activePoints); + final String exportDump = gson.toJson(activePoints); log.debug("Exported ground markers: {}", exportDump); @@ -173,7 +173,7 @@ class GroundMarkerSharingManager try { // CHECKSTYLE:OFF - importPoints = GSON.fromJson(clipboardText, new TypeToken>(){}.getType()); + importPoints = gson.fromJson(clipboardText, new TypeToken>(){}.getType()); // CHECKSTYLE:ON } catch (JsonSyntaxException e) 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 46a4a0924d..cfa99092a8 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 @@ -88,7 +88,6 @@ public class ObjectIndicatorsPlugin extends Plugin private static final String MARK = "Mark object"; private static final String UNMARK = "Unmark object"; - private final Gson GSON = new Gson(); @Getter(AccessLevel.PACKAGE) private final List objects = new ArrayList<>(); private final Map> points = new HashMap<>(); @@ -108,6 +107,9 @@ public class ObjectIndicatorsPlugin extends Plugin @Inject private ObjectIndicatorsConfig config; + @Inject + private Gson gson; + @Provides ObjectIndicatorsConfig provideConfig(ConfigManager configManager) { @@ -428,7 +430,7 @@ public class ObjectIndicatorsPlugin extends Plugin } else { - final String json = GSON.toJson(points); + final String json = gson.toJson(points); configManager.setConfiguration(CONFIG_GROUP, "region_" + id, json); } } @@ -442,7 +444,7 @@ public class ObjectIndicatorsPlugin extends Plugin return null; } - Set points = GSON.fromJson(json, new TypeToken>() + Set points = gson.fromJson(json, new TypeToken>() { }.getType()); // Prior to multiloc support the plugin would mark objects named "null", which breaks diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java index 88dca9d214..5ec747e3f5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenmarkers/ScreenMarkerPlugin.java @@ -88,6 +88,9 @@ public class ScreenMarkerPlugin extends Plugin @Inject private ScreenMarkerCreationOverlay overlay; + @Inject + private Gson gson; + @Getter @Inject private ColorPickerManager colorPickerManager; @@ -266,7 +269,6 @@ public class ScreenMarkerPlugin extends Plugin return; } - final Gson gson = new Gson(); final String json = gson .toJson(screenMarkers.stream().map(ScreenMarkerOverlay::getMarker).collect(Collectors.toList())); configManager.setConfiguration(CONFIG_GROUP, CONFIG_KEY, json); @@ -279,7 +281,6 @@ public class ScreenMarkerPlugin extends Plugin return Stream.empty(); } - final Gson gson = new Gson(); final List screenMarkerData = gson.fromJson(json, new TypeToken>() { }.getType()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockManager.java index 720c5633e9..30d16ff573 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockManager.java @@ -53,6 +53,9 @@ public class ClockManager @Inject private Notifier notifier; + @Inject + private Gson gson; + @Getter private final List timers = new CopyOnWriteArrayList<>(); @@ -183,7 +186,6 @@ public class ClockManager if (!Strings.isNullOrEmpty(timersJson)) { - final Gson gson = new Gson(); final List timers = gson.fromJson(timersJson, new TypeToken>() { }.getType()); @@ -200,7 +202,6 @@ public class ClockManager if (!Strings.isNullOrEmpty(stopwatchesJson)) { - final Gson gson = new Gson(); final List stopwatches = gson.fromJson(stopwatchesJson, new TypeToken>() { }.getType()); @@ -227,14 +228,12 @@ public class ClockManager void saveTimers() { - final Gson gson = new Gson(); final String json = gson.toJson(timers); configManager.setConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.TIMERS, json); } void saveStopwatches() { - final Gson gson = new Gson(); final String json = gson.toJson(stopwatches); configManager.setConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.STOPWATCHES, json); } 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 index 8552d90eef..622e4db98b 100644 --- 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 @@ -67,7 +67,6 @@ public class WikiSearchChatboxTextInput extends ChatboxTextInput 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(); @@ -78,7 +77,7 @@ public class WikiSearchChatboxTextInput extends ChatboxTextInput @Inject public WikiSearchChatboxTextInput(ChatboxPanelManager chatboxPanelManager, ClientThread clientThread, ScheduledExecutorService scheduledExecutorService, @Named("developerMode") final boolean developerMode, - OkHttpClient okHttpClient) + OkHttpClient okHttpClient, Gson gson) { super(chatboxPanelManager, clientThread); this.chatboxPanelManager = chatboxPanelManager; diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java b/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java index dfee412c03..e842005dba 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java @@ -26,6 +26,7 @@ package net.runelite.client.util; import com.google.common.base.Strings; +import com.google.gson.Gson; import java.awt.Toolkit; import java.awt.TrayIcon; import java.awt.datatransfer.Clipboard; @@ -54,7 +55,6 @@ import net.runelite.api.GameState; import net.runelite.api.WorldType; import net.runelite.client.Notifier; import static net.runelite.client.RuneLite.SCREENSHOT_DIR; -import net.runelite.http.api.RuneLiteAPI; import okhttp3.Call; import okhttp3.Callback; import okhttp3.HttpUrl; @@ -75,6 +75,7 @@ public class ImageCapture private final Client client; private final Notifier notifier; private final OkHttpClient okHttpClient; + private final Gson gson; private final String imgurClientId; @Inject @@ -82,12 +83,14 @@ public class ImageCapture final Client client, final Notifier notifier, final OkHttpClient okHttpClient, + final Gson gson, @Named("runelite.imgur.client.id") final String imgurClientId ) { this.client = client; this.notifier = notifier; this.okHttpClient = okHttpClient; + this.gson = gson; this.imgurClientId = imgurClientId; } @@ -204,7 +207,7 @@ public class ImageCapture */ private void uploadScreenshot(File screenshotFile, boolean notify) throws IOException { - String json = RuneLiteAPI.GSON.toJson(new ImageUploadRequest(screenshotFile)); + String json = gson.toJson(new ImageUploadRequest(screenshotFile)); Request request = new Request.Builder() .url(IMGUR_IMAGE_UPLOAD_URL) @@ -225,8 +228,8 @@ public class ImageCapture { try (InputStream in = response.body().byteStream()) { - ImageUploadResponse imageUploadResponse = RuneLiteAPI.GSON - .fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), ImageUploadResponse.class); + ImageUploadResponse imageUploadResponse = + gson.fromJson(new InputStreamReader(in, StandardCharsets.UTF_8), ImageUploadResponse.class); if (imageUploadResponse.isSuccess()) { From 243929826b99f3dad1605c48f85f000268bdc41e Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 24 Dec 2020 08:42:30 -0700 Subject: [PATCH 004/133] ReflectUtil: allow privateLookupIn cross-classloader with JDK-8173978 --- .../ExternalPluginClassLoader.java | 16 ++++- .../net/runelite/client/util/ReflectUtil.java | 62 ++++++++++++++++++- 2 files changed, 75 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java index 054a7779b5..5690c3d61e 100644 --- a/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java +++ b/runelite-client/src/main/java/net/runelite/client/externalplugins/ExternalPluginClassLoader.java @@ -24,18 +24,32 @@ */ package net.runelite.client.externalplugins; +import java.lang.invoke.MethodHandles; import java.net.URL; import java.net.URLClassLoader; import lombok.Getter; +import lombok.Setter; +import net.runelite.client.util.ReflectUtil; -class ExternalPluginClassLoader extends URLClassLoader +class ExternalPluginClassLoader extends URLClassLoader implements ReflectUtil.PrivateLookupableClassLoader { @Getter private final ExternalPluginManifest manifest; + @Getter + @Setter + private MethodHandles.Lookup lookup; + ExternalPluginClassLoader(ExternalPluginManifest manifest, URL[] urls) { super(urls, ExternalPluginClassLoader.class.getClassLoader()); this.manifest = manifest; + ReflectUtil.installLookupHelper(this); + } + + @Override + public Class defineClass0(String name, byte[] b, int off, int len) throws ClassFormatError + { + return super.defineClass(name, b, off, len); } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java b/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java index 60b3b305f1..fbcc28d28d 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ReflectUtil.java @@ -25,6 +25,8 @@ */ package net.runelite.client.util; +import com.google.common.io.ByteStreams; +import java.io.IOException; import java.lang.invoke.MethodHandles; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; @@ -36,7 +38,7 @@ public class ReflectUtil { } - public static MethodHandles.Lookup privateLookupIn(Class clazz) + public static MethodHandles.Lookup privateLookupIn(Class clazz) { try { @@ -44,7 +46,16 @@ public class ReflectUtil // we need to access it via reflection. This is preferred way because it's Java 9+ public api and is // likely to not change final Method privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class); - return (MethodHandles.Lookup) privateLookupIn.invoke(null, clazz, MethodHandles.lookup()); + MethodHandles.Lookup caller; + if (clazz.getClassLoader() instanceof PrivateLookupableClassLoader) + { + caller = ((PrivateLookupableClassLoader) clazz.getClassLoader()).getLookup(); + } + else + { + caller = MethodHandles.lookup(); + } + return (MethodHandles.Lookup) privateLookupIn.invoke(null, clazz, caller); } catch (InvocationTargetException | IllegalAccessException e) { @@ -69,4 +80,51 @@ public class ReflectUtil } } } + + public interface PrivateLookupableClassLoader + { + // define class is protected final so this needs a different name to become public + Class defineClass0(String name, byte[] b, int off, int len) throws ClassFormatError; + + MethodHandles.Lookup getLookup(); + void setLookup(MethodHandles.Lookup lookup); + } + + /** + * Allows private Lookups to be created for classes in this ClassLoader + *

+ * Due to JDK-8173978 it is impossible to create get a lookup with module scoped permissions when teleporting + * between modules. Since external plugins are loaded in a separate classloader to us they are contained in unique + * unnamed modules. Since we (via LambdaMetafactory) are creating a hidden class in that module, we require module + * scoped access to it, and since the methods can be private, we also require private access. The only way to get + * MODULE|PRIVATE is to either 1) invokedynamic in that class, 2) call MethodHandles.lookup() from that class, or + * 3) call privateLookupIn with an existing lookup with PRIVATE|MODULE created from a class in the same module. + * Our solution is to make classloaders call this method which will define a class in the classloader's unnamed + * module that calls MethodHandles.lookup() and stores it in the classloader for later use. + */ + public static void installLookupHelper(PrivateLookupableClassLoader cl) + { + try + { + String name = PrivateLookupHelper.class.getName(); + byte[] classData = ByteStreams.toByteArray(ReflectUtil.class.getResourceAsStream("/" + name.replace('.', '/') + ".class")); + Class clazz = cl.defineClass0(name, classData, 0, classData.length); + + // force to run + clazz.getConstructor().newInstance(); + } + catch (IOException | ReflectiveOperationException e) + { + throw new RuntimeException("unable to install lookup helper", e); + } + } + + public static class PrivateLookupHelper + { + static + { + PrivateLookupableClassLoader pcl = (PrivateLookupableClassLoader) PrivateLookupHelper.class.getClassLoader(); + pcl.setLookup(MethodHandles.lookup()); + } + } } From ca56ef10823aea1ecd275c0d3c3f9a3b17838ff9 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 30 Jan 2021 13:14:33 -0500 Subject: [PATCH 005/133] Apply default font to text with unicode characters MacOS does not support fallback fonts, and any character not in our RS fonts do not render correctly. We only render unicode characters a handful of places, mostly for the check mark/cross in overlays, and on the icon text field suggestion button. So this sets the font of those places to the default system font which can render them correctly. --- .../barrows/BarrowsBrotherSlainOverlay.java | 2 ++ .../plugins/cluescrolls/clues/EmoteClue.java | 5 ++- .../cluescrolls/clues/FaloTheBardClue.java | 2 ++ .../cluescrolls/clues/SkillChallengeClue.java | 18 +++++----- .../net/runelite/client/ui/FontManager.java | 36 +++++++++---------- .../client/ui/components/IconTextField.java | 9 ++--- .../ui/overlay/components/LineComponent.java | 30 +++++++++++----- .../ui/overlay/components/TextComponent.java | 24 ++++++++++++- 8 files changed, 83 insertions(+), 43 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/barrows/BarrowsBrotherSlainOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/barrows/BarrowsBrotherSlainOverlay.java index c7cdef0058..4e4eba0175 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/barrows/BarrowsBrotherSlainOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/barrows/BarrowsBrotherSlainOverlay.java @@ -34,6 +34,7 @@ import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; import net.runelite.api.Varbits; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.ui.FontManager; import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.ui.overlay.OverlayPanel; @@ -82,6 +83,7 @@ public class BarrowsBrotherSlainOverlay extends OverlayPanel panelComponent.getChildren().add(LineComponent.builder() .left(brother.getName()) .right(slain) + .rightFont(FontManager.getDefaultFont()) .rightColor(brotherSlain ? Color.GREEN : Color.RED) .build()); } 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 b59be0da14..ab3ac38982 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 @@ -45,6 +45,7 @@ import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR; import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin; +import static net.runelite.client.plugins.cluescrolls.clues.Enemy.*; import net.runelite.client.plugins.cluescrolls.clues.emote.Emote; import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.*; import static net.runelite.client.plugins.cluescrolls.clues.emote.Emote.BULL_ROARER; @@ -53,7 +54,7 @@ import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.*; import static net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit.SHANTAY_PASS; import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement; import static net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirements.*; -import static net.runelite.client.plugins.cluescrolls.clues.Enemy.*; +import net.runelite.client.ui.FontManager; import net.runelite.client.ui.overlay.OverlayUtil; import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.PanelComponent; @@ -258,6 +259,7 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu panelComponent.getChildren().add(LineComponent.builder() .left("STASH Unit:") .right(stashUnitBuilt ? UNICODE_CHECK_MARK : UNICODE_BALLOT_X) + .rightFont(FontManager.getDefaultFont()) .rightColor(stashUnitBuilt ? Color.GREEN : Color.RED) .build()); } @@ -292,6 +294,7 @@ public class EmoteClue extends ClueScroll implements TextClueScroll, LocationClu .left(requirement.getCollectiveName(client)) .leftColor(TITLED_CONTENT_COLOR) .right(combinedFulfilled ? UNICODE_CHECK_MARK : UNICODE_BALLOT_X) + .rightFont(FontManager.getDefaultFont()) .rightColor(equipmentFulfilled ? Color.GREEN : (combinedFulfilled ? Color.ORANGE : Color.RED)) .build()); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/FaloTheBardClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/FaloTheBardClue.java index 16974ffd16..c208d5238f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/FaloTheBardClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/FaloTheBardClue.java @@ -41,6 +41,7 @@ import net.runelite.client.plugins.cluescrolls.clues.item.AnyRequirementCollecti import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement; import net.runelite.client.plugins.cluescrolls.clues.item.RangeItemRequirement; import net.runelite.client.plugins.cluescrolls.clues.item.SingleItemRequirement; +import net.runelite.client.ui.FontManager; import net.runelite.client.ui.overlay.OverlayUtil; import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.PanelComponent; @@ -134,6 +135,7 @@ public class FaloTheBardClue extends ClueScroll implements TextClueScroll, NpcCl .left(requirement.getCollectiveName(plugin.getClient())) .leftColor(TITLED_CONTENT_COLOR) .right(inventoryFulfilled ? "\u2713" : "\u2717") + .rightFont(FontManager.getDefaultFont()) .rightColor(inventoryFulfilled ? Color.GREEN : Color.RED) .build()); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java index 67de8ad26a..41f6f283cb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java @@ -25,6 +25,11 @@ package net.runelite.client.plugins.cluescrolls.clues; import com.google.common.collect.ImmutableSet; +import java.awt.Color; +import java.awt.Graphics2D; +import java.util.ArrayList; +import java.util.List; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.Setter; @@ -34,25 +39,21 @@ import net.runelite.api.ItemID; import net.runelite.api.NPC; import net.runelite.api.Point; import net.runelite.api.TileObject; +import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR; import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin; import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_BORDER_COLOR; import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_FILL_COLOR; import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.CLICKBOX_HOVER_BORDER_COLOR; +import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET; import net.runelite.client.plugins.cluescrolls.clues.item.AnyRequirementCollection; -import static net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirements.*; import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement; +import static net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirements.*; import net.runelite.client.plugins.cluescrolls.clues.item.SingleItemRequirement; +import net.runelite.client.ui.FontManager; import net.runelite.client.ui.overlay.OverlayUtil; import net.runelite.client.ui.overlay.components.LineComponent; import net.runelite.client.ui.overlay.components.PanelComponent; import net.runelite.client.ui.overlay.components.TitleComponent; -import java.awt.Color; -import java.awt.Graphics2D; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import static net.runelite.client.plugins.cluescrolls.ClueScrollOverlay.TITLED_CONTENT_COLOR; -import static net.runelite.client.plugins.cluescrolls.ClueScrollWorldOverlay.IMAGE_Z_OFFSET; @Getter public class SkillChallengeClue extends ClueScroll implements NpcClueScroll, NamedObjectClueScroll @@ -379,6 +380,7 @@ public class SkillChallengeClue extends ClueScroll implements NpcClueScroll, Nam .left(requirement.getCollectiveName(plugin.getClient())) .leftColor(TITLED_CONTENT_COLOR) .right(combinedFulfilled ? "\u2713" : "\u2717") + .rightFont(FontManager.getDefaultFont()) .rightColor(equipmentFulfilled || (combinedFulfilled && !requireEquipped) ? Color.GREEN : (combinedFulfilled ? Color.ORANGE : Color.RED)) .build()); } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/FontManager.java b/runelite-client/src/main/java/net/runelite/client/ui/FontManager.java index 564ebcba62..5305506a41 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/FontManager.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/FontManager.java @@ -24,17 +24,25 @@ */ package net.runelite.client.ui; -import javax.swing.text.StyleContext; import java.awt.Font; import java.awt.FontFormatException; import java.awt.GraphicsEnvironment; import java.io.IOException; +import javax.swing.text.StyleContext; +import lombok.Getter; public class FontManager { + @Getter private static final Font runescapeFont; + @Getter private static final Font runescapeSmallFont; + @Getter private static final Font runescapeBoldFont; + @Getter + private static final Font defaultFont; + @Getter + private static final Font defaultBoldFont; static { @@ -48,7 +56,7 @@ public class FontManager ge.registerFont(font); runescapeFont = StyleContext.getDefaultStyleContext() - .getFont(font.getName(), Font.PLAIN, 16); + .getFont(font.getName(), Font.PLAIN, 16); ge.registerFont(runescapeFont); Font smallFont = Font.createFont(Font.TRUETYPE_FONT, @@ -57,16 +65,16 @@ public class FontManager ge.registerFont(smallFont); runescapeSmallFont = StyleContext.getDefaultStyleContext() - .getFont(smallFont.getName(), Font.PLAIN, 16); + .getFont(smallFont.getName(), Font.PLAIN, 16); ge.registerFont(runescapeSmallFont); Font boldFont = Font.createFont(Font.TRUETYPE_FONT, - FontManager.class.getResourceAsStream("runescape_bold.ttf")) - .deriveFont(Font.BOLD, 16); + FontManager.class.getResourceAsStream("runescape_bold.ttf")) + .deriveFont(Font.BOLD, 16); ge.registerFont(boldFont); runescapeBoldFont = StyleContext.getDefaultStyleContext() - .getFont(boldFont.getName(), Font.BOLD, 16); + .getFont(boldFont.getName(), Font.BOLD, 16); ge.registerFont(runescapeBoldFont); } catch (FontFormatException ex) @@ -77,20 +85,8 @@ public class FontManager { throw new RuntimeException("Font file not found.", ex); } - } - public static Font getRunescapeFont() - { - return runescapeFont; - } - - public static Font getRunescapeSmallFont() - { - return runescapeSmallFont; - } - - public static Font getRunescapeBoldFont() - { - return runescapeBoldFont; + defaultFont = new Font(Font.DIALOG, Font.PLAIN, 16); + defaultBoldFont = new Font(Font.DIALOG, Font.BOLD, 16); } } diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java b/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java index cb317b8afd..b4c2ff1127 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java @@ -29,6 +29,7 @@ package net.runelite.client.ui.components; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; +import java.awt.Font; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; @@ -125,7 +126,7 @@ public class IconTextField extends JPanel textField.addMouseListener(hoverEffect); innerTxt.addMouseListener(hoverEffect); - clearButton = createRHSButton(ColorScheme.PROGRESS_ERROR_COLOR, Color.PINK); + clearButton = createRHSButton(ColorScheme.PROGRESS_ERROR_COLOR, Color.PINK, FontManager.getRunescapeBoldFont()); clearButton.setText("×"); clearButton.addActionListener(evt -> { @@ -192,7 +193,7 @@ public class IconTextField extends JPanel } }); - suggestionButton = createRHSButton(ColorScheme.LIGHT_GRAY_COLOR, ColorScheme.MEDIUM_GRAY_COLOR); + suggestionButton = createRHSButton(ColorScheme.LIGHT_GRAY_COLOR, ColorScheme.MEDIUM_GRAY_COLOR, FontManager.getDefaultBoldFont()); suggestionButton.setText("▾"); suggestionButton.addActionListener(e -> { @@ -237,11 +238,11 @@ public class IconTextField extends JPanel add(rhsButtons, BorderLayout.EAST); } - private JButton createRHSButton(Color fg, Color rollover) + private JButton createRHSButton(Color fg, Color rollover, Font font) { JButton b = new JButton(); b.setPreferredSize(new Dimension(30, 0)); - b.setFont(FontManager.getRunescapeBoldFont()); + b.setFont(font); b.setBorder(null); b.setRolloverEnabled(true); SwingUtil.removeButtonDecorations(b); 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 20638e7874..83c67c09e0 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 @@ -28,6 +28,7 @@ import com.google.common.base.MoreObjects; import com.google.common.base.Strings; import java.awt.Color; import java.awt.Dimension; +import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Point; @@ -50,6 +51,10 @@ public class LineComponent implements LayoutableRenderableEntity @Builder.Default private Color rightColor = Color.WHITE; + private Font leftFont; + + private Font rightFont; + @Builder.Default private Point preferredLocation = new Point(); @@ -67,13 +72,16 @@ public class LineComponent implements LayoutableRenderableEntity final String left = MoreObjects.firstNonNull(this.left, ""); final String right = MoreObjects.firstNonNull(this.right, ""); - final FontMetrics metrics = graphics.getFontMetrics(); + final Font leftFont = MoreObjects.firstNonNull(this.leftFont, graphics.getFont()); + final Font rightFont = MoreObjects.firstNonNull(this.rightFont, graphics.getFont()); + final FontMetrics lfm = graphics.getFontMetrics(leftFont), rfm = graphics.getFontMetrics(rightFont); + final int fmHeight = Math.max(lfm.getHeight(), rfm.getHeight()); final int baseX = preferredLocation.x; - final int baseY = preferredLocation.y + metrics.getHeight(); + final int baseY = preferredLocation.y + fmHeight; int x = baseX; int y = baseY; - final int leftFullWidth = getLineWidth(left, metrics); - final int rightFullWidth = getLineWidth(right, metrics); + final int leftFullWidth = getLineWidth(left, lfm); + final int rightFullWidth = getLineWidth(right, rfm); final TextComponent textComponent = new TextComponent(); if (preferredSize.width < leftFullWidth + rightFullWidth) @@ -87,8 +95,8 @@ public class LineComponent implements LayoutableRenderableEntity leftSmallWidth -= rightSmallWidth; } - final String[] leftSplitLines = lineBreakText(left, leftSmallWidth, metrics); - final String[] rightSplitLines = lineBreakText(right, rightSmallWidth, metrics); + final String[] leftSplitLines = lineBreakText(left, leftSmallWidth, lfm); + final String[] rightSplitLines = lineBreakText(right, rightSmallWidth, rfm); int lineCount = Math.max(leftSplitLines.length, rightSplitLines.length); @@ -100,19 +108,21 @@ public class LineComponent implements LayoutableRenderableEntity textComponent.setPosition(new Point(x, y)); textComponent.setText(leftText); textComponent.setColor(leftColor); + textComponent.setFont(leftFont); textComponent.render(graphics); } if (i < rightSplitLines.length) { final String rightText = rightSplitLines[i]; - textComponent.setPosition(new Point(x + preferredSize.width - getLineWidth(rightText, metrics), y)); + textComponent.setPosition(new Point(x + preferredSize.width - getLineWidth(rightText, rfm), y)); textComponent.setText(rightText); textComponent.setColor(rightColor); + textComponent.setFont(rightFont); textComponent.render(graphics); } - y += metrics.getHeight(); + y += fmHeight; } final Dimension dimension = new Dimension(preferredSize.width, y - baseY); @@ -126,6 +136,7 @@ public class LineComponent implements LayoutableRenderableEntity textComponent.setPosition(new Point(x, y)); textComponent.setText(left); textComponent.setColor(leftColor); + textComponent.setFont(leftFont); textComponent.render(graphics); } @@ -134,10 +145,11 @@ public class LineComponent implements LayoutableRenderableEntity textComponent.setPosition(new Point(x + preferredSize.width - rightFullWidth, y)); textComponent.setText(right); textComponent.setColor(rightColor); + textComponent.setFont(rightFont); textComponent.render(graphics); } - y += metrics.getHeight(); + y += fmHeight; final Dimension dimension = new Dimension(preferredSize.width, y - baseY); bounds.setLocation(preferredLocation); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TextComponent.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TextComponent.java index 45326fea28..0b6054a466 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TextComponent.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/components/TextComponent.java @@ -26,10 +26,12 @@ package net.runelite.client.ui.overlay.components; import java.awt.Color; import java.awt.Dimension; +import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics2D; import java.awt.Point; import java.util.regex.Pattern; +import javax.annotation.Nullable; import lombok.Setter; import net.runelite.client.ui.overlay.RenderableEntity; import net.runelite.client.util.ColorUtil; @@ -45,10 +47,22 @@ public class TextComponent implements RenderableEntity private Point position = new Point(); private Color color = Color.WHITE; private boolean outline; + /** + * The text font. + */ + @Nullable + private Font font; @Override public Dimension render(Graphics2D graphics) { + Font originalFont = null; + if (font != null) + { + originalFont = graphics.getFont(); + graphics.setFont(font); + } + final FontMetrics fontMetrics = graphics.getFontMetrics(); if (COL_TAG_PATTERN_W_LOOKAHEAD.matcher(text).find()) @@ -105,6 +119,14 @@ public class TextComponent implements RenderableEntity graphics.drawString(text, position.x, position.y); } - return new Dimension(fontMetrics.stringWidth(text), fontMetrics.getHeight()); + int width = fontMetrics.stringWidth(text); + int height = fontMetrics.getHeight(); + + if (originalFont != null) + { + graphics.setFont(originalFont); + } + + return new Dimension(width, height); } } From cdba2ef2977e3b477de3c079f7e7a34edb398248 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Sun, 31 Jan 2021 11:57:00 -0500 Subject: [PATCH 006/133] hotkey button: use default font --- .../java/net/runelite/client/plugins/config/HotkeyButton.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java index 55b9160c5b..131aef04bc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/HotkeyButton.java @@ -32,6 +32,7 @@ import javax.swing.JButton; import lombok.Getter; import net.runelite.client.config.Keybind; import net.runelite.client.config.ModifierlessKeybind; +import net.runelite.client.ui.FontManager; class HotkeyButton extends JButton { @@ -40,6 +41,7 @@ class HotkeyButton extends JButton public HotkeyButton(Keybind value, boolean modifierless) { + setFont(FontManager.getDefaultFont().deriveFont(12.f)); setValue(value); addMouseListener(new MouseAdapter() { From ab4bb3bc977820fcdefc058c0cacd7277e3c9324 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 31 Jan 2021 12:34:00 -0500 Subject: [PATCH 007/133] Revert "runelite-client: Don't use system specific modifier key names" This reverts commit 962bc58178503ee8ffcd0890b3b2882370ffd5eb. --- .../net/runelite/client/config/Keybind.java | 29 +------------------ 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/config/Keybind.java b/runelite-client/src/main/java/net/runelite/client/config/Keybind.java index e7e338a6b3..eba500b859 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/Keybind.java +++ b/runelite-client/src/main/java/net/runelite/client/config/Keybind.java @@ -159,7 +159,7 @@ public class Keybind String mod = ""; if (modifiers != 0) { - mod = getModifiersExText(modifiers); + mod = InputEvent.getModifiersExText(modifiers); } if (mod.isEmpty() && key.isEmpty()) @@ -177,33 +177,6 @@ public class Keybind return mod; } - public static String getModifiersExText(int modifiers) - { - StringBuilder buf = new StringBuilder(); - if ((modifiers & InputEvent.META_DOWN_MASK) != 0) - { - buf.append("Meta+"); - } - if ((modifiers & InputEvent.CTRL_DOWN_MASK) != 0) - { - buf.append("Ctrl+"); - } - if ((modifiers & InputEvent.ALT_DOWN_MASK) != 0) - { - buf.append("Alt+"); - } - if ((modifiers & InputEvent.SHIFT_DOWN_MASK) != 0) - { - buf.append("Shift+"); - } - - if (buf.length() > 0) - { - buf.setLength(buf.length() - 1); // remove trailing '+' - } - return buf.toString(); - } - @Nullable public static Integer getModifierForKeyCode(int keyCode) { From fe01c7dbf4a421c885787cec95c06fed5e168320 Mon Sep 17 00:00:00 2001 From: Psikoi Date: Sun, 31 Jan 2021 19:15:54 +0000 Subject: [PATCH 008/133] Resize and tweak hiscores boss icons --- .../plugins/hiscore/bosses/abyssal_sire.png | Bin 876 -> 687 bytes .../plugins/hiscore/bosses/alchemical_hydra.png | Bin 435 -> 366 bytes .../plugins/hiscore/bosses/barrows_chests.png | Bin 272 -> 251 bytes .../client/plugins/hiscore/bosses/bryophyta.png | Bin 594 -> 493 bytes .../client/plugins/hiscore/bosses/callisto.png | Bin 364 -> 315 bytes .../client/plugins/hiscore/bosses/cerberus.png | Bin 291 -> 276 bytes .../hiscore/bosses/chambers_of_xeric.png | Bin 518 -> 398 bytes .../bosses/chambers_of_xeric_challenge_mode.png | Bin 510 -> 358 bytes .../plugins/hiscore/bosses/chaos_elemental.png | Bin 471 -> 341 bytes .../plugins/hiscore/bosses/chaos_fanatic.png | Bin 515 -> 371 bytes .../hiscore/bosses/commander_zilyana.png | Bin 616 -> 497 bytes .../plugins/hiscore/bosses/corporeal_beast.png | Bin 861 -> 600 bytes .../hiscore/bosses/crazy_archaeologist.png | Bin 863 -> 568 bytes .../plugins/hiscore/bosses/dagannoth_prime.png | Bin 384 -> 336 bytes .../plugins/hiscore/bosses/dagannoth_rex.png | Bin 382 -> 338 bytes .../hiscore/bosses/dagannoth_supreme.png | Bin 352 -> 317 bytes .../hiscore/bosses/deranged_archaeologist.png | Bin 368 -> 323 bytes .../plugins/hiscore/bosses/general_graardor.png | Bin 531 -> 430 bytes .../plugins/hiscore/bosses/giant_mole.png | Bin 422 -> 357 bytes .../hiscore/bosses/grotesque_guardians.png | Bin 332 -> 288 bytes .../client/plugins/hiscore/bosses/hespori.png | Bin 478 -> 399 bytes .../plugins/hiscore/bosses/kalphite_queen.png | Bin 422 -> 364 bytes .../hiscore/bosses/king_black_dragon.png | Bin 331 -> 304 bytes .../client/plugins/hiscore/bosses/kraken.png | Bin 351 -> 294 bytes .../client/plugins/hiscore/bosses/kreearra.png | Bin 407 -> 343 bytes .../plugins/hiscore/bosses/kril_tsutsaroth.png | Bin 549 -> 449 bytes .../client/plugins/hiscore/bosses/mimic.png | Bin 403 -> 363 bytes .../client/plugins/hiscore/bosses/nightmare.png | Bin 609 -> 492 bytes .../client/plugins/hiscore/bosses/obor.png | Bin 627 -> 389 bytes .../client/plugins/hiscore/bosses/sarachnis.png | Bin 488 -> 409 bytes .../client/plugins/hiscore/bosses/scorpia.png | Bin 333 -> 282 bytes .../client/plugins/hiscore/bosses/skotizo.png | Bin 473 -> 382 bytes .../hiscore/bosses/the_corrupted_gauntlet.png | Bin 546 -> 419 bytes .../plugins/hiscore/bosses/the_gauntlet.png | Bin 618 -> 510 bytes .../plugins/hiscore/bosses/theatre_of_blood.png | Bin 613 -> 487 bytes .../bosses/thermonuclear_smoke_devil.png | Bin 543 -> 462 bytes .../client/plugins/hiscore/bosses/tzkal_zuk.png | Bin 583 -> 370 bytes .../client/plugins/hiscore/bosses/tztok_jad.png | Bin 315 -> 292 bytes .../client/plugins/hiscore/bosses/venenatis.png | Bin 378 -> 341 bytes .../client/plugins/hiscore/bosses/vetion.png | Bin 391 -> 347 bytes .../client/plugins/hiscore/bosses/vorkath.png | Bin 597 -> 478 bytes .../plugins/hiscore/bosses/wintertodt.png | Bin 873 -> 759 bytes .../client/plugins/hiscore/bosses/zalcano.png | Bin 485 -> 407 bytes .../client/plugins/hiscore/bosses/zulrah.png | Bin 374 -> 346 bytes 44 files changed, 0 insertions(+), 0 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/abyssal_sire.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/abyssal_sire.png index 0209db1f974818c6d8638e7ba7f39c3f860ca2b8..c5b238a9d09d126105b1e6ee9bea13ffd60eb814 100644 GIT binary patch literal 687 zcmeAS@N?(olHy`uVBq!ia0vp^5Xfq~I0z$e5N$R!mR=;%1w*jO4F zadNPm8XAfU3lwGe6r_1uo0*yF=%{OI8mOr$$V$sfNbvD+vokaMdAMi9ySg|yT3A|| znwt3==&30yYpKaZ$xHi7OEgas6cXUEbK=et;Yt#flaB~WD^Y(Ui zceAl~@V2#0PquNevNAKU3iL9|Ha75M(08`gP14g2*3oj)(zGyBk5N)swdI=CqK76-(xaS&*%d8UuPb< z_NTkq3|btHP50?AKJu2)tl{9#n!npu7RUy$Fr1$^RW)@X%i4~R17&{m=G91hFWt`@ zIm@!O%>C_=jca%Ie^2^*V&i#RLnqY-~l?XJ9fWy=#Wc|FN8`_3xMsy)kBJnL6) zQCgNVVRMDW{(Td@mwMhfxKlzgWJSn(?n&#^J*Irx&to=GF?I8f70Q;42^Yj|ug;w+ zv}vE(hN}weOt${I@tk`{rDn5y+=IloFQu3YzkN8>%02(P{tAheKj%zKwfn&=yS#fF g+a>i>M^8)Z|7XP%cJeCEZ3QJePgg&ebxsLQ0P8Q(s{jB1 literal 876 zcmeAS@N?(olHy`uVBq!ia0vp^qChOd!3-orr?xRNFfjfI@Ck7Ra>)QpOpI|CzyZoxWQ8k(9+jEu6fT%ioow#LR5y1LrxvVq)8E-Vbn3=9%t0vxO?3bLYs zUS`I|#*VhOB0|E>wz@(BJib11YN{$`hK6<)7T&hDlLZ;vyuDqWoGmRaqd6H&w6)C) z3>P&6%*l8)7DOBXW(FB6c!RPHZe8O(NPc=PtwyaQEak7aXC5R0Jp?UmYI|8l>XX{eYBQk#3 z($Ut{?C$F6;^F4y{QezLYXN`oE%*2 z=$$=VL_T23{=JJguim|UyZ-+ALq|MIColIAWw0#2^+nQ{^D|vjdB)S# K&t;ucLK6Vl^XKCL diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/alchemical_hydra.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/alchemical_hydra.png index 3b708b7522d2f340a64f4887fdbf6355aa2880a7..b34ed9c93e42e60fb4816b7b895caba0284349dc 100644 GIT binary patch literal 366 zcmeAS@N?(olHy`uVBq!ia0vp^5NYw@Sgt!7}2vBiwNGg_%YHAHE zEB7rdvI-3gs;t(uvyZH6NzZK#tF8CU&JBska!E;Z^$0g`^U!efP_nW~?3&;hnBeIf zW9sB*n6W%M28EiZtDnm{r-UW| D))0vD literal 435 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+3?vf;>QaH!>Hwb**Z=?j1DO!uTUe}OZy%i_ z<&~3X>g%WB=8;q^8(3E1lV4yJ8WvPp9ah(%=H%>=mF=9IYUJe;(b(*kmTnszW8mfy zP*NJ*)E-)0YZD%kS}vQ@z96Z4LR5WgKys-=K%9+NgqFQSR?F;!j{b<+W}mnMr=TPa zBO47Hn}V|5wA`kUh%8Uv7*i)dB`cfChM7@ug>GI^t{!0y&VjZLzQvUjGV@wY7Orvz zI+VF2$S;_IVcKJnic(YU~go~+5qpNFoj+Wm4|EbfM zl#QQg7F(J7iY$$1j$~n&80*cVCA-L*<(7wez;+j*8GgKyDiY2sldWXAgqcl)g?1d3 z+}hZgyel+dPo!MBmciVa`vL`@*?bW5DV@gDV<6(V=9;5O?l$Y|{ef%MGwM0|mVZ|5 zTYq(Vl73V4q_oO7li%EjY%6U}U*Emc{gS#a_wKzLmgR^2S-5Nq_t_tM9@ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/barrows_chests.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/barrows_chests.png index 89ebe418dcc6662dc8ef41f6008ba24812aad3f2..1560f1aaaa10a428c0c440741e96c10241d9c98b 100644 GIT binary patch literal 251 zcmeAS@N?(olHy`uVBq!ia0vp^5|JxJDtM+tp43W5; ztiT{4I?=+Xe}aY6be0EQQ?}@7AFOcFOW!v0P=%JBp88~GQH50vbEZySD6@=DeEQYN zz6zopr05k$hxBvhE literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^LLkh+3?vf;>QaG}Wq?nJ>;M1%flLVKZ%&-un_*(2 zJbz-2oSdkwt)`WQy1u@Gs;Y#91fQd$wzjski;KRer=hR6v0Hg}Ay6B0NswPK1H-h( zQlHiXxy_y~jv*44Q!hN^JFLLLl5jjXY^~__@AWqr)2FX}+<$U9H;3lZ=qV15eB)H^ z6e=EU-ssAtkk8tu;<$G0+r2VvDm__Oue@=Xkic_z%`e3>FQ=93K3($MWRHMB``I4G zCE_pbRbRxX7bP0l+XkKGNoSE diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/bryophyta.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/bryophyta.png index e64276d9caac5de32aaf29f7895afedc036da05a..68c4e2c90442d97822113cbfb9d64caffa734f90 100644 GIT binary patch literal 493 zcmeAS@N?(olHy`uVBq!ia0vp^5NbL>q32_C|n4r2fDn8!4IMS@B zCOE6aFDJw(BHSR(Rk5KvKE1#vGtMJE-8DPQHquEk#7;K1JTN)iBQ4S;DaoQD)*?3A zG|gMp)I?56L9BIRQbl7#bxue@l237iXHJ4=bc#zxiBm$TQ+S+1L7shbhIMU%Rb`}3 zafqh2zO1T-q>K!|hzPF`KSybOSbbqwX}W)Im0MS~LsP0vPJo7kgQ~8Mq>+wFQ4jTxt1mvHZAmX~peD#j+ z@4o!{KVOt_=Y&sZ(x1#}8wEb* SpRtPu`O?$X&t;ucLK6VZvb2T( literal 594 zcmeAS@N?(olHy`uVBq!ia0vp@KrF(+3?viZxXUsyFxmz9gt!8^qylLPfy8JFL4Gb) z7Usfeoyr7l4Gqb}D9hF?qoQcd{4`sAZRxxmmxff`%xJ@sVz0t{cR2-7b#=+KAdQrC zm-Kw^q%03zLq%aBp4=#-Xm2fd5AC2ZD>olw7G@?kHkO=J+n5kzFCV?U4DX0|2TM~m z8EL`79G{eUmmoj0q70wZSStxJ0X9|^QDK27f9t|zpNtsym_WPCcsBuR84+<&eqT4^5MK*EUQPooDMKCkq$sCQKMP550ajLK78Yh}Qzan*E{}qK zdtmShlmz(&Gdx&euu&i&;qt74319ma9O5>2*8t@qkczK@Y*x*#Tspu0|msgH}T=%A~RkL=n3AY?yvvTR$#jBT}=XNO=VKgt!7}2#}GIQdU&d*3>l8 z(^FSfb+WS)5fn5xGP1L@^zm@BGBfjXb#--eEX>PDh>VB{3rk6gi3|=73h+s`G>_EQ zY@U5F5@--pNswPK!?gVWzdeDx1)eUBAsXkmUN~9AtSG>ILE)K>woLKPc zyC+VwB+ps7>RnL5OcR$E$CN(yayQspt~yy#R_ypYzUK7DuZ6EJOGa+6`)69)KyhY^!260#eF^8>?|#n6%~`BBV)qC9BpjeoSfujWJCl7<0B#> zgM%ZrHN8AslP%4io$P}Gd{UBP@^jKl3bPm(7~fs(>jj$5ToU9L%)l`1vDByaK<-vg z7sn8Z%f1s{Pdlu@(K2~~%fu7gB)8wq{`}8Ba?8!)Y4#7E8z@~aIp*Zj{k-y@wP5QB z*?j_Q6;n0OtTCC8y=|*nx2k*TT;;0WF_-tHx2?=t+;=ZOTSL+C!mOp*TLdV$JYD@<);T3K F0Ra36hnfHY diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/cerberus.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/cerberus.png index 8d0802478387885cd93eaf406d7a88cec114df56..f7dc1f8047d9fe636209a75eee663d964dd8a376 100644 GIT binary patch literal 276 zcmeAS@N?(olHy`uVBq!ia0vp^5NI3=ggt!7}2#{xB@ZsYW6BJAl z=CtEvFyLg!7vs}lU{I5k?2;9T65EaloaenPYZ@$9{Jgkp5^5*^fd-wk;-B2^uu)ui&LQ=fb z-adTA@yS-!q3w$DjN8>qKL?@zf0MO+-}Pf3^|CEz@_#(cbgMV7 Q#v0@|Pgg&ebxsLQ0PMb93jhEB delta 280 zcmbQjw3w;BGr-TCmrII^fq{Y7)59eQNb>`+AO|y$G`+$$8At^N_=LFr|NkGz1Ohuw zh9n_QKR!+|A)zu!zF;BFG%-#WJ_dOPhI}zT15O4F1_nt!J{@@(HA%_xk{~ZW&Ic=H z_DztC5aMi;;p>$bGUsH772=#1dp?h0Vu5JA#AV+Jw}lQXaJZxf&T5%twsrfL|Mpv6 zEl_@Znz1aFx6$EgOJIV6AFqn%bXHl;*PB-I9J_kMrLAE?PWWEmrr(7&BFW69>ny+X zU3=cWRcuc3Ibq>@^I6i0`EFbiUq0^^Z{1O@4U_IDFN}SBUg5dyMqP)fUyB6h@Am)O ZW-oJ0uH+)DkaMp?2dn*B#LLXg($Xp^DOpof@A2{I>+6=HqtVva&eYYmzrWMi+2iKs$Ii~y+uP;o>Br5^ z!pX{{udjWDhpxD}thTqez`?n~!l<*fyTZiK)z*E6hir6p)!ErpPMzhs+w$SPJ z`l_r9hNH1Ff(ljp6Mm}ciXk&XFqNR(80roj9SkbIp|26%+Cn_t(wutlPb~eBMzxkS?ps#7b7AGC zIsKbwm9Cr7w4^t-yD~J*rR~_-q7{?#y2@n>iZfdZ!x{>tvT~BDvjR%fM5AJ23w-X^ z=g9zVWhx2s3uc&>|NplqkT=iM#W6(V{MHG_`C1HkTu$Eb=5bu zk#zELdbZlFr7TB|{pnrM;N5C4K~-QK<3G=gY+a=*Gv7R9oP3qfap}rv0mhKY*EaoR zth}8dC*Py>cuL!owyVN>-qkz1OU(bm__)YZ1Xzth;+b%719VbOQvjCKT+{!L0002JNklVHy~SNiad-Lu&tXfGmdPcXeQTLx%n%1Sv&6ySbp8>zKyupz zZjTr6`C)-TPzuT62<#nEDJJ3x*h`YBbVdm45=SP#V`W4?fn=yJ7QZ?-!yO6ctm=pIk!3w2Pc<9Z`MMeRNX zGr@x@NEHS6gt!7}2uKLe$xNt-3Q7z2 zPYLpl_jC?%vGY%kF3L}>KU9(*8)Yt}36oZr=78 z%1j^kdcF``qttnbc~;pB0kg#cWm}#s_^I>Nr&s9P_jvtl1u{jB2l7~=UwHg>@?5-X z(wPN82Sv7R+vnG~z{qb^fOz6e&0BrJLF~2HI0Y+SKYf^T_jK&0?+JfGv&6S6?Y5oU fUz;WP`~Op3brx3n<%={ML4o7x>gTe~DWM4f)3|>L literal 471 zcmeAS@N?(olHy`uVBq!ia0vp^{6H+g!3-q-bzC(AQqKc?LR|m<{|{sWfrV<|=InHJ zX{&3!ok12ALY&e!r%X6hlJBaQ8fad6y}x^7W}1(2o{pTu+Vms`otQIKr7KbsuCz9I z=x5}m)}_al$A{;H`=@3mRK|v6#)M=gM-_$pr{t&B2YJWE1ZM_##<|-0#{_4@g=UBQ zC40MsC5Go_Csidy7v`rmq{fyQ>)S*HrUm=NCxqpM2c%@gmnVehMhB%QMHNK`rTV!? zMFpq1+WQB4C&Y#4g!m--xS>vJ zI)^yh`kENnDDW>i2J|FTNswPK!?gVWzdeDxxt=bLAsp9z{jZ8O1qh_DGjFusAj%q@ z^XA5>CmPg?QMrVx{;8U2$&)`et$bqh|Lb0JGRYq|FKU(DJDGQC?*mYBus zgfDZraQ#=>?%VQxr%uk|$avmewfF8^VUGUiumAq6%}BekPyhXLUa8PGDq5h(@pScb JS?83{1OS#huo(aV diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chaos_fanatic.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/chaos_fanatic.png index b27a0109358c901c0963a4727efc72884c74772e..b712e9b70a9cd1db051d4960ffc242668fee5aba 100644 GIT binary patch literal 371 zcmeAS@N?(olHy`uVBq!ia0vp^5NVNp`gt!7}QUN0)qraD@36rrZ zqY674n+v;(4TF`nxw#&bo&ckOFSD;Uqqdf+8Xpf2I|F+Zb5v|-XqdmBx0{=*gM$-; zgN2ESvA({ZjEaloaenCpbFsq)JkF{+kNY+5dNM8Uef|D3({@TeKCbLIAvajBqVq6U@r)lA zxok`q%(=|LtT4^Z_O;{JQ??~rP5g>qtlcW;5hmKea^Xp)oAt^3XoE*9c0Y7!=ULzR z=uX8?e&@ALTJY0zQgJb>WQ^T$+k@6U`>cX^4_R`#aWrV$DErHJ*^@(8 T&v?%%P|$h0`njxgN@xNAhtnz>VB*c@ZOUR|$6({c;Gn{! z!p6X+&#dRq;P1le?8@%q%I?a~$j{8c%)-pX#KPq7fpe^&JGkd*4O9b;jyu_^z-llDzY*&jSUTDW@ggU(e`$8FE=zSI1v!N z?(=D&{8CRB#}J9jy%&y(H3bN;J@Bq@b#Rg0y_@^!5wYF(|0kAk9(MZwwEUTm;3sXK z(;pvfl5P#K5b8a;(pPb!&r-$7tO>fW&OPNm8niQLEvMMmE7E7fc3*zmxV3r3SN2~5 zD#xyc%k`>n>bSP(@=V8xg70fKmMEBpWwS2M-+VVy%kJtO`4W4*seS5~8^6>r9#`qx ve~(EbbccP5LfF%vRhuO^YvR=>)qi6AWXQk2qEB)*C@MW&{an^LB{Ts5)KZ1- diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/commander_zilyana.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/commander_zilyana.png index bec91f5bc183edc8886564f7fc6642cedb2320e5..9f1d6d20cca73317ea4192b347d000e118b05b8d 100644 GIT binary patch literal 497 zcmV3{mCLY^!>EO{ntRc~c&U(cql<5zhig<+X@6{1 zwOduER8ON(OqEJSfImBIHZsG*%cG>kxw*rEg1wfOyxhyVkdd@|d$gjWt!-|iY;B*A zkei5zl7@zm*U5`^c7D^ueQ|MnY;1LEYH_uvXohlIn2c7MO-OrjMvO*6T1`EKLOp0R zFJUh(MIjz;6%LrId&&R+00MMUPE!DuZ~yyA0002MNkljx z3`&x2HnVZjp5=1sR3#2-%ZzKZYf*C{5m?x@MVBfr#3e@`8X*TA^}rC nufkg}xP81YBI|oZ^rv|OOlA(r7G5-900000NkvXXu0mjf+z{ik literal 616 zcmV-u0+;=XP)ox;X5r!G<>MDXh_DC>ac~HWh=R32M8w2dB_yS!Wmpip*kt8I`oRaAitW=4=h)YO4MLsLXcTSu3Lk%2`|-@wq&(8$=t)Xdz}0wiK-WvyYuX=H0> zug34-2oiB}c5!uebNBG@^z!!c1^R`>&(S|1Feo@AG%UnBJR*{X!PhE^g(W&BHZI;P zAu%c0nuUQSg@qYpe=19wS31agpzX|%&|n6085jZ3n;R(+>-l#80000Xfq_vlz$e5N$R!?3Xe}*2 zTRpF%x3QqMBqc7?i=U5&nVEUp%85&7G|!z-GqbmvBZJ&R z{G1ZJoKoHG{XA^_U2Gg}Ohc_q-R(^5tqjeLv@MKP)l?Le6{OXbrQ}3K&K=o!WaqL& zTj%dxKW)+UrsnFbwyLbk{Djiv_>!!c{Djby*nsRr_sl?de=j#5dvkve6Mu6fHwQfz zBW*VWZ97X%Qws*jTzM(KavIi9QZ#)K=cK-QSg*&*B4)E=|3X%EkcLWFgrQ( zhuY%if&*I=c1@mIrMS-KdW6gx#d)93eX6->a;EW|Uz4)hlm}Zhrm(&)%jhuL8eX*S zZaB*|$A?m1Za%;DWmVSKpKJcH7wvhx;oF`mS2H{wUH#3OcKYIXcHR^Hx6aE{Zj6rQ z>J#01Yv1Dz-4&{@q9u2h-z=RkC$;t7+1+zE6?-!-oeBHN+H4|ge6ZFw4U|4SUHx3v IIVCg!068k=k^lez literal 861 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9S!3-p~d(N$3U|{?g;1l8sw+r z#LvTyiIFir+-uFENwfQFG82PEg#|Pu5fvSJ*qO=omh)s&bdRvH_e$lv1ZrW zscYtUx;a^TyI4ufbKIf^!IYhPIQk64y?{dPIk2^O^$al(&p#m+r_hTB{2H= zOM?7@87@3f=wG(r>x4ZH2Lu8NY97{rQp|r(7sn8Z%gG4`SS5ZoHgLLj#5)9dM6^hH zwz#^4_{2C(67_6}_lj~0^NVX_J(T|Vd3Z?Bv+%&s;Oh_CJ3eM-Wxjs#>ZP=Vl;muU zPsU8GH++AdJ#$u9XYHEYoVP#T{%mxT;p_7|r?)Qeo!q>*dv^QgI4Gut+5c}iu;9Uj z3mZOkoY2slka#BWqT$BEjUPEXJx^)|SnmwJe972o^QNE0ML!e$&Y#xT(9zP<)YWe1 z4h;zly?*7|RdbU=Ejwn&z`{tB0$0?W(T&edf2i&m0!fM>B0wUu}6=`^-4) z+$>x6B<1&aDnEz7#Nc{=$32_C|lmmH5E^ZFy^a#Ch zS2WRF0zIFYw z^x%*4rmz83T=jHu4AD5hb%J}*AqN3hT?4k{$%$!GHn#{_KA&U!eLuHWnD0TMs_Xx+ zu77nsUS2|LmX}KN`Jd}=7+o#Wmf>WT`@~Sc#QTfebJ=NsoCP+$c(iFpwn^yQIgJWu zroDK3v^0B`>Vae1JESYOM`~s-`@sHrTe6gB>B2M1tlUHtPHZlzjCjF!OsBCy=;PH1 zR;~9k4n6-PVc_l5DKZEX+*u(p*6<;@-9r{*L^f_M%=6@?MULAs!koRRj} z(w=s55nfsu;TEYO#uJMDW>tm-+AGUTa4<0f0fV>@ru&^*Q8mY-9`j~jz zh)1}}2RTTq$nu7{$obhxsmSv9*h+~Daj>y69pMPc06IvuB*+h#95`dPzIk4k*48rH z$k=wyvThH57LH&>)9406y>ubB^-a?P#ZvYduK;Rz=jq}YB5^r6fq_XaO)Si;OrX0_ z(4}C-s+HLpS%pP~pBq>!ww9KB{ld%B+vDr=(}C$v+B30fVb{#Im3`x5Qebp+S5FrY zH!tV+*Uz88r6?b7|G(kDf(H{W7$h!`bb2T_G4P_{#=?)Bot`IsdU%vHS7vs;l$;rP z({g9!$wfUpK0jGoU5~0RO?@glHEEH9?xWDFrdvzD^7i_k)mLZep7HYopf=o+qeUUUy_w?*$4n1*Zp7Ly?@_T<+xy3yl|0pRRSI}D@u(jf3 tkn4=+4h}1`ZWg7cFfKU3B|3qD!CArjP0R(I6rj%;JYD@<);T3K0RTiJ;~fA1 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_prime.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_prime.png index 18e004e18b9f6a4137eeffa9471463f45ad60f9b..bc6612dbf421d80be5bfa7699506a382ed90040a 100644 GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^5NTmh%gt!7}2*@;6jZTgZiwP^U z)XvDyNXSSCj0ns(Q%}xLj*O4=3-Jqy3i0ssSXrqvJzLe))799*xZX~$+D2zliPnNb ztz-igFBK^#Hzx~Qi@Evgy{XD^dWxk%a?Xm9aZaM8C#pc!GnEAS1v5;`|Nq+)$eZrz z;uxZFervxw-(dqDma|hmM7`gc+W!BaGUtxYYmo!oA55$jwppmM1c*6k25kGUdUM|< z!38}BW<@^K2#I2goXFMZERem5Lwy=89kj@L>5Q$=HeZ{3ut2NxGQ)G{1zopr01&lz00000 literal 384 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv@!3-oh+|s`Ssonse5ZC|z{{xvokZG)%n3?Dw z>K_&p7M&Cw5)~4Y8dGkiQ){c|p)45~5f~XC8J`}Xk)M%On3brn?BVN?oSp2WDxG7h z?it{jmYY^>qf=n6o@S_OXl__yp|!e7XGMk1f&vA-vI07VsU*lRm|}q&W`{)02Ne%7JGnd1{-(RV?r_YmcPv=1m+g8&NY|AcZzT12J!;06>xq0$$hj;K#<&rC!p6c~tuR{8Lk!AfW Z-*Mk@VZR%vdWanqik_~1F6*2Ung9p_kLUmZ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_rex.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_rex.png index 1b73c2d416676b4884d009716817ac74dabc8ca1..a18662f7024b2f3fc78163a5b72eeea9faa8d50a 100644 GIT binary patch literal 338 zcmeAS@N?(olHy`uVBq!ia0vp^5NTmh%gt!7}2(Yy>%t;GNj`J_f zj;gLo%1jDLO$dmK^h${KP0LcPt4*yeOeimlj}G++^mX+2vM(&t&M8pO&yS1+q!l3d(?KZctGwg^ncv&&o!B0^hP}xa z+I=i`;a?3!i@eE988_LLPBVX5mwVSC@~PE1A?fGST8%Xs++V%B9Aob`^})PlZ|t2V zLYp$yy%tZ=6=u6qI$fH_EHu^DQ#dfOVlj)$#pOIM>MySR&T;zExJC7w@`2s)wW&UN aze43b1X!!~YwY_43K>sVKbLh*2~7Z3rg|*^ literal 382 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv@!3-oh+|s`SsrCS$5ZC|z{{xvoP>>ZF9qN%B z=bxDr5)tH<5bc|t8d_DASXZ0s;b9dQ>6Mlcn42C^Q4n8Ml$f04Z);^3T3Q+x z7w6^bYVKrfEGjBgQ<@ywuC*PkcIS*58G1`)WVjoWi$!o3-?1cKN{S zm+La<5qszo}OsTJ9DaS!sB}TM}MSi YnZ>trtUI+cw-pqIp00i_>zopr09^E#?f?J) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_supreme.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/dagannoth_supreme.png index 721bd215e4c9646962dd8ad553799bb6805a3f05..4cb52e41563e8268ae248605fa3f19ab2f4ddc3e 100644 GIT binary patch literal 317 zcmeAS@N?(olHy`uVBq!ia0vp^5NQDRZgt!7}2#~hYiAoI!iSr7G zat}-Jl{8gP%8keb9y=5NEVsI%q`?uTVyC&NKQ8Tk=5vz_%+?jf$7non_})%ChpY@=-NVAb7g^xvX literal 352 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv@!3-oh+|s`SsnP(S5ZC|z{{xvoAZ?|SlpEn6 z;T96-6`AZGmf-6X>KvUG$fqnB80`_48IoQUolzQ>kR2+dCFkaEFRHIBX{zoS6reR;^Q1u9H)so|>ten5OFNW0PN^YHn-DDJ>-Dpf4mUwBS}= zJ8^O^RlV}Yi;Jw?+fv=^4bQNpc!?jn3<@PrS3j3^P6NQDIWgt!7}2ncm?@wTyE(OMYg z;Wn*2HPFGq&C)W)%X3p-<=W2DmW;@>pg?C+)7jNoojEZjF@8yYzUJE6OPcZ*HRjE$ z&2}&`$_Wq43JtL_Fo+R9AP+Q@sU*lRm|a=L;5v(IKV9w&v<_m0J~ zFF25*>GrBDqp9OfNcKa%Q~%jE-k+v;Kx6jJsOg`cahWrn@pxSmdKI;Vst0FqdHX8-^I literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!63?wyl`GbK}MSxF;E0Bf&XH!!*OUpn92TL8D zFjrSE8!KO1+b9pW7%xu;6QdAkr#K&y?A;>T622D@|Hpy1A`TiyHIRca@dK24sbX40v20Zn{`%Fwy+boCS;i|8M7OJGSiQ zrOA`uxlLRzu`&Puf%9u8FfbV|@hN0*bZu8@a{hk0p`Kyt74|h(U4uD5;pXY;=d#Wz Gp$Pzw@QRrL diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/general_graardor.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/general_graardor.png index f26ca05eca402a5c21078e5e88b81e5640928013..cefbc3c19b6588ad7f9bd6d1abab682d95100aee 100644 GIT binary patch literal 430 zcmeAS@N?(olHy`uVBq!ia0vp^5NUaO-32_C|5a3{Kol+bV6BCvg z6%`&Bm{6GznjIRL8tCik?h)dA;=r1g?9ABG*qE@en5+;-M;k3Gt;~vq^7y#$@W9{% zAAf&Ozi9V_SVxCI2UiCNZyRe%7jqjMa~pGW12whA#)|bT%T_Nfp3q%fT9T7pom8A3 zQyd$UoE#mM9u(;1m7e0};p`mZ?Bwd|5EEhRY-(z1U|?gVp`aiuB_SayC>ZF(>0!m$ zG5x+V(78+{L4Lsu)AIlS_5||Qdb&7V?_Vv1uzvO?0zVzaB00%APvt*o4EZCza*?Cq@rQUWa9EGrTdT640?;$n+q zV?vyre1WnK4t|~<4uK9jmO4$DX%Rs|VF3YwUS2`zL9yZCiBVA#x{E~x1+AP-!@>d- z6l9}ALp_|Gr6eR88!Jjna;gqa$pZS8xg^Lhn1Ny1W2sN;f!s5mE{-7*mvj4H7Bx8t zv_wjU3HN&(;9k7wnNla`>s%I5|E{QODQlX&27|_t6 z#<9vPQ>!<5Nkg2*bb)iu2F{xw&O067s1mA?aeY^!i1(s^H&4B;nq-@|ExazBs5mQZ z_04y8?%lPw343#Fvct8{-qZKZx;FdShPZf^i&8x7i$A9PvMaXjZ~bcYto!MW@8R<6 zf?LaBUs%`)e=^s^eL*nGu@r~mzXIEQ`znV*OM>-E2AYku+~uxOES27igHNTkN~ RJ@TMf^>p=fS?83{1OR0($rS(q diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/giant_mole.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/giant_mole.png index 5fb3f328e8c8971b09e187c56231478407d31c9f..48619319dce9ff0fecd9b01594894251eed4f7de 100644 GIT binary patch literal 357 zcmeAS@N?(olHy`uVBq!ia0vp^5NaY9kgt!7}2(U5L@~}5lRh0B~ zGS=5pbg?mr40N|NRM%0JHr7*dvd|G15y(miNsSJO3-t>1b@pjlosq+d$6`9N$+mOd(QUQFRVLvvUL|;^Kb+OmZz(q%Q~loCIEpQh3NnQ literal 422 zcmeAS@N?(olHy`uVBq!ia0vp^f} z>1!$Ks7k9UN{WgKSsAL^nrfNqDeGw|SH!r^n>X3XLdV0&sx95e)kfdb-q6>{*wx-V z)Ym!0!@|l`&rnbO+S<;$yXObEngdk?c{w@R7)J+r%qR`pvuD%huJniiH*-Us?R}XW zJJaGqz0zU=vlBx_g@iKWLz1KX7uLiSq($zTm^~psXzSKhXIFJx0y>MiB*-tAfnnNX zsZZ;H+-;sNjv*44eJ9)&YBJz)xve+NE8t=fqbLx(pZ@>9xwM4rcTLO9vptnuv{@at zPW|)Eas73}n_r_|8<^CZ88!APEwV64GmvrI)Y>7`Wh3c-@N9$5zbcvzW?wfg z>E%2yd7?pq=F1Dheu=Z5o;w{R@;d2R@eKWjJ5!$)ZJoF9mP5jB<~iD*cg4Lcv}pPs z%vBm{>O1*wo}k6t>pNnMYuNZADVgt!7}2+%jt4~`7Bu(iqTc*Ao}ynVs{-A>{T(XG5edutX?eER&htMUnp zV-Dve1rqh#oj4b2Nw1f2IJ-+hWW!#i7FX+n_ASaz=YvDyo6lywJt%FEeQwp&+fpW` z?sjf-;@mboIchtzeyZ<_1Wmc=6O!L>L@Apj*T*}sLQXv68A+A9B|Ns9$5(t7Kg0ytB zJp4So0=>*_%&qLL99$g?Obu+EY}GW>WEEshEKTwgRwkq;D5)qpdpNuKxP?ZC>KW-p zCr5|JhWm#2#-_$bB}Q>4OyU3<$6ONR7tFvg?XlFS^+4_tPZ!4!iOaDQZVNRl@VGRx zx&~z#Hm&{p|9{S*D;Ev*4=7H*D^c9ieEIM#;RDWz?w=-c95CQ|&5=Au$gy7T$Kok3 zHB+ucEkBW1bxB84e}RUu(Q{RS^&9!t=H*8;FMN7OMcE+AKV6FDN)XUHx3vIVCg!0HA1g`2YX_ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/hespori.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/hespori.png index 0c104674dbe6df264b138a22b26354cfb29d2ceb..a85a83ce3c9b4d8b527d65b8e6411307da3911cb 100644 GIT binary patch literal 399 zcmeAS@N?(olHy`uVBq!ia0vp^5NX-fG32_C|5D?<)yLCxbM_ta& z6}1}{lyz6d+FDrjHWhT&#}x-wt(slDd`98osrgYMAujgzhc>qCTh~|-TDNI&<+YHDC`-A{S~&~Z#9L4Lsu)AIlS z_5|`4c)B=-Xq;bq!CmOE0*^zWasgxb9q*jw-{<|lue+h=gVsOqdl_seX9+SYTz>Cu zr+L89OZVA<-=mF-x`HQS)GQFzG`rzo_3#D_;@cMR$7`X91uni8pK4-zM nLkvu3PF@PRe?dTYb7f7QjBAa8 za9dSXY-CnvUQ%^uP-k35cyC~piFS&BZi07aH83zZedDyRef+!b!|;^T|#0`Nk~{yQh;<;iG5v_h-Ha+U8I$7WnNuW zO--PZY@(EIr+R!~rogkrLzd5nNwv7vXjr+m4neZH=Nub_6js(!Vk zdQnPB!m)(s2NozT$?4NthH#6&Fm>2|rm=p*y?4JV`I4LG58zP~ U(`CLnBme*a07*qoM6N<$f|_5#U;qFB diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kalphite_queen.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kalphite_queen.png index 956e3a502d9fe18d57aa2cc692da9214ba109c42..c88f0bb4485af8e77a4801b2906758349f7d62b6 100644 GIT binary patch literal 364 zcmeAS@N?(olHy`uVBq!ia0vp^5NRb_at)L*b(ER6rg-PE{MFmjQeB-rBQ+8V)LnyAV{=tPl9l$JDx9`Bw|ja@+vKDO z8>@gst*8tYBU>3~Ur8G`@vR4QHy_C9pOdj*O~Rbzv9p%OOq>($V``jStf3(z>3aY4 zcc86IB|(0{4Ab)e|Mmp(mUy~2hG?AMdf_JDAq4@}fSo+E%eJq*duRIp|1O7B*fMI2 zKH53@y%R|Lyq=@uz4;qK#yojJ?Hl(P%}T6Q7WZvAGw(uGhqijEygtKiQJGt7azws9 z*LLBm4eRfHX|zV+{O4|^AeB@ljrp#I63HiaXzDGT6tt?6v1^muas{RjNxG{R<}GnH z@!52sy+eE9;;q{rBu}lG-x|+m$ild#nB#WeS@XJc(g%9k`~+S`YJtMc)78&qol`;+ E0G2j~0RR91 literal 422 zcmV;X0a^ZuP)QfwPjsxBwA=B zRAM4PO(KewWN1f4V?;q^MMR*iU_D77V{<4;Stw9pDm6hIGCdqbQ6xG<7DrVlIz=7? zyE60u000AYQchC<0G8t#@~;2@0JBL%K~xyiP0-g8f-n#T(7?4Liir(lqr?^=p#1;0 znIhso?VQ<6_AVh^`S1zxJkK?*(1SDdTl6~{3<1j+jnTfH1k=zcX7jHRL<`E6D?J=_ zw%%;x9p(GOvHh3ebWYMN7ncN`U2pdX<(vjmYIGJqi=q@laG@$aYOR)1((nZoahg>c z&8jfS7~W-?O9jtnk``f#a~I1@Hci7|+Zt_DgLW5i{LTb4N`d^}$+6q{1M(pbjOx50 QyZ`_I07*qoM6N<$f}T31$^ZZW diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/king_black_dragon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/king_black_dragon.png index 7efce3fe35e23d8305a2a42fe4aba621f5ead25e..c162208002d86654a9075123d9974daa23ae483d 100644 GIT binary patch literal 304 zcmeAS@N?(olHy`uVBq!ia0vp^5NO=VKgt!7}2#}GKRFsnw6B1HW zR@TzcShHZk&UI@K?%1($>C&aMX6ota+_`aM)AHpj=FDC&ZQAUK6Q*``RTmdk78Vp_ zWCVD7t^FQ)2WSveNswPK!?gVWzdeDxsh%#5AsXkmPVnYCtia=PT3Xs;^1u4;`!jo` zTvc9en%lbZ8LwN~cX?Hh`#uvAn1yFO^?UVL?IzD0)`C2R{Y#`CTxoom^r>dbyh6W{ z#ox>;cV@K|zK~m(vYA~)8AeYQr=C8Bt`l>^sPupt7f@xUQ|BmX_Su*l=iQrkk3-wY8Os ziYh25n~{*Pq@=N>rMj=Ld2w*JtgED%n8UletDv7QEG&C-as=3s*8l(j19VbOQvd*# z;~Mg>0001*NklTD>5K*AxB@ID{Qbfn;ndC>MD55VUuNF@aLgt!7}2w2@C+FHuDxK*gL zg0CQxFF%nlJDMvdkh8y5sINq*xr(nnm#;pXuPmFdHk~iRmovzlGs1&&<=nYbD$3UP zi_UBp>S-35+aNTlLMSnsGclCY*PheUhSSBplnH17Q%R6tFvGO`|Gzzfykbum#}JM4 zXZ_uU8Vm%CmfWmf|NsBe%^wp^F;8J;k0?|4#lWKz!n6GY56=??PT?hjma{DNf)&b` zGOPu9924@LR=j#6wl}`^mI>E4<0nyKrSWNMiPpOwWY_)B%2NHQ;=-DKNTNH3?Rimg h_=e@DEp=fS?83{1OQC+V^IJA literal 351 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9Q!3-pIZrslUQjGyVA+G=b{|7Q5Ai|e(<=nY> zQG8i3oUJ*0F@cnEBVUu_&W3X`fG*q()n6z_}27@&TSCtDH2Kyzjo{K{ zsFO6)|EY;CSCrU`BwKEnOcjvb9XOkwyM{S oDR%C8xbA=LwoA;Ww-?oaWl#=ay=BfMDGmxFPgg&ebxsLQ0QOgVfB*mh diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kreearra.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kreearra.png index 0ef3398abf3c7852c16674016f4d47665fa5ec82..2bc96314764242724e04a310d27781a2bceef9ce 100644 GIT binary patch literal 343 zcmeAS@N?(olHy`uVBq!ia0vp^5NRZ$GJ{qp~1BA}}yB(x$7ip{=I+%(BwV#Kh-U_Lt`56lP@|NsJEk z_G-xWObfG03bA-{cFT?JeTU}eZ=8^po)8}&8M!AuVn%hCv%P(cziFt4zM`Dm(jQZE zfVMJ~1o;IsOw0fO+Y`v^_jGX#(Kx^LqC4MV10I$G87s5A!wP#Zzy3d8;-)#A{s$Qu70RLR|m<{|{tBKxchjQ&m-N zT3S>{NMB3Kq>he;T+bKR4xL$6dT4I`wC=8%{e4f)Zh3ZTPiA7`jNYD_l9I!T(R<<} zHcm(@%*v`P$j{HnxUs$OXj06K>ae=9vI%W%K|Veafr04>@m-A#4HXs6_V%SYIX%ry z0bZVpa&noGHu>@Pq5l4%9{OovRx$pjNg)>5$;qwN)$O%4@sW{Vc0RlVbR2U@kY6wZ z!?edzpVkApOFUg1LnJPTUWgQGG7w<7pfWeX!KHOYgZ82o513y3J)b7hdSU+hWof(G z1f2FKGAy2A@{{xVEuJKSJ5mjjESc8M@do~)++$C%HLn&bM&qc}+i6Z*pugj*} ztv8FVey;W-{)nAl>m8ZCSk50eHJ{$GkH4kawnyVrc2Rfqq7IY0@~No-0-0K?N7Jg9aU&q75Ae4(d1> diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kril_tsutsaroth.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/kril_tsutsaroth.png index 6b36fb211c25683884db6a057e0738e2ee3c6eaf..878a14c18beb8ac4edfb2cbccd45dd275d2cc9dd 100644 GIT binary patch literal 449 zcmeAS@N?(olHy`uVBq!ia0vp^5NNox532_C|5TL`(FvmfCr?38M zH=XImLK6&yd?YzjML45MpW0vEQkUuH zYA zqgrDu=U^yTsxNofM|z)=WSW-bR(sJkHlmTrqMg!wg_3;HBHS@ToI!%@?tJV{eC*DA z42J9satsVY@0pJS9nDk{){Bdn%#9w~R2cznfX zX?}$)=lMckgt|&^pO&eYPM8Mis> n>4hs4Z5eiTEMS^?B>lGF@uyq|jAzVr0!4(UtDnm{r-UW|E;XAn literal 549 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv@!3-oh+|s`SsXqZeA+G=b{|7Q5z=@CDSCX?w zUnoqFy-|m+Q=dJ%Pd3#T+I#H8EPt$Q$#tEMYw|m*`s`IU2XKI84I;GWcfN7&vj7u6XclGQ@Ybv zKQ6$2pOa*BZAOF;XRQujh=;{$H=XT1`ZFd~9Skr$5@?tv%DKiybgrq;R(nxrK86jR zdZh(%TfFqvdFZ5RN$&GETwpFV!A3PrnX^=1ZkemrWLwo5W4TUgzFKKMUp~$%Nxm2% zPB{jKMb26??bRcdMcw(>gM>H>CHXuhIfI2czuFhM0)5V0666=mz%cEx)Ti}8?p040 z#}J9jxf7y;n*u~yEuV9{Iyvu3TI#l_Wa*VRNlR0rZl3-0|NnG8rpw*g=ijvFUz@iz zE&G-br=nw*i_lDiuo9yw%BhvgsUl~3)<(TFJlf;=N%cw6nff%51YYS=euq?EOV5tJ zZM`mdZ4I;k!1uK+_t)8JMXSp41Y~6A&zbl1++l88v!a{tSnm4$UR5sp z%Y6I3;`UEp-^<@Qo{-S`&!9@mR(pPe30wZBPruA++xeR<9)ITExA*3o=jD70k1p!g lidGA2@!7TH|A}qeV(Oi{gtmDXOBjG++SAp~Wt~$(696}f!Mgwe diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/mimic.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/mimic.png index 0666325dfac408c4daa22fbdc5becddcc6a953d3..7f523c6b879984d87cf348fba2874394ed4b2a52 100644 GIT binary patch literal 363 zcmeAS@N?(olHy`uVBq!ia0vp^5NTmh%gt!7}C{R;lkd^Jp6bQ88 zNK4Dl4dAVb;cZUjjdJ4Xa?WUN?GLr|o;Gdq=Czd!CZ;wTrezVlMWMWDJ{(@=9LDMl z9i3B(i)(Zg7@C@TYHHdR%+3i8j*N2%N%G)`j!y17)VBs`4O2;wUogY8{Qti_fxJzg zE{-7@=ePFXj5O@A(}N6#NlEm?nUs>qDqz{^WQIPO{`v0L70tQi znh>-rBkTR&eW&NGn{;mH*{!*+ZMCF&YbP0l+XkK D+B%H+ literal 403 zcmeAS@N?(olHy`uVBq!ia0vp^!ayv@!3-oh+|s`Ssg?kr5ZC|z{{xv25F8xoWzOMm z%@Jb95$VVg>&B7f!I9>}QC3!;7s#ulzz`jsobAtBUENw++g?;uT@=b&7Qx%zF}b^Y zT2(Z!u{uLzV|R0NZ(S^JQzCC`GVg-fIh)s3b~$IXw)Ur`<%e2&$2o-9Xqfh73QU`} zm_b&SK~0Up#MH`7RTbz0=8_=4UCs)bWFHg4>!R&RM=<`ZbyFryGP`Xj7_sEl#PZTGg-iD0{`VW}@2|gnYp2AfKd*cEY?B4H<;C_MwZ3pG r)_(bKnT*X+>06l3aOHjDzg^AfzDzt@W#_6%pm6nc^>bP0l+XkKo4=T_ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/nightmare.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/nightmare.png index f8edd9b211ec893a8f0fab554cf2f821b7d7e0c9..c0f7645c02c50ca4a8f51792daa69a258ea4e5d0 100644 GIT binary patch literal 492 zcmVik5M4dR=OEU|?r; zP-k6TWot-Ys});bKvY~kNkc7yi$js}!w+p(es=MV=oI(X~MLnHNmk*QI7}>QO zK^YRld1DjB)f|o}HjM2|lnOwVuE=&Fd+a)~?x4CYs0 z1SD`l8k5en5ONLgH(~tC;WlctejmIAANWc5zCWUy?LiJKiRg(jrTlO_CJErKqL#MZ iw|n}GlQx__{>m5gGY{Kv1ji!)0000FY^EY8+P06IG!Ir=lPy{?juQr5Qe^SZmf%8;>xb6o}Ig(ezlu=lYL`@Z&;CaQhRAq zcxF+IaZq_;MR8j{Zdg56Mld^t2Ot0d00MMUPE!DuZ~yyA0002ANklmHXB0)*cA{~tukB{&Kf*ux&?X*4_9#XnP^_@c9Uf|9Ri3BvBY#2nb0ONW^R z98R~zgjGYq+qY?D)b$EhUL-n>&v#DA+$c zXUCL`4gD$Wrj|`_Oq!h@mb7xO)DV$OfI;k{hVrfuUf#1gdl>I9w@0?S)pecHKRp`u` zu;uM>Yr2ybwZu#+3+^uP?<)$JT^HV->rL6Lw6|b>d(P~Z%+|ul>WrYC z(x}?3kebY34!I;7V6bwP1o;IsOtV>fr}#kltH$pK7WM;0_j|fHhDcmaPH13q)6;Wf z6JZc}`0Syp=+j4!#RWu!o;d}oDQRe_=_z(Dnc(f^@+-NTnpODF1Y?q5H@zM&`~z<}E++an{Q!X+i7gh$&+$4t*o(NI(J=KdVbm6vd$@?2>=8p B34QNKFXv32_C|5TM4uAj`l|sH>i) zp^|E-tH;R@Yi{bRqh+I{q$ns@Y-yTjW}L01=AxmdBPAih%bRU)Q)!@8s;8Bpreq{5 zWgsXRBFHJl$=Q^V-e6~yZfjLxZ4s)gZLgvdq9kjsAgd+FYr)CTUtHLgpO+sIp5*7d zAi!&ir*opEd6tPmfSO8-ib8;bl)_dSU7&-QN`m}?8K&j`|LqCnZS!<-4AD5hbfT-+ zVFLl@?8-MY4uzy0`+a|1qmm9o_$f6sT_R*q-C++&{gwqbXiX~MnT wBF%BGvk%KmUbmq1hZGY}M3~*O?@K2BVmh{xi^ajK(hd~Bp00i_>zopr0K9>W(f|Me literal 488 zcmeAS@N?(olHy`uVBq!ia0vp^!ayvA)3QZ!Tw%uNCXIrDYYYHck;4D_5e)a+GM%oSt} z1O>wcIRo@`JhU`yl$3O&B-BJjv+ZqsbhOkM7-Sh36a)n&czJC&8T2?AV$Ds{jP&Z$ zQoHi=jAW&XElrdBeDfp1Te33yiwi3av`je}k_~hg1b8J{nwMFbPw{k4x3#LWv6SNE z4A;`o666g~Q)zavN%|T!59ny-k|4ie28L;mr9Q0(a&LLMIEF}Ej=k`*sL4QtHQ}V= zfv61{UAY=hnmEOF1w?Pxd;dR*moatO_wZ-4Q;N>X_U=k#FlIl!XtF~ zap{yR-wzy5-=+W9=|!ftwDpR|nd0+zZ<}kl;_;i;zkBxWe967^)%^Np#{a9Pzm5HV coXzkXyJLXh-&~LM8K9{0boFyt=akR{0BxAC0ssI2 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/scorpia.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/scorpia.png index db4c0ff9888f8ebb15c87652d26b21008fb56082..f39f9baa6e10dd18972337e5398f31e10db37eba 100644 GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0vp^5NZADVgt!7}2ynEq3H0{1F*jF~ zmp9haGtkjdS5ZArlu7K!&QmO-$2bwB|(0{ z4Ab)e|Mmp(x;&u+;iW<=oc3K*&we6Ty{n6X_wfwl zx`Vn-GI|2a&K(`+7OJLPck%WKQweVpog1If7IIQw-6W&*-B}-5Oa2AlbeHx{u=r$J zqp{b{Iw{;mOXB-2?v4+^8ltuO42cW>@QPW#HS4Xl*q7MW%lGV%Nt0=xc#&(L`N_3R UFI4S!2Y`I&>FVdQ&MBb@0LM~Y!~g&Q literal 333 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tQ2{<7u0Z<#|NlS|2m-yml@t`p z^YheIRc$RSeB9lvOic}RbmU}YtT-6_Jv_X4Ihrae+;}+>qax!YBAo5)a#B(ptgVgp z^t3cJf##SS8F{(5;u=|<$jXtrZ+$D4Adt4Hd1 zEx&f`0dtSlPQy~ixqQnE-!v*dDwxu6N=5w)}^O z_qqiW7Jly0e7%U1%`$6ik^M5s$haBYtL8SXQGWYvcSOnQd+)^KcJ5xg>HG8ksmEO6 XWtXwapP4>K5fmPtu6{1-oD!MNaY9kgt!7}2vAm3OpJ<5jg1ZT z^0F~E_i=M`wzHF$kqHY3P*+uzU{E$UHWp@(krEfrNl7u*(-RRCEXvA?2@8t~3DID) zRO2#KVA0do)GW`>vorCtGVpMBa>`Fn4{}Y*Oic8)k2Th`mu1nGcy_`TXdhEakY6yv zwEX|SJ%PMEo-U3d8t2!ZJ1Nv;Aixr^Jbe>8&+4w#YH-RzX!&iPbg)a@2oa?z*IV$Ue+9YKpObZS%pAg$%t#GH%Zq`TjEylltte$_5j`nrn@W1!^($iG_v|Hj)Q$&hRZryfJs-NG)Dt<74`f0>sK0+gWTc&? zWm0sMwV9bLi?)cMV0Ln{v9Ym*v9Xl6xCDc;5{rSdqGC~2mW{Cw0|TSCeXItHr96vn zcwk^=VxpzKTUlOiT3npEs;az?zP6^Oqm7L!hrTF-ytk{Xv8KHmm$9L)t~!sguZMe(kB_^PQ+|577MJDwTF$pX zhccH0`2{mDOnWT#X+4m8%hSa%MB;Mph3xPa1p&4PcPm5B2p!G~b6O_29{dS{F%5*n)R?}Td=^n3C-d^ zwiY)E?bxx0sq5&v{D8ZU-tC*Vsv^QS)%Wj>ElumM*>bL%udQ5}D84$b<<-p%!Rvn; za=UfhJF@#_?!S*a=U@6DdB$h6n%$FQJ{MNKFp#32_C|5b!X><5@x4b#J>x z7JBa&&U!Pc>s4pNtCs2~sZoz(10Mu9zh6A}bx-rtoTNwL-e=vdx7e9XHP-&Jf7j>T zTR(1C^gAeB#TtqkDsqWRGQQGcFYC)* zR24k%ce?CpyWiPjxs|~jGu_!{y1j;)O}c72{JdKtPEG*2iK!&WFPLFk{{P>eK;B+Y z7sn8d^J^zu7iv}zaY<#YzOEuw?YaK{|I$Jk&R5B5&s(|E%Mt_rF)&7+V+z>GeB9>Y zW9xehJrX)OzG=DfPbgeExzQxkcjS_m76v#WFsAUDkI=fL*{X3>V* zUm+a0KsMuDQKLC8ef9ZpI=XPu6Z)D0CLhYK8Uzq<6kxpF8W+Dt!!EhcLv zAM?Am^SZQ#E+=*=BI}Qa?30V*UQ)k9IG{K#U7L7g00003bW%=J005Tb8uG6I007=e zL_t&-({0fEQ-VMc2k_&*mk)|Eg)~wzpCE;S*%N!3uTcO0|Hiu;1e$4oxtY)G?(Hri z|Ag`WMiB$j kf939u)|9u%*x?4k_INxJ(IlturvLx|07*qoM6N<$f{Ld3-~a#s diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/the_gauntlet.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/the_gauntlet.png index d24830e178bc468b3a7a8d05af0e77ca475ab53a..3e03f805c1124ef40e62b752f8c26b7dbb42d171 100644 GIT binary patch literal 510 zcmVM)TAr0otf67^%XH1daK*b|s-sDtl|XWDI%j0GMS6BKj&(nAYZ+!zC{RltPDdI!HIDVy zq~F((+17{k)p_*MjP1~J^Ur+K&xP#HdD6~`*3DM&$am4kVCTYd$Gtk`xoNq!P1mw& zyRkISs4~c+O{SYJxRpqol2)0JQjv;Eg@P!bfJlFRG?IBYc5gjyXDobXCTn9Ta$YQB zTr6N%C|6P%N<|GKAb$(b@c;k-0(4SNQvjB4|NBV*007EKL_t(2&#jQv(!xLtM!RR1 zmJ03;zq`8&{{9aH!?dL3lHpusbLPutV(kEhiq|60@}fyRQDI&FVF}cu2&#I%) zgyPmmDY$#;Efv&VqQplFuI$s&|DcC?bfQrTW7^&G{gcvg_NCplk1Rs9;5g+EPQ4fP)QnTR9c>uYP+#?&B9xtmqmGVh3w9b_1BE;&^hC{ zCTn9FN<}E2fMBVhKXGqnw5xT`%XQDhbIZYOzqM((uU({^eACa7+17{k)pyaxF1VFc zl#xb!dP$y?f9uSo-`8rpxH@NLG?IBVk9H_>UJW83NSl&Prkj-7*NWE7WwNL`XJSx{ zib#KbB1}daIWksisPWf;V<=dGygT$f989!cEt*GK_RSBxu3_000AYQchC< z0G8t#@~;2@0Ov_WK~xyiV_?7n7*PNdGZRP*NFl_SSwMn}%&g4F>e!eW7?_yZk;E98 zIe-e8Ik}j@3YfuWFadQlFmv-TGXX_-`S^jN%*-4DKrulfVP<9#Q894|NhxM&8CjrS zCT2Nlc`gM-C1n*=H5PSF4Q8-gm^HPub#(PK^;tAPrh?rJ6g4n3GBzG+d8X^<0xVr!V00MMUPE!Du zZ~yyA0002aNklMEU6l_g2+6x(BV|dqKhRQuA_o@deVQcw zVXW)&C=Z-=TQc?6*zKvyNglN9P1Uf5hCxqmVTU3=jIG_gUE@}OLXM_n&R4urC?YJ7khLrG-IVtbsOzp=8+rl-YgYlUoWh^(y0 zs;bALqr+uof0LBCet(}@T5)}SooQ)=)Mu@Y00003bW%=J005Tb8uG6I008kxL_t&- z(^b&vR>Ck41>lmKv=XY|LPc?*A}y|9!37jiSrowq1=PB||634f3T1xfd}n6PtWN+rAN4ou}uQ*RI-@>%@QY_TKCD^?^8gk^w$G zzY1yBWgHVD_%?ss)-O{L%n$b8e;0w-V9)}F6*d^}O9a{;00000NkvXXu0mjfBNF5IF32_C|Fu-W-WRjMZ=Nas1 zYGaU7P@0^U9iN&O5giv080PNft7odErm7?=!JC;`5S5rTp*yj*GBh|cI4Q=rAlJv) z!`Z;t$iYs})UDqgxxJ!ehguLqoj+14~N-!UA1geQmwmtn96==PRG!`S&z#};Za;PTo=so(e!lFr-uo(dZLNT-QGT>|yt>JTm}~)ML9rc) zJYq~K8h_ii%1Uwu{jcw>%Ddt2Y5B8ef_7%vQS&Pcv^h7k=q#|E+V}C(AyF+?U%8S^ z4^^JNF1mlZDy~5DF|9U_p7VugWPV_aK(c~wwQR*#BcKtL*+n`%~3ML;|0002eNklfrLx*ld92pz$;MtsqxtmwQrYj|wbkyt zDen*%hWY-{{_I)^&jh`mZGIW1-L)({&$Hp9(EMlhaCXY)TB7VK>4$V^s? ht;rvi#a@Ja_8+tP6$M+4>$m^_002ovPDHLkV1nQf)rJ57 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/tzkal_zuk.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/tzkal_zuk.png index c227671ec899df08a3ad14ee4eb70ff7c56022aa..1c6a966f19d2b07d95a35a4809ee3ee821d0d3ff 100644 GIT binary patch literal 370 zcmeAS@N?(olHy`uVBq!ia0vp^5NYw@Sgt!7}IPi9Lb$4=dvA2(~ zu@3g}39>NL)zS(J2ynErvN1RJFfuUF*SFW!^!N0%GBq_fHug3#a?#f_)YVm2RaI6} z@={jtGcl4B5-O4r3=!b)wzm(qvIsOcwba!K)76xfme!FJ-=HGWCo9-4$rCNW;laz! z#~42oXgzaDkY6yvG>*&L5(|Lb1)eUBAsXlRUU27QR^(y5U?4AH<@5gK*MH(Zo0>W| z$>r<(K6z<+`E4;qhkxt1t2#x#ho0pNph$#RNrL z3;bAjmUq*PQ#_|kZZuBa`RiD(QDj5SulM_ngr2{B#p`*B{cz*G^K|udS?83{ F1OU_ddc*(# literal 583 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{Rd`U{nb3332`Z|36R!83cNH`M9~s zN=sW98(W*1xjQ-8TUqI9X{oEKswgQL>gk0A1i0ASJ7{ZKn3%XaIJh}Fdb_#?`}zj^ z_=NcS23eT7=1n8` zc{n@!dwSTIo0}OKnds|#@Ur*G3T{vl2@>E`R#I}Zwe_~Q5ApU6_49Mq)6J zw6n0NZ@gUr3?0^zAirP+hH0D}Pu_g&01906ba4!kxIFj5^`Juz608ZE3OEE9g&iE0 zuJq_F5nU3_-L|ge{(oJcPy8{)b5_sIFH1Wg+5GTIL6ro6N5?t=@I3$l$zf}As(XBp7vtDXeqxh5MK4#PP zMdmJjF23n{(bKh2tIcQWF1V}s`rCw8{0-mvCFjSd9L}$pB4`lyCd2>45=MQ0QG*tl z%_nOTuiXFpGj`pz2MRgctY0lEyZ1kH^OlqC=jL=?taNZAMYgt!7}Akb&v1Ct_l47}b9 zLi!90i40O<3=&#gQs(>$*$fKa3<`-1d{5^`mvX6wF{s)xDA+MbWiyD_b#ny(buyI% z`2{mf%m4q|6Udw7>EaloaenK?+g!|wJS-QiCNJ4F{oeoo(bl_{zUcCAJ=g5SXSitj zewB%t0U>8JUaxvBWt?#N%r?1qafcKtO&mUJE4Aldns@YQqehg->{SN4%@fubdnxGu zIKmP-Wl#7*4z1dQu@kfkc5%vl+an*Hv6F%IR{w&Z`${J)l|JM>N7Sh8fJ^TBcTvAj ia~)Z1fAi<|J&epu%x~@Ee!l>^j=|H_&t;ucLK6Tn6l1Rd literal 315 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5t-T^)#uK)l42Qnc*mVsA~K~#@{ zSBY0tmVr}>Q&5e8)06>7itd_ZZNnhw#31UyDWS$7Xv!dA%D}0{z+l6`smH)6%fR5o zz$^bh_$JUG=8_=4U=NXwxhq*=8?!T}7et?wNUvN*3*wKEmgfrlhy1ZdfIzW@ux|{4#Qfk>#z^iQQcV z8xKntE3C>&Z)NW}*Dgt!7}2vC)i6A=_tl#D;!qu1$iBL8Dtn31RjU80!?Qs3GxeOn3n(lwzopr0BN^#lK=n! literal 378 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5t`2jv5uK)l42QneRjhDlbm%)mI zAwZDBiI_&|F*FL{l?TkV8g9#92hpN?$)&gkx%Vmx!RC zoglBdAg{iv>d8X~kL=#9t*9u&z_4xgsvT?B%$q!U-J(UCmM>d1Z(h=xAU2?7%q2m7 z!3+%39!q^%59Dt5ba4!kxSV_8HD8kf4{O3UUPzq7DX~#o3S)g#P%-z;6z_tMf}Pf-4@ZrPI$YzwMt?y{Qn&742`R*t+`G*{W* SU%Nd(!RP7f=d#Wzp$Pzvrixww diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vetion.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vetion.png index cfc9d47a03bc22fa10ec065933e3708013f243b3..67a7b3d645d3fab48a46ddb0cb58767e7ca7fb69 100644 GIT binary patch literal 347 zcmeAS@N?(olHy`uVBq!ia0vp^5NYw@Sgt!7}2vB8E?TzbkmvggX zu+(MHm0^&{=gmv!NQ>i$4c7^ekP8pz2=&wU^OW*bRdv!A)VH$Ilj4vPk=oqgQC}WV zmSdBhloS_f6cMBv6eJTEz!4DX>7}XZ72xUVrtZeT;poUMZ$C1Pd5U}7j`D59#X z$*VbCZG#)odZv;fzhH)G`Tu`=0(o7YE{-7@=eJHcE_BF%hxMaX-`u#Ddk_EruW+uw zfZ0mu?vlAzJ@SqGG^g-%OyKnH-SaX*)z#4ZoVjD-#3?WP=5ve0nqB8h+Sl?{x%bw* z8+)fJJW+pjCuUXk(dWrNjhEK-Ym4Wl@yi8?`1f2}kzm3*FaMQTa$BW>v-|O_?5eJ+ j?XEs)6N8US|6ec8Uc&Ow)7oe{D2O~={an^LB{Ts5H;QfJ literal 391 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5tlLLH0T>t<74`f0>Z(L7OQk*J- zYJjJwE`x4^M}0nTo+E?334=+FO?D(lL^?-Wq^FmytzjHTY&t^_7XSLGSzt>rJwQ9vKzt!z)C$Z|i-FKAl b_OE#PZ?WvATVH(h2Zg1ltDnm{r-UW|{SSsz diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vorkath.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/vorkath.png index 696669c35306f5dab14d48a9bbfb3f6ce5d81cef..c3732b1d0225342ec3ab508a0963fd1992713661 100644 GIT binary patch literal 478 zcmV<40U`d0P)RSjFN~{O;pX%%V?*#w!E@rp|MtrnVX}QmYk81n2k+* ziF|Wba85N za(HiUb8Bp5YB*V9R!~+^N>L|2MItpk)7Q~ro~~e;sj0E0U6rJ$ucCvEjeUlPfQ5T^ zZgW3oac68~G*VkwQdllbR8Uk+LOnu4ML;AvKOHhR5gi}^;YBF`0007XQchC~4BOD!q5OmEOz$e-#2z9EmqLPa_|VEX$dXLm(cX zK;jWA-~|b~$2_aRvO2iMlGedfXj*^^MEKU1Oz6697hSIb6g0VIBA5GfZ1=a8NK5*A zu;qyUJ+EA}-JeF6FLz3!rkat8`zL@{!7)TwR$(^b07*qoM6N<$f;hF#fdBvi literal 597 zcmV-b0;>IqP)zO=~A#k;|`P*hEBu)%P%#i_BSw!E@urnX?3sZfB7JYj3T!@A7S%9ovz zLPbD*bb4Ktq+FDuWumfTo~~|lYp%Ackd})|c!fxGe?@P3Ia_2A9UzO4hE9EoQiYM0 zosu+CTOu_)!^gk6zqY8aqJV{aaA|OAVrrF}kV0y7H&|gLIzJsUH;s$_5IdMv)r5uB1qC!TMYN!H$Y|@x z>*}fM>Khm`L)^#2EMuf&tgdTpVrpj2%n0@evxTLVwU&*ooxOvj6HLt6MaR|6-NTdB z%NwrFmD$V3*T+xSUco;ACKedv>+8!C91JyRaL5cQ+Z@kv|&>J_D`}_PoY^#pJYj8Q$zBtLcV}OU{687W;s(# zIMS>(#G*7vJ2T>XGWoAD*?KXna4^YaFN|L^eC9J*s1vtJq&6%}AS5^H4+XEzM2bP6yn3KA9O&J?2n^uB|frGS7B_}fjL`y_%VUdPf zo_I-?b-jtdI3vtZkt+3gC8bo6+z?yCK#w{RxMkw%8nLRX<=JtbVUn&!elQ6pfAOZq zXhk_Ww*pyN9p%#I3aA8+Sc9>tw4Q3L6*_IyEO#OgI%24jmgB0012v92|OSCvjpbAs!YX7Y@0L zVLL4#5DyE)qBIp16?9ApQ#u7_Hw+;f5R6|g$-1H9qG$iVe$tX`84e24tTs3r1G-}z zvtJskTNy1L7%(g#6bl1jJQA8>CHJ#+i%bEhgSd$ z2RAZ*C2M64KL7wQEeZt%1C2!h?YN2Vv4Q`-fcTbW{iJyIpnUM3Y4E&xs(VvpNG?b# zCi9wj@sMfPdt$U!?K#4ho@JN>?r?G8-2R2nGNEAlJmK z^vt2=-pmID1E7LxURhI_f@~5I3u#wKJ~1bMHZLk*PeBq63}sV8J1-?BBqT^XGn05@ zV^~lkARu{UQZy(XT2)m-JS!_GC<6uvXjx4U4i6RYSjYeX02XvoPE!CNra+HyurQhs z-symlV6oHC$WZY0SjecT0003BNklWS$a7#bNfF@ki7nV6cH zTUc6I+t}LKJ2*PAKnxRec5!uc_Ym{+^7ird^B03!5fB&@91DVH zdq%9iqq7U>q3)jEJ~3oJ3V^h+^iP;LX)+4~IuM&Ob=ve9Vq$`5!ZT;ho-=ped?7U9 z1q&A~&R-%Vj25;_mo3j{6A?x?TvUvM6V3qu*BLtJqan{R00000NkvXXu0mh|g2_5G A#sB~S diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/zalcano.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/zalcano.png index f2bdac1cea07b0239ff8fa057a8c12313ec3ce88..72a0a1714b22268e940714ba9285e09b8538689d 100644 GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^5NKFXv32_C|5a8kG9~>F!7aD4B zWo2n%VyLI*>gny{=H}t-Y-4V&t*x!BppcNB9+RA6>*Qo&V`FMypscDYqo5ET8yn~2 z6X)dMY-e}3P3o$Sl%%BOR0Y8@3Eu3Y;-svsBtPF6FHZ+o*Kbp0Kby$j)ss4_C2>?k zVn&Q$pRAy$pkRv>Z*M?bY&li=FZbU4Uly1)gVW-)3-`@m{-q!5lM@~%pLkGFpOD;kAht_$?m<7b z7AL+>Oxz-6ZT^j}3?Eo6v-xziW2V;a?%W}LnzMjY=X(3Lo{Bk7W}Lb4NvUU5*QZiG zvuN{&zhm#e;ESLB;qu1)sj@w%AFfEOc;cAAz;;~l>`zVR#lkP=74Li<5}z1SF;9KT uq)TsD<2CpD1v94cF=jFL>^kW2_h6i{Fh|IJq3>#-;PrI%b6Mw<&;$TbR*vxi literal 485 zcmeAS@N?(olHy`uVBq!ia0vp^!XV7S3?yCqj{O5t2LgOTT>t<74`f1sS74xTNQj%4 zkBOyaKvZ# zTPG(y1EcJs;v_%cyLwXQHa4;f3J$KWu_>u>P7Yc+I!5*mpG{=HO_iOhAb7V;NUMUF)dn+p&bMp!b-VzC34_;0wB_$OlB?Vd888L#g3=BqkdOmJ$eX@dK zf}A>9TBZgDqJn}>wzjSg4!iBt{eZ4#E(!7rW?-21SnAVyAorrDi(`ny<=hE3Cp8%e zxO(a>=seV*{ciU;r87I4x_(%=q=w^U?iJF|)RxVk_S&ZC-5p@4s`}-u3K} WJ^Ttk6#{O9V#?Fi&t;ucLK6V*#jC&o diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/zulrah.png b/runelite-client/src/main/resources/net/runelite/client/plugins/hiscore/bosses/zulrah.png index 7e2dc8807f59627687bcf2f085131e8d7e648c79..44a28c4cbd98d4eb4a5d3ae9a933ff6f754f0a8f 100644 GIT binary patch literal 346 zcmeAS@N?(olHy`uVBq!ia0vp^5NVNp`gt!7}VgNTc>xyO5CiK@9 z6{aoPB3e_&uc6N>EW)&F&%&8ATl)IS%FE+3Ga}Q|L!zRB($WGw%uLOl^pD<=-g#bX z{b7mjc_J;Hg7(3J%@g<|l6Y)gIOG&rCNE)#j$$yfWYFNda}($Yo{}KHV1|B!mwPsD zXmI#&AmI3or`AC6N>3NZ5RLP5&wC0rIq|%8N+iSGq%;z(I-@9*II+-@AB1DF&N{2Mlc-r}v$ht+glfbj|+XdbtZ4XV{#d>%M2pZ~xHUt93h5 zm?lmtdB$_b*q-m6%pc1i1rs)y`>pS?^zfC^`Rv!c`0UDC3~y($-mibhnFb0WPgg&e IbxsLQ0Amh${Qv*} literal 374 zcmeAS@N?(olHy`uVBq!ia0vp^LO?9c!3-o<`K&zvq~-_sgt!7}VgM%>Lqrmfx(g`33| zZ4q6vc Date: Mon, 1 Feb 2021 13:47:50 -0500 Subject: [PATCH 009/133] openosrs: use openosrs dir --- runelite-client/src/main/java/net/runelite/client/RuneLite.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9d728a6865..f2b4a9ac8d 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -93,7 +93,7 @@ import org.slf4j.LoggerFactory; @Slf4j public class RuneLite { - public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".runelite"); + public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".openosrs"); public static final File CACHE_DIR = new File(RUNELITE_DIR, "cache"); public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugins"); public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles"); From b5b64bc9ee05e2bf7e8d49be8821cfd463722632 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Wed, 27 Jan 2021 12:00:26 -0500 Subject: [PATCH 010/133] loottracker: add Gold Chest tracking (Shades of Mort'ton) --- .../client/plugins/loottracker/LootTrackerPlugin.java | 5 +++++ 1 file changed, 5 insertions(+) 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 7bf83abca3..816041298a 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 @@ -186,6 +186,11 @@ public class LootTrackerPlugin extends Plugin put(ObjectID.SILVER_CHEST_4128, "Silver key crimson"). put(ObjectID.SILVER_CHEST_4129, "Silver key black"). put(ObjectID.SILVER_CHEST_4130, "Silver key purple"). + put(ObjectID.GOLD_CHEST, "Gold key red"). + put(ObjectID.GOLD_CHEST_41213, "Gold key brown"). + put(ObjectID.GOLD_CHEST_41214, "Gold key crimson"). + put(ObjectID.GOLD_CHEST_41215, "Gold key black"). + put(ObjectID.GOLD_CHEST_41216, "Gold key purple"). build(); // Hallow Sepulchre Coffin handling From e4d5450283685ca7c39498bf48b6649cb7c9c062 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Wed, 27 Jan 2021 12:02:46 -0500 Subject: [PATCH 011/133] worldmap: add Shades of Mort'ton minigame location --- .../net/runelite/client/plugins/worldmap/MinigameLocation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java index a5f0b55ca0..cc387c4872 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MinigameLocation.java @@ -55,6 +55,7 @@ enum MinigameLocation PYRAMID_PLUNDER("Pyramid Plunder", new WorldPoint(3288, 2787, 0)), RANGING_GUILD("Ranging Guild", new WorldPoint(2671, 3419, 0)), ROGUES_DEN("Rogues' Den", new WorldPoint(2905, 3537, 0)), + SHADES_OF_MORTTON("Shades of Mort'ton", new WorldPoint(3505, 3315, 0)), SORCERESSS_GARDEN("Sorceress's Garden", new WorldPoint(3285, 3180, 0)), TROUBLE_BREWING("Trouble Brewing", new WorldPoint(3811, 3021, 0)), VOLCANIC_MINE("Volcanic Mine", new WorldPoint(3812, 3810, 0)), From 5e7242388daedef973ee3504a1831a093d42fc0a Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 2 Feb 2021 21:03:56 -0500 Subject: [PATCH 012/133] image component: support setPreferredSize --- .../ui/overlay/components/ImageComponent.java | 29 ++++++++++++------- 1 file changed, 19 insertions(+), 10 deletions(-) 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 39ec431697..46f07f0515 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 @@ -24,35 +24,37 @@ */ package net.runelite.client.ui.overlay.components; +import com.google.common.base.MoreObjects; 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.NonNull; import lombok.RequiredArgsConstructor; import lombok.Setter; +import net.runelite.client.util.ImageUtil; @RequiredArgsConstructor -@Setter public class ImageComponent implements LayoutableRenderableEntity { + @NonNull private final BufferedImage image; + private BufferedImage scaledImage; + @Getter private final Rectangle bounds = new Rectangle(); + @Setter private Point preferredLocation = new Point(); @Override public Dimension render(Graphics2D graphics) { - if (image == null) - { - return null; - } - - graphics.drawImage(image, preferredLocation.x, preferredLocation.y, null); + BufferedImage i = MoreObjects.firstNonNull(scaledImage, image); + graphics.drawImage(i, preferredLocation.x, preferredLocation.y, null); final Dimension dimension = new Dimension(image.getWidth(), image.getHeight()); bounds.setLocation(preferredLocation); bounds.setSize(dimension); @@ -60,8 +62,15 @@ public class ImageComponent implements LayoutableRenderableEntity } @Override - public void setPreferredSize(Dimension dimension) + public void setPreferredSize(Dimension preferredSize) { - // Just use image dimensions for now + if (preferredSize == null || (preferredSize.width == image.getWidth() && preferredSize.height == image.getHeight())) + { + scaledImage = null; + } + else + { + scaledImage = ImageUtil.resizeImage(image, preferredSize.width, preferredSize.height); + } } -} \ No newline at end of file +} From bbfa08f8b7d2343d97ef40be608fcfc69f52de43 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Mon, 25 Jan 2021 23:45:29 -0800 Subject: [PATCH 013/133] MenuOptionClicked: Add selectedItemIndex field --- .../main/java/net/runelite/api/events/MenuOptionClicked.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java b/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java index 8eb4707ae8..2cd15c48f9 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java +++ b/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java @@ -67,6 +67,10 @@ public class MenuOptionClicked * @see net.runelite.api.widgets.WidgetID */ private int widgetId; + /** + * The selected item index at the time of the option click. + */ + private int selectedItemIndex; /** * Whether or not the event has been consumed by a subscriber. */ From a58b2d3fdfd6dcb6c5c98b52a01995da351005f9 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 2 Feb 2021 23:26:18 -0500 Subject: [PATCH 014/133] Revert "image component: support setPreferredSize" This reverts commit 5e7242388daedef973ee3504a1831a093d42fc0a. --- .../ui/overlay/components/ImageComponent.java | 29 +++++++------------ 1 file changed, 10 insertions(+), 19 deletions(-) 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 46f07f0515..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 @@ -24,37 +24,35 @@ */ package net.runelite.client.ui.overlay.components; -import com.google.common.base.MoreObjects; 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.NonNull; import lombok.RequiredArgsConstructor; import lombok.Setter; -import net.runelite.client.util.ImageUtil; @RequiredArgsConstructor +@Setter public class ImageComponent implements LayoutableRenderableEntity { - @NonNull private final BufferedImage image; - private BufferedImage scaledImage; - @Getter private final Rectangle bounds = new Rectangle(); - @Setter private Point preferredLocation = new Point(); @Override public Dimension render(Graphics2D graphics) { - BufferedImage i = MoreObjects.firstNonNull(scaledImage, image); - graphics.drawImage(i, preferredLocation.x, preferredLocation.y, null); + if (image == null) + { + return null; + } + + graphics.drawImage(image, preferredLocation.x, preferredLocation.y, null); final Dimension dimension = new Dimension(image.getWidth(), image.getHeight()); bounds.setLocation(preferredLocation); bounds.setSize(dimension); @@ -62,15 +60,8 @@ public class ImageComponent implements LayoutableRenderableEntity } @Override - public void setPreferredSize(Dimension preferredSize) + public void setPreferredSize(Dimension dimension) { - if (preferredSize == null || (preferredSize.width == image.getWidth() && preferredSize.height == image.getHeight())) - { - scaledImage = null; - } - else - { - scaledImage = ImageUtil.resizeImage(image, preferredSize.width, preferredSize.height); - } + // Just use image dimensions for now } -} +} \ No newline at end of file From a6841c5cebca365bd71cfa610af9e5bc534c4e89 Mon Sep 17 00:00:00 2001 From: Jonathan Lee Date: Wed, 3 Feb 2021 07:29:48 -0800 Subject: [PATCH 015/133] loot tracker: add support for isle of souls chests They have the same description and behavior as stone chest. From melky test: 2021-02-03 13:54:03 [Client] DEBUG n.r.c.p.l.LootTrackerPlugin - Tick: 417 2021-02-03 13:54:04 [Client] DEBUG client-patch - Chat message type SPAM: You manage to unlock the chest. 2021-02-03 13:54:04 [Client] DEBUG n.r.c.p.l.LootTrackerPlugin - Tick: 418 2021-02-03 13:54:04 [Client] DEBUG client-patch - Chat message type SPAM: You steal some loot from the chest. 2021-02-03 13:54:04 [Client] DEBUG n.r.c.p.l.LootTrackerPlugin - Received icc: [Item(id=12791, quantity=1), Item(id=13068, quantity=1), Item(id=13222, quantity=1), Item(id=13069, quantity=1), Item(id=5698, quantity=1), Item(id=6705, quantity=1), Item(id=6705, quantity=1), Item(id=6705, quantity=1), Item(id=6705, quantity=1), Item(id=6705, quantity=1), Item(id=6705, quantity=1), Item(id=6705, quantity=1), Item(id=228, quantity=2), Item(id=12631, quantity=1), Item(id=11953, quantity=1), Item(id=1523, quantity=1), Item(id=1523, quantity=1), Item(id=1355, quantity=1), Item(id=314, quantity=121), Item(id=1621, quantity=1), Item(id=2510, quantity=8)] tick: 418 --- .../client/plugins/loottracker/LootTrackerPlugin.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) 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 816041298a..f3802e230a 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 @@ -145,7 +145,8 @@ public class LootTrackerPlugin extends Plugin // Chest loot handling private static final String CHEST_LOOTED_MESSAGE = "You find some treasure in the chest!"; private static final Pattern LARRAN_LOOTED_PATTERN = Pattern.compile("You have opened Larran's (big|small) chest .*"); - private static final String STONE_CHEST_LOOTED_MESSAGE = "You steal some loot from the chest."; + // Used by Stone Chest, Isle of Souls chest, Dark Chest + private static final String OTHER_CHEST_LOOTED_MESSAGE = "You steal some loot from the chest."; private static final String DORGESH_KAAN_CHEST_LOOTED_MESSAGE = "You find treasure inside!"; private static final String GRUBBY_CHEST_LOOTED_MESSAGE = "You have opened the Grubby Chest"; private static final Pattern HAM_CHEST_LOOTED_PATTERN = Pattern.compile("Your (?[a-z]+) key breaks in the lock.*"); @@ -161,6 +162,8 @@ public class LootTrackerPlugin extends Plugin put(10835, "Dorgesh-Kaan Chest"). put(10834, "Dorgesh-Kaan Chest"). put(7323, "Grubby Chest"). + put(8593, "Isle of Souls Chest"). + put(7827, "Dark Chest"). build(); // Shade chest loot handling @@ -630,7 +633,7 @@ public class LootTrackerPlugin extends Plugin final String message = event.getMessage(); - if (message.equals(CHEST_LOOTED_MESSAGE) || message.equals(STONE_CHEST_LOOTED_MESSAGE) + if (message.equals(CHEST_LOOTED_MESSAGE) || message.equals(OTHER_CHEST_LOOTED_MESSAGE) || message.equals(DORGESH_KAAN_CHEST_LOOTED_MESSAGE) || message.startsWith(GRUBBY_CHEST_LOOTED_MESSAGE) || LARRAN_LOOTED_PATTERN.matcher(message).matches()) { From 9381e62f6d321a0af215765101c370d621c4aa8e Mon Sep 17 00:00:00 2001 From: Jonatino Date: Thu, 14 Jan 2021 14:50:58 -0500 Subject: [PATCH 016/133] Fix incorrect named value when storing FlatStorage caches --- cache/src/main/java/net/runelite/cache/fs/flat/FlatStorage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cache/src/main/java/net/runelite/cache/fs/flat/FlatStorage.java b/cache/src/main/java/net/runelite/cache/fs/flat/FlatStorage.java index 92a82e26b7..c8d651bcf6 100644 --- a/cache/src/main/java/net/runelite/cache/fs/flat/FlatStorage.java +++ b/cache/src/main/java/net/runelite/cache/fs/flat/FlatStorage.java @@ -220,7 +220,7 @@ public class FlatStorage implements Storage br.printf("revision=%d\n", idx.getRevision()); br.printf("compression=%d\n", idx.getCompression()); br.printf("crc=%d\n", idx.getCrc()); - br.printf("named=%b\n", idx.getCompression()); + br.printf("named=%b\n", idx.isNamed()); idx.getArchives().sort(Comparator.comparing(Archive::getArchiveId)); for (Archive archive : idx.getArchives()) From 27fb56f5b8cdfc8b376b1d42b48953e6c348277f Mon Sep 17 00:00:00 2001 From: Taylor Abraham Date: Fri, 11 Dec 2020 10:08:47 -0500 Subject: [PATCH 017/133] music: Make volume percent visible when hovering handle --- .../java/net/runelite/client/plugins/music/MusicPlugin.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java index 6f3421b08a..b557cc18b3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/music/MusicPlugin.java @@ -466,6 +466,7 @@ public class MusicPlugin extends Plugin public void update() { + handle.setNoClickThrough(false); handle.setOnDragListener((JavaScriptCallback) this::drag); handle.setOnDragCompleteListener((JavaScriptCallback) this::drag); handle.setHasListener(true); @@ -511,6 +512,9 @@ public class MusicPlugin extends Plugin int level = (x * channel.max) / getWidth(); level = Ints.constrainToRange(level, 0, channel.max); channel.setLevel(level); + + int percent = (int) Math.round((level * 100.0 / channel.getMax())); + sliderTooltip = new Tooltip(channel.getName() + ": " + percent + "%"); } protected int getWidth() From 263b02ac14ce1c298790915b46decac6445b06a9 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Feb 2021 14:20:50 -0500 Subject: [PATCH 018/133] config manager: use createTempFile for config temp file With multiple clients with different configs saving at once, the same temp file was being used, which can cause it to not save or to move() a temp file from a different client --- .../src/main/java/net/runelite/client/config/ConfigManager.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 01d8ebfe1d..a2a4eef25c 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 @@ -394,7 +394,7 @@ public class ConfigManager parent.mkdirs(); - File tempFile = new File(parent, RuneLite.DEFAULT_CONFIG_FILE.getName() + ".tmp"); + File tempFile = File.createTempFile("runelite", null, parent); try (FileOutputStream out = new FileOutputStream(tempFile)) { From 4f6f518a153e4424c27c4c15cbc444e72744d7e6 Mon Sep 17 00:00:00 2001 From: Usman Akhtar <60450353+akhtar-u@users.noreply.github.com> Date: Wed, 3 Feb 2021 17:18:16 -0500 Subject: [PATCH 019/133] worldmap: use boosted level for map icon tooltips --- .../net/runelite/client/plugins/worldmap/WorldMapPlugin.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 1114d56720..dcac0c700f 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 @@ -186,7 +186,7 @@ public class WorldMapPlugin extends Plugin { case AGILITY: { - int newAgilityLevel = statChanged.getLevel(); + int newAgilityLevel = statChanged.getBoostedLevel(); if (newAgilityLevel != agilityLevel) { agilityLevel = newAgilityLevel; @@ -196,7 +196,7 @@ public class WorldMapPlugin extends Plugin } case WOODCUTTING: { - int newWoodcutLevel = statChanged.getLevel(); + int newWoodcutLevel = statChanged.getBoostedLevel(); if (newWoodcutLevel != woodcuttingLevel) { woodcuttingLevel = newWoodcutLevel; From e90b8293de3e37dc3c99578ce2d7438001492b1c Mon Sep 17 00:00:00 2001 From: Usman Akhtar Date: Mon, 25 Jan 2021 23:47:05 -0800 Subject: [PATCH 020/133] grounditems: Add despawn timer for items placed on tables --- .../grounditems/GroundItemsOverlay.java | 30 ++++++++++++++----- .../grounditems/GroundItemsPlugin.java | 25 ++++++++++++++-- .../client/plugins/grounditems/LootType.java | 1 + 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java index e2a69bec33..6f0b96aff9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java @@ -75,6 +75,7 @@ public class GroundItemsOverlay extends Overlay private static final Duration DESPAWN_TIME_INSTANCE = Duration.ofMinutes(30); private static final Duration DESPAWN_TIME_LOOT = Duration.ofMinutes(2); private static final Duration DESPAWN_TIME_DROP = Duration.ofMinutes(3); + private static final Duration DESPAWN_TIME_TABLE = Duration.ofMinutes(10); private static final int KRAKEN_REGION = 9116; private static final int KBD_NMZ_REGION = 9033; private static final int ZILYANA_REGION = 11602; @@ -397,8 +398,11 @@ public class GroundItemsOverlay extends Overlay private Instant calculateDespawnTime(GroundItem groundItem) { - // We can only accurately guess despawn times for our own pvm loot and dropped items - if (groundItem.getLootType() != LootType.PVM && groundItem.getLootType() != LootType.DROPPED) + // We can only accurately guess despawn times for our own pvm loot, dropped items, + // and items we placed on tables + if (groundItem.getLootType() != LootType.PVM + && groundItem.getLootType() != LootType.DROPPED + && groundItem.getLootType() != LootType.TABLE) { return null; } @@ -461,9 +465,18 @@ public class GroundItemsOverlay extends Overlay } else { - despawnTime = spawnTime.plus(groundItem.getLootType() == LootType.DROPPED - ? DESPAWN_TIME_DROP - : DESPAWN_TIME_LOOT); + switch (groundItem.getLootType()) + { + case DROPPED: + despawnTime = spawnTime.plus(DESPAWN_TIME_DROP); + break; + case TABLE: + despawnTime = spawnTime.plus(DESPAWN_TIME_TABLE); + break; + default: + despawnTime = spawnTime.plus(DESPAWN_TIME_LOOT); + break; + } } if (now.isBefore(spawnTime) || now.isAfter(despawnTime)) @@ -477,8 +490,11 @@ public class GroundItemsOverlay extends Overlay private Color getItemTimerColor(GroundItem groundItem) { - // We can only accurately guess despawn times for our own pvm loot and dropped items - if (groundItem.getLootType() != LootType.PVM && groundItem.getLootType() != LootType.DROPPED) + // We can only accurately guess despawn times for our own pvm loot, dropped items, + // and items we placed on tables + if (groundItem.getLootType() != LootType.PVM + && groundItem.getLootType() != LootType.DROPPED + && groundItem.getLootType() != LootType.TABLE) { return null; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java index 0c8db3a0e1..e7e79b5186 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java @@ -52,7 +52,10 @@ import lombok.Setter; import lombok.Value; import net.runelite.api.Client; import net.runelite.api.GameState; +import net.runelite.api.InventoryID; +import net.runelite.api.Item; import net.runelite.api.ItemComposition; +import net.runelite.api.ItemContainer; import net.runelite.api.ItemID; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; @@ -180,6 +183,7 @@ public class GroundItemsPlugin extends Plugin private LoadingCache highlightedItems; private LoadingCache hiddenItems; private final Queue droppedItemQueue = EvictingQueue.create(16); // recently dropped items + private int lastUsedItem; @Provides GroundItemsConfig provideConfig(ConfigManager configManager) @@ -194,6 +198,7 @@ public class GroundItemsPlugin extends Plugin mouseManager.registerMouseListener(inputListener); keyManager.registerKeyListener(inputListener); executor.execute(this::reset); + lastUsedItem = -1; } @Override @@ -384,6 +389,7 @@ public class GroundItemsPlugin extends Plugin final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemId; final int alchPrice = itemComposition.getHaPrice(); final boolean dropped = tile.getWorldLocation().equals(client.getLocalPlayer().getWorldLocation()) && droppedItemQueue.remove(itemId); + final boolean table = itemId == lastUsedItem && tile.getItemLayer().getHeight() > 0; final GroundItem groundItem = GroundItem.builder() .id(itemId) @@ -394,12 +400,11 @@ public class GroundItemsPlugin extends Plugin .haPrice(alchPrice) .height(tile.getItemLayer().getHeight()) .tradeable(itemComposition.isTradeable()) - .lootType(dropped ? LootType.DROPPED : LootType.UNKNOWN) + .lootType(dropped ? LootType.DROPPED : (table ? LootType.TABLE : LootType.UNKNOWN)) .spawnTime(Instant.now()) .stackable(itemComposition.isStackable()) .build(); - // Update item price in case it is coins if (realItemId == COINS) { @@ -687,5 +692,21 @@ public class GroundItemsPlugin extends Plugin // item spawns that are drops droppedItemQueue.add(itemId); } + else if (menuOptionClicked.getMenuAction() == MenuAction.ITEM_USE_ON_GAME_OBJECT) + { + final ItemContainer inventory = client.getItemContainer(InventoryID.INVENTORY); + if (inventory == null) + { + return; + } + + final Item clickedItem = inventory.getItem(menuOptionClicked.getSelectedItemIndex()); + if (clickedItem == null) + { + return; + } + + lastUsedItem = clickedItem.getId(); + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/LootType.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/LootType.java index b434298faf..604815732f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/LootType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/LootType.java @@ -27,6 +27,7 @@ package net.runelite.client.plugins.grounditems; enum LootType { UNKNOWN, + TABLE, DROPPED, PVP, PVM; From df909d2a73e5720052da90e4a9ebf6b75a805a79 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 4 Feb 2021 06:13:57 +0000 Subject: [PATCH 021/133] 1/n --- .../java/com/openosrs/api/AnimationID.java | 239 ------- .../com/openosrs/api/widgets/Varbits.java | 312 -------- .../com/openosrs/api/widgets/WidgetID.java | 673 ------------------ .../com/openosrs/api/widgets/WidgetInfo.java | 488 ------------- .../main/java/net/runelite/api/Client.java | 11 +- .../main/java/net/runelite/api/Varbits.java | 11 + .../net/runelite/api/events/ActorDeath.java | 2 +- .../runelite/api/events/AnimationChanged.java | 2 +- .../api/events/AreaSoundEffectPlayed.java | 2 +- .../runelite/api/events/BeforeMenuRender.java | 2 +- .../net/runelite/api/events/BeforeRender.java | 2 +- .../runelite/api/events/CannonChanged.java | 2 +- .../net/runelite/api/events/CannonPlaced.java | 2 +- .../api/events/CanvasSizeChanged.java | 2 +- .../net/runelite/api/events/ChatMessage.java | 2 +- .../net/runelite/api/events/ClientTick.java | 2 +- .../runelite/api/events/CommandExecuted.java | 2 +- .../api/events/ConfigButtonClicked.java | 3 +- .../api/events/DecorativeObjectChanged.java | 2 +- .../api/events/DecorativeObjectDespawned.java | 2 +- .../api/events/DecorativeObjectSpawned.java | 2 +- .../runelite/api/events/DialogProcessed.java | 2 +- .../api/events/DraggingWidgetChanged.java | 2 +- .../events/DynamicObjectAnimationChanged.java | 2 +- .../java/net/runelite/api/events/Event.java | 3 - .../net/runelite/api/events/FakeXpDrop.java | 2 +- .../net/runelite/api/events/FocusChanged.java | 2 +- .../net/runelite/api/events/FriendAdded.java | 2 +- .../api/events/FriendsChatChanged.java | 2 +- .../api/events/FriendsChatMemberJoined.java | 2 +- .../api/events/FriendsChatMemberLeft.java | 2 +- .../api/events/GameObjectChanged.java | 2 +- .../api/events/GameObjectDespawned.java | 2 +- .../api/events/GameObjectSpawned.java | 2 +- .../runelite/api/events/GameStateChanged.java | 2 +- .../net/runelite/api/events/GameTick.java | 2 +- .../api/events/GrandExchangeOfferChanged.java | 2 +- .../api/events/GrandExchangeSearched.java | 2 +- .../runelite/api/events/GraphicChanged.java | 2 +- .../api/events/GraphicsObjectCreated.java | 2 +- .../api/events/GroundObjectChanged.java | 2 +- .../api/events/GroundObjectDespawned.java | 2 +- .../api/events/GroundObjectSpawned.java | 2 +- .../runelite/api/events/HitsplatApplied.java | 2 +- .../runelite/api/events/InteractChanged.java | 2 +- .../api/events/InteractingChanged.java | 2 +- .../api/events/ItemContainerChanged.java | 2 +- .../runelite/api/events/ItemDespawned.java | 2 +- .../api/events/ItemQuantityChanged.java | 2 +- .../net/runelite/api/events/ItemSpawned.java | 2 +- .../java/net/runelite/api/events/Menu.java | 2 +- .../runelite/api/events/MenuEntryAdded.java | 2 +- .../net/runelite/api/events/MenuOpened.java | 2 +- .../api/events/MenuOptionClicked.java | 2 +- .../api/events/MenuShouldLeftClick.java | 2 +- .../api/events/NameableNameChanged.java | 2 +- .../runelite/api/events/NpcActionChanged.java | 2 +- .../net/runelite/api/events/NpcChanged.java | 2 +- .../net/runelite/api/events/NpcDespawned.java | 2 +- .../net/runelite/api/events/NpcSpawned.java | 2 +- .../api/events/OverheadPrayerChanged.java | 40 ++ .../api/events/OverheadTextChanged.java | 2 +- .../runelite/api/events/PlayerDespawned.java | 2 +- .../api/events/PlayerMenuOptionClicked.java | 2 +- .../api/events/PlayerMenuOptionsChanged.java | 2 +- .../api/events/PlayerSkullChanged.java | 40 ++ .../runelite/api/events/PlayerSpawned.java | 2 +- .../runelite/api/events/PostHealthBar.java | 2 +- .../api/events/PostItemComposition.java | 2 +- .../runelite/api/events/ProjectileMoved.java | 2 +- .../api/events/ProjectileSpawned.java | 2 +- .../api/events/ResizeableChanged.java | 2 +- .../api/events/ScriptCallbackEvent.java | 2 +- .../runelite/api/events/ScriptPostFired.java | 2 +- .../runelite/api/events/ScriptPreFired.java | 2 +- .../api/events/SoundEffectPlayed.java | 2 +- .../net/runelite/api/events/StatChanged.java | 2 +- .../runelite/api/events/UsernameChanged.java | 2 +- .../api/events/VarClientIntChanged.java | 2 +- .../api/events/VarClientStrChanged.java | 2 +- .../runelite/api/events/VarbitChanged.java | 2 +- .../runelite/api/events/VolumeChanged.java | 2 +- .../api/events/WallObjectChanged.java | 2 +- .../api/events/WallObjectDespawned.java | 2 +- .../api/events/WallObjectSpawned.java | 2 +- .../net/runelite/api/events/WidgetLoaded.java | 2 +- .../api/events/WidgetMenuOptionClicked.java | 2 +- .../runelite/api/events/WidgetPositioned.java | 2 +- .../runelite/api/events/WidgetPressed.java | 2 +- .../runelite/api/events/WorldListLoad.java | 2 +- .../headicon/OverheadPrayerChanged.java | 17 - .../player/headicon/PlayerSkullChanged.java | 17 - .../java/net/runelite/api/kit/KitType.java | 31 +- .../runelite/api/queries/ShopItemQuery.java | 4 +- .../java/com/openosrs/client/OpenOSRS.java | 18 +- .../client/config/OpenOSRSConfig.java | 4 +- .../client/events/ExternalPluginChanged.java | 3 +- .../client/events/ExternalPluginsLoaded.java | 3 +- .../events/ExternalRepositoryChanged.java | 3 +- .../com/openosrs/client/game/NPCStats.java | 229 ------ .../plugins/ExternalPluginClasspath.java | 13 - .../plugins/ExternalPluginFileFilter.java | 61 -- .../com/openosrs/client/plugins/Plugin.java | 9 - .../plugins/openosrs/OpenOSRSPlugin.java | 6 +- .../externals/ExternalPluginManagerPanel.java | 10 +- .../openosrs/externals/PluginsPanel.java | 10 +- .../openosrs/externals/RepositoryBox.java | 6 +- .../openosrs/externals/RepositoryPanel.java | 6 +- .../client/ui/components/InfoPanel.java | 3 +- .../com/openosrs/client/util/ColorUtil.java | 34 - .../util/DeferredDocumentChangedListener.java | 58 -- .../com/openosrs/client/util/ImageUtil.java | 59 -- .../java/net/runelite/client/RuneLite.java | 7 +- .../OPRSExternalPf4jPluginManager.java} | 37 +- .../plugins/OPRSExternalPluginClasspath.java | 38 + .../plugins/OPRSExternalPluginFileFilter.java | 86 +++ .../plugins/OPRSExternalPluginManager.java} | 15 +- .../net/runelite/client/plugins/Plugin.java | 3 +- .../plugins/config/PluginToggleButton.java | 2 +- .../client/plugins/info/InfoPanel.java | 13 +- .../util/DeferredDocumentChangedListener.java | 83 +++ .../net/runelite/client/util/ImageUtil.java | 52 ++ .../net/runelite/client/util/PvPUtil.java | 104 +++ .../com/openosrs/client/game/npc_stats.json | 1 - .../src/main/resources/openosrs.properties | 1 + .../net/runelite/mixins/RSClientMixin.java | 10 - .../net/runelite/mixins/RSPlayerMixin.java | 22 +- 127 files changed, 649 insertions(+), 2409 deletions(-) delete mode 100644 runelite-api/src/main/java/com/openosrs/api/AnimationID.java delete mode 100644 runelite-api/src/main/java/com/openosrs/api/widgets/Varbits.java delete mode 100644 runelite-api/src/main/java/com/openosrs/api/widgets/WidgetID.java delete mode 100644 runelite-api/src/main/java/com/openosrs/api/widgets/WidgetInfo.java delete mode 100644 runelite-api/src/main/java/net/runelite/api/events/Event.java create mode 100644 runelite-api/src/main/java/net/runelite/api/events/OverheadPrayerChanged.java create mode 100644 runelite-api/src/main/java/net/runelite/api/events/PlayerSkullChanged.java delete mode 100644 runelite-api/src/main/java/net/runelite/api/events/player/headicon/OverheadPrayerChanged.java delete mode 100644 runelite-api/src/main/java/net/runelite/api/events/player/headicon/PlayerSkullChanged.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/game/NPCStats.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginClasspath.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginFileFilter.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/plugins/Plugin.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/util/ColorUtil.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/util/DeferredDocumentChangedListener.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/util/ImageUtil.java rename runelite-client/src/main/java/{com/openosrs/client/plugins/ExternalPf4jPluginManager.java => net/runelite/client/plugins/OPRSExternalPf4jPluginManager.java} (82%) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginClasspath.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginFileFilter.java rename runelite-client/src/main/java/{com/openosrs/client/plugins/ExternalPluginManager.java => net/runelite/client/plugins/OPRSExternalPluginManager.java} (98%) create mode 100644 runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java create mode 100644 runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java delete mode 100644 runelite-client/src/main/resources/com/openosrs/client/game/npc_stats.json create mode 100644 runelite-client/src/main/resources/openosrs.properties diff --git a/runelite-api/src/main/java/com/openosrs/api/AnimationID.java b/runelite-api/src/main/java/com/openosrs/api/AnimationID.java deleted file mode 100644 index 8a3de35994..0000000000 --- a/runelite-api/src/main/java/com/openosrs/api/AnimationID.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.openosrs.api; - -//This is where Animation IDs should go that aren't found in RuneLite -public class AnimationID extends net.runelite.api.AnimationID -{ - - // NPC animations - public static final int TZTOK_JAD_MAGIC_ATTACK = 2656; - public static final int TZTOK_JAD_RANGE_ATTACK = 2652; - public static final int HELLHOUND_DEFENCE = 6566; - - public static final int FISHING_TRAILBLAZER_HARPOON_2 = 8785; - - public static final int CRYSTALLINE_RAT_DEATH = 8334; - public static final int CRYSTALLINE_BAT_DEATH = 4917; - public static final int CRYSTALLINE_WOLF_DEATH = 8335; - public static final int CRYSTALLINE_SPIDER_DEATH = 8338; - public static final int CRYSTALLINE_UNICORN_DEATH = 6377; - public static final int CRYSTALLINE_DRAGON_DEATH = 92; - public static final int CRYSTALLINE_BEAR_DEATH = 4929; - public static final int CRYSTALLINE_DARK_BEAST_DEATH = 2733; - public static final int CORRUPTED_SCORPION_DEATH = 6256; - - public static final int TABLET_TELEPORT = 4069; - - public static final int THIEVING_STALL = 832; - public static final int PICKPOCKET_SUCCESS = 881; - public static final int PULL_LEVER = 2140; - public static final int STANDARD_PURPLE_TELEPORT = 714; - public static final int ECTOPHIAL_TELEPORT = 878; - public static final int FAIRY_RING_TELEPORT = 3265; - public static final int SCROLL_TELEPORT = 3864; - public static final int XERICS_TALISMAN_TELEPORT = 3865; - public static final int WILDERNESS_OBELISK_TELEPORT = 3945; - public static final int SEED_POD_TELEPORT = 4544; - - //block animations for players and perhaps npcs as well? - public static final int BLOCK_DEFENDER = 4177; - public static final int BLOCK_NO_SHIELD = 420; - public static final int BLOCK_SHIELD = 1156; - public static final int BLOCK_SWORD = 388; - public static final int BLOCK_UNARMED = 424; // Same Animation as failed pickpocked - - public static final int NIGHTMARE_DEATH = 8612; - public static final int LOW_LEVEL_MAGIC_ATTACK = 1162; - public static final int HIGH_LEVEL_MAGIC_ATTACK = 1167; - public static final int BLOWPIPE_ATTACK = 5061; - - // NPC animations - public static final int BLACKJACK_KO = 838; - - // Fight Caves - public static final int TZTOK_JAD_MELEE_ATTACK = 2655; - public static final int TOK_XIL_RANGE_ATTACK = 2633; - public static final int TOK_XIL_MELEE_ATTACK = 2628; - public static final int KET_ZEK_MELEE_ATTACK = 2644; - public static final int KET_ZEK_MAGE_ATTACK = 2647; - public static final int MEJ_KOT_MELEE_ATTACK = 2637; - public static final int MEJ_KOT_HEAL_ATTACK = 2639; - - // Vorkath - public static final int VORKATH_WAKE_UP = 7950; - public static final int VORKATH_DEATH = 7949; - public static final int VORKATH_SLASH_ATTACK = 7951; - public static final int VORKATH_ATTACK = 7952; - public static final int VORKATH_FIRE_BOMB_OR_SPAWN_ATTACK = 7960; - public static final int VORKATH_ACID_ATTACK = 7957; - - // Tekton - public static final int TEKTON_ANVIL = 7475; - public static final int TEKTON_AUTO1 = 7482; - public static final int TEKTON_AUTO2 = 7483; - public static final int TEKTON_AUTO3 = 7484; - public static final int TEKTON_FAST_AUTO1 = 7478; - public static final int TEKTON_FAST_AUTO2 = 7488; - public static final int TEKTON_ENRAGE_AUTO1 = 7492; - public static final int TEKTON_ENRAGE_AUTO2 = 7493; - public static final int TEKTON_ENRAGE_AUTO3 = 7494; - - // Hydra - public static final int HYDRA_WALKING = 8232; - public static final int HYDRA_IDLE = 8233; - public static final int HYDRA_POISON_1 = 8234; - public static final int HYDRA_RANGED_1 = 8235; - public static final int HYDRA_MAGIC_1 = 8236; - public static final int HYDRA_1_1 = 8237; - public static final int HYDRA_1_2 = 8238; - public static final int HYDRA_LIGHTNING = 8241; - public static final int HYDRA_RANGED_2 = 8242; - public static final int HYDRA_MAGIC_2 = 8243; - public static final int HYDRA_2_1 = 8244; - public static final int HYDRA_2_2 = 8245; - public static final int HYDRA_FIRE = 8248; - public static final int HYDRA_RANGED_3 = 8249; - public static final int HYDRA_MAGIC_3 = 8250; - public static final int HYDRA_3_1 = 8251; - public static final int HYDRA_3_2 = 8252; - public static final int HYDRA_MAGIC_4 = 8254; - public static final int HYDRA_POISON_4 = 8254; - public static final int HYDRA_RANGED_4 = 8255; - public static final int HYDRA_RANGED_OR_POISON_ATTACK = 8256; - public static final int HYDRA_4_1 = 8257; - public static final int HYDRA_4_2 = 8258; - - // Inferno animations - public static final int JAL_NIB = 7574; - public static final int JAL_MEJRAH = 7578; - public static final int JAL_MEJRAH_STAND = 7577; - public static final int JAL_AK_RANGE_ATTACK = 7581; - public static final int JAL_AK_MELEE_ATTACK = 7582; - public static final int JAL_AK_MAGIC_ATTACK = 7583; - public static final int JAL_IMKOT = 7597; - public static final int JAL_XIL_MELEE_ATTACK = 7604; - public static final int JAL_XIL_RANGE_ATTACK = 7605; - public static final int JAL_ZEK_MAGE_ATTACK = 7610; - public static final int JAL_ZEK_MELEE_ATTACK = 7612; - public static final int JALTOK_JAD_MELEE_ATTACK = 7590; - public static final int JALTOK_JAD_MAGE_ATTACK = 7592; - public static final int JALTOK_JAD_RANGE_ATTACK = 7593; - public static final int TZKAL_ZUK = 7566; - public static final int JAL_MEJJAK = 2858; - - // General Graardor - public static final int MINION_AUTO1 = 6154; - public static final int MINION_AUTO2 = 6156; - public static final int MINION_AUTO3 = 7071; - public static final int MINION_AUTO4 = 7073; - public static final int GENERAL_AUTO1 = 7018; - public static final int GENERAL_AUTO2 = 7020; - public static final int GENERAL_AUTO3 = 7021; - - // Kr'il Tsutsaroth - public static final int ZAMMY_GENERIC_AUTO = 64; - public static final int KRIL_AUTO = 6948; - public static final int KRIL_SPEC = 6950; - public static final int ZAKL_AUTO = 7077; - public static final int BALFRUG_AUTO = 4630; - - // Commander Zilyana - public static final int ZILYANA_MELEE_AUTO = 6964; - public static final int ZILYANA_AUTO = 6967; - public static final int ZILYANA_SPEC = 6970; - public static final int STARLIGHT_AUTO = 6376; - public static final int BREE_AUTO = 7026; - public static final int GROWLER_AUTO = 7037; - - // Kree'arra - public static final int KREE_RANGED = 6978; - public static final int SKREE_AUTO = 6955; - public static final int GEERIN_AUTO = 6956; - public static final int GEERIN_FLINCH = 6958; - public static final int KILISA_AUTO = 6957; - - // Vetion - public static final int VETION_EARTHQUAKE = 5507; - - // Zulrah - public static final int ZULRAH_DEATH = 5804; - public static final int ZULRAH_PHASE = 5072; - - //Dagannoth Kings - public static final int DAG_REX = 2853; - public static final int DAG_PRIME = 2854; - public static final int DAG_SUPREME = 2855; - - // Lizardman shaman - public static final int LIZARDMAN_SHAMAN_SPAWN = 7157; - public static final int LIZARDMAN_SHAMAN_SPAWN_EXPLOSION = 7159; - - // Cerberus - public static final int CERBERUS_MAGIC_ATTACK = 4489; - public static final int CERBERUS_RANGED_ATTACK = 4490; - public static final int CERBERUS_MELEE_ATTACK = 4491; - public static final int CERBERUS_LAVA_ATTACK = 4493; - public static final int CERBERUS_SUMMON_GHOSTS = 4494; - - // Gauntlet Hunleff - public static final int HUNLEFF_TRAMPLE = 8420; - public static final int HUNLEFF_ATTACK = 8419; - public static final int HUNLEFF_TORNADO = 8418; - public static final int HUNLLEF_SWITCH_TO_MAGIC = 8754; - public static final int HUNLLEF_SWITCH_TO_RANGED = 8755; - - //Zalcano - public static final int ZALCANO_KNOCKED_DOWN = 8437; - public static final int ZALCANO_WAKEUP = 8439; - public static final int ZALCANO_ROCK_GLOWING = 8448; - - // Theatre of Blood - Sugadinti Maiden - public static final int SUGADINTI_MAIDEN_BLOOD_SPLAT_ATTACK = 8091; - public static final int SUGADINTI_MAIDEN_MAGIC_ATTACK = 8092; - public static final int SUGADINTI_MAIDEN_DEATH = 8094; - - // Theatre of Blood - Pestilent Bloat - public static final int BLOAT_SLEEP = 8082; - - // Theatre of Blood - Sotetseg - public static final int SOTETSEG_MELEE_ATTACK = 8138; - public static final int SOTETSEG_REGULAR_PROJECTILE_ATTACK = 8139; - - // Theatre of Blood - Verzik Vitur - public static final int VERZIK_PHASE_1_MAGIC_ATTACK = 8109; - public static final int VERZIK_PHASE_1_MAGIC_ATTACK_CHANNEL = 8110; - public static final int VERZIK_CHANGE_TO_PHASE_2 = 8111; - public static final int VERZIK_PHASE_2_MAGIC_ATTACK = 8114; - public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_1 = 8116; - public static final int VERZIK_PHASE_2_HEALING_CHANNEL = 8117; - public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_2 = 8118; - public static final int VERZIK_CHANGE_TO_PHASE_3 = 8119; - public static final int VERZIK_PHASE_3_MELEE_ATTACK = 8123; - public static final int VERZIK_PHASE_3_MAGIC_ATTACK = 8124; - public static final int VERZIK_PHASE_3_RANGED_ATTACK = 8125; - public static final int VERZIK_PHASE_3_GREEN_POOL_ATTACK = 8126; - public static final int VERZIK_PHASE_3_WEB_ATTACK = 8127; - public static final int VERZIK_DEATH_1 = 8128; - public static final int VERZIK_DEATH_2 = 8129; - - // The Nightmare of Ashihama - public static final int NIGHTMARE_SPAWN_SLEEPWALKERS = 8572; - public static final int NIGHTMARE_FLOATY = 8592; - public static final int NIGHTMARE_WALKING = 8592; - public static final int NIGHTMARE_IDLE = 8593; - public static final int NIGHTMARE_MELEE_ATTACK = 8594; - public static final int NIGHTMARE_MAGIC_ATTACK = 8595; - public static final int NIGHTMARE_RANGED_ATTACK = 8596; - public static final int NIGHTMARE_SURGE_ATTACK = 8597; - public static final int NIGHTMARE_GHOST_AOE_ATTACK = 8598; - public static final int NIGHTMARE_CURSE_PRAYERS_ATTACK = 8599; - public static final int NIGHTMARE_SPAWN_INFECTIOUS_SPORES = 8600; - public static final int NIGHTMARE_SPAWN_ROOM_SECTION_FLOWERS = 8601; - public static final int NIGHTMARE_CHANNEL_DEVASTATING_ATTACK = 8604; - public static final int NIGHTMARE_SWITCH_TO_DEVIL_PHASE = 8605; - public static final int NIGHTMARE_PARASITE_ATTACK = 8606; - public static final int NIGHTMARE_JUMP_DOWN = 8607; - public static final int NIGHTMARE_SINK_DOWN = 8608; - public static final int NIGHTMARE_JUMP_UP = 8609; - public static final int NIGHTMARE_JUMP_UP_2 = 8610; - public static final int NIGHTMARE_WAKE_UP = 8611; -} diff --git a/runelite-api/src/main/java/com/openosrs/api/widgets/Varbits.java b/runelite-api/src/main/java/com/openosrs/api/widgets/Varbits.java deleted file mode 100644 index 805f65efb0..0000000000 --- a/runelite-api/src/main/java/com/openosrs/api/widgets/Varbits.java +++ /dev/null @@ -1,312 +0,0 @@ -package com.openosrs.api.widgets; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public enum Varbits -{ - /* - * Kharedst's Memoirs Teleport Item - */ - KHAREDSTS_MEMOIRS_CHARGES(6035), - - /** - * Grand Exchange - */ - GRAND_EXCHANGE_PRICE_PER_ITEM(4398), - - - /** - * Locked Prayers - * 0-7 = Locked - * 8 = Unlocked - */ - CHIVPIETY_UNLOCKED(3909), - - /** - * Locked Prayers - * 0 = Locked - * 1 = Unlocked - */ - - RIGOUR_UNLOCKED(5451), - AUGURY_UNLOCKED(5452), - PRESERVE_UNLOCKED(5453), - - /** - * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating - */ - THEATRE_OF_BLOOD(6440), - BLOAT_DOOR(6447), - - /** - * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb - */ - THEATRE_OF_BLOOD_ORB_1(6442), - THEATRE_OF_BLOOD_ORB_2(6443), - THEATRE_OF_BLOOD_ORB_3(6444), - THEATRE_OF_BLOOD_ORB_4(6445), - THEATRE_OF_BLOOD_ORB_5(6446), - - /** - * Nightmare Zone - */ - NMZ_ABSORPTION(3956), - NMZ_POINTS(3949), - NMZ_OVERLOAD(3955), - - /** - * Pyramid plunder - */ - PYRAMID_PLUNDER_SARCO_OPEN(2362), - PYRAMID_PLUNDER_CHEST_OPEN(2363), - PYRAMID_PLUNDER_ROOM_LOCATION(2365), - PYRAMID_PLUNDER_TIMER(2375), - PYRAMID_PLUNDER_THIEVING_LEVEL(2376), - PYRAMID_PLUNDER_ROOM(2377), - - /** - * In the Wilderness - */ - IN_THE_WILDERNESS(5963), - - /** - * Kingdom Management - */ - KINGDOM_FAVOR(72), - KINGDOM_COFFER(74), - KINGDOM_WORKERS_WOOD(81), - KINGDOM_WORKERS_HERBS(82), - KINGDOM_WORKERS_FISHING(83), - KINGDOM_WORKERS_MINING(84), - KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked - KINGDOM_WORKERS_HARDWOOD(2131), - KINGDOM_WORKERS_FARM(2132), - KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both - KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax - - /** - * Varbit used for Slayer reward points - */ - SLAYER_REWARD_POINTS(4068), - - /** - * 0 = standard - * 1 = ancients - * 2 = lunars - * 3 = arrceus - **/ - SPELLBOOK(4070), - - /** - * Bank settings/flags - **/ - BANK_NOTE_FLAG(3958), - - - /** - * Spells being auto-casted - */ - AUTO_CAST_SPELL(276), - - /** - * Temple Trekking - */ - TREK_POINTS(1955), - TREK_STARTED(1956), - TREK_EVENT(1958), - TREK_STATUS(6719), - BLOAT_ENTERED_ROOM(6447), - - /** - * f2p Quest varbits, these don't hold the completion value. - */ - QUEST_DEMON_SLAYER(2561), - QUEST_GOBLIN_DIPLOMACY(2378), - QUEST_MISTHALIN_MYSTERY(3468), - QUEST_THE_CORSAIR_CURSE(6071), - QUEST_X_MARKS_THE_SPOT(8063), - QUEST_ERNEST_LEVER_A(1788), - QUEST_ERNEST_LEVER_B(1789), - QUEST_ERNEST_LEVER_C(1790), - QUEST_ERNEST_LEVER_D(1791), - QUEST_ERNEST_LEVER_E(1792), - QUEST_ERNEST_LEVER_F(1793), - - /** - * member Quest varbits, these don't hold the completion value. - */ - QUEST_ANIMAL_MAGNETISM(3185), - QUEST_BETWEEN_A_ROCK(299), - QUEST_CONTACT(3274), - QUEST_ZOGRE_FLESH_EATERS(487), - QUEST_DARKNESS_OF_HALLOWVALE(2573), - QUEST_DEATH_TO_THE_DORGESHUUN(2258), - QUEST_DESERT_TREASURE(358), - QUEST_DEVIOUS_MINDS(1465), - QUEST_EAGLES_PEAK(2780), - QUEST_ELEMENTAL_WORKSHOP_II(2639), - QUEST_ENAKHRAS_LAMENT(1560), - QUEST_ENLIGHTENED_JOURNEY(2866), - QUEST_THE_EYES_OF_GLOUPHRIE(2497), - QUEST_FAIRYTALE_I_GROWING_PAINS(1803), - QUEST_FAIRYTALE_II_CURE_A_QUEEN(2326), - QUEST_THE_FEUD(334), // 14 = able to pickpocket - QUEST_FORGETTABLE_TALE(822), - QUEST_GARDEN_OF_TRANQUILLITY(961), - QUEST_GHOSTS_AHOY(217), - QUEST_THE_GIANT_DWARF(571), - QUEST_THE_GOLEM(346), - QUEST_HORROR_FROM_THE_DEEP(34), - QUEST_ICTHLARINS_LITTLE_HELPER(418), - QUEST_IN_AID_OF_THE_MYREQUE(1990), - QUEST_THE_LOST_TRIBE(532), - QUEST_LUNAR_DIPLOMACY(2448), - QUEST_MAKING_HISTORY(1383), - QUEST_MOUNTAIN_DAUGHTER(260), - QUEST_MOURNINGS_END_PART_II(1103), - QUEST_MY_ARMS_BIG_ADVENTURE(2790), - QUEST_RATCATCHERS(1404), - QUEST_RECIPE_FOR_DISASTER(1850), - QUEST_RECRUITMENT_DRIVE(657), - QUEST_ROYAL_TROUBLE(2140), - QUEST_THE_SLUG_MENACE(2610), - QUEST_SHADOW_OF_THE_STORM(1372), - QUEST_A_SOULS_BANE(2011), - QUEST_SPIRITS_OF_THE_ELID(1444), - QUEST_SWAN_SONG(2098), - QUEST_A_TAIL_OF_TWO_CATS(1028), - QUEST_TEARS_OF_GUTHIX(451), - QUEST_WANTED(1051), - QUEST_COLD_WAR(3293), - QUEST_THE_FREMENNIK_ISLES(3311), - QUEST_TOWER_OF_LIFE(3337), - QUEST_WHAT_LIES_BELOW(3523), - QUEST_OLAFS_QUEST(3534), - QUEST_ANOTHER_SLICE_OF_HAM(3550), - QUEST_DREAM_MENTOR(3618), - QUEST_GRIM_TALES(2783), - QUEST_KINGS_RANSOM(3888), - QUEST_MONKEY_MADNESS_II(5027), - QUEST_CLIENT_OF_KOUREND(5619), - QUEST_BONE_VOYAGE(5795), - QUEST_THE_QUEEN_OF_THIEVES(6037), - QUEST_THE_DEPTHS_OF_DESPAIR(6027), - QUEST_DRAGON_SLAYER_II(6104), - QUEST_TALE_OF_THE_RIGHTEOUS(6358), - QUEST_A_TASTE_OF_HOPE(6396), - QUEST_MAKING_FRIENDS_WITH_MY_ARM(6528), - QUEST_THE_ASCENT_OF_ARCEUUS(7856), - QUEST_THE_FORSAKEN_TOWER(7796), - //TODO - QUEST_SONG_OF_THE_ELVES(7796), - - /** - * mini-quest varbits, these don't hold the completion value. - */ - QUEST_ARCHITECTURAL_ALLIANCE(4982), - QUEST_BEAR_YOUR_SOUL(5078), - QUEST_CURSE_OF_THE_EMPTY_LORD(821), - QUEST_ENCHANTED_KEY(1391), - QUEST_THE_GENERALS_SHADOW(3330), - QUEST_SKIPPY_AND_THE_MOGRES(1344), - QUEST_LAIR_OF_TARN_RAZORLOR(3290), - QUEST_FAMILY_PEST(5347), - QUEST_THE_MAGE_ARENA_II(6067), - //TODO - QUEST_IN_SEARCH_OF_KNOWLEDGE(6067), - - /** - * Spellbook filtering (1 = unfiltered, 0 = filtered) - */ - FILTER_SPELLBOOK(6718), - - /** - * POH Building mode (1 = yes, 0 = no) - */ - BUILDING_MODE(2176), - - /** - * 1 if in game, 0 if not - */ - LMS_IN_GAME(5314), - - /** - * Amount of pvp kills in current game - */ - LMS_KILLS(5315), - - /** - * The x coordinate of the final safespace (world coord) - */ - LMS_SAFE_X(5316), - - LMS_POISON_PROGRESS(5317), - - /** - * The y coordinate of the final safespace (world coord) - */ - LMS_SAFE_Y(5320), - - /** - * 1 is true, 0 is false. - */ - GAUNTLET_FINAL_ROOM_ENTERED(9177), - - /** - * 1 is true, 0 is false. - */ - GAUNTLET_ENTERED(9178), - - WITHDRAW_X_AMOUNT(3960), - - IN_PVP_AREA(8121), - - /** - * Value of hotkey varbits can be 0-13 - * 0 corresponds to no hotkey set - * 1-12 correspond to F1-F12 respectively - * 13 corresponds to escape - */ - COMBAT_TAB_HOTKEY(4675), - STATS_TAB_HOTKEY(4676), - QUESTS_TAB_HOTKEY(4677), - INVENTORY_TAB_HOTKEY(4678), - EQUIPMENT_TAB_HOTKEY(4679), - PRAYER_TAB_HOTKEY(4680), - SPELLBOOK_TAB_HOTKEY(4682), - FRIENDS_TAB_HOTKEY(4684), - ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), - LOGOUT_TAB_HOTKEY(4689), - OPTIONS_TAB_HOTKEY(4686), - EMOTES_TAB_HOTKEY(4687), - CLAN_TAB_HOTKEY(4683), - MUSIC_TAB_HOTKEY(4688), - - /** - * Chat Notifications settings - *
- * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false - * LOOT_DROP_NOTIFICATIONS_VALUE: gp value - * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false - * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered - * DROP_ITEM_WARNINGS: 1 is true, 0 is false - * DROP_ITEM_WARNINGS_VALUE: gp value - */ - LOOT_DROP_NOTIFICATIONS(5399), - LOOT_DROP_NOTIFICATIONS_VALUE(5400), - UNTRADEABLE_LOOT_NOTIFICATIONS(5402), - BOSS_KILL_COUNT_UPDATES(4930), - DROP_ITEM_WARNINGS(5411), - DROP_ITEM_WARNINGS_VALUE(5412), - - PARASITE(10151), - ; - - /** - * The raw varbit ID. - */ - private final int id; -} diff --git a/runelite-api/src/main/java/com/openosrs/api/widgets/WidgetID.java b/runelite-api/src/main/java/com/openosrs/api/widgets/WidgetID.java deleted file mode 100644 index 6b5b3797e2..0000000000 --- a/runelite-api/src/main/java/com/openosrs/api/widgets/WidgetID.java +++ /dev/null @@ -1,673 +0,0 @@ -package com.openosrs.api.widgets; - -public class WidgetID extends net.runelite.api.widgets.WidgetID -{ - public static final int BANK_PIN_GROUP_ID = 213; - public static final int PLAYER_TRADE_CONFIRM_GROUP_ID = 334; - public static final int PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID = 243; - public static final int DIALOG_MINIGAME_GROUP_ID = 229; - public static final int BA_HORN_OF_GLORY = 484; - public static final int MOTHERLODE_MINE_FULL_INVENTORY_GROUP_ID = 229; - public static final int DIALOG_PLAYER_GROUP_ID = 217; - public static final int DIALOG_NOTIFICATION_GROUP_ID = 229; - public static final int FOSSIL_ISLAND_MUSHROOM_TELE_GROUP_ID = 608; - public static final int PERFORMERS_FOR_THE_THEATRE_GROUPS_GROUP_ID = 364; - public static final int PERFORMERS_FOR_THE_THEATRE_PLAYERS_GROUP_ID = 50; - public static final int DIALOG_SPRITE2_ID = 11; - public static final int EQUIPMENT_PAGE_GROUP_ID = 84; - public static final int QUESTTAB_GROUP_ID = 629; - public static final int MUSICTAB_GROUP_ID = 239; - public static final int JEWELLERY_BOX_GROUP_ID = 590; - public static final int OPTIONS_GROUP_ID = 261; - public static final int MULTISKILL_MENU_GROUP_ID = 270; - public static final int THEATRE_OF_BLOOD_PARTY_GROUP_ID = 28; - public static final int GWD_KC_GROUP_ID = 406; - public static final int GAUNTLET_MAP_GROUP_ID = 638; - - public static final int SETTINGS_SIDE_GROUP_ID = 116; - public static final int SETTINGS_GROUP_ID = 134; - - static class SettingsSide - { - static final int CAMERA_ZOOM_SLIDER_TRACK = 59; - static final int MUSIC_SLIDER = 13; - static final int SOUND_EFFECT_SLIDER = 17; - static final int AREA_SOUND_SLIDER = 21; - } - - static class Settings - { - static final int INIT = 1; - } - - static class DialogPlayer - { - static final int HEAD_MODEL = 1; - static final int NAME = 2; - static final int CONTINUE = 3; - static final int TEXT = 4; - } - - static class DialogNotification - { - static final int TEXT = 0; - static final int CONTINUE = 1; - } - - static class DialogOption - { - static final int TEXT = 0; - static final int OPTION1 = 1; - static final int OPTION2 = 2; - static final int OPTION3 = 3; - static final int OPTION4 = 4; - static final int OPTION5 = 5; - } - - static class PestControlBoat - { - static final int INFO = 3; - - static final int NEXT_DEPARTURE = 4; - static final int PLAYERS_READY = 5; - static final int POINTS = 6; - } - - static class PestControlExchangeWindow - { - static final int ITEM_LIST = 2; - static final int BOTTOM = 5; - static final int POINTS = 8; - static final int CONFIRM_BUTTON = 6; - } - - static class MinigameDialog - { - static final int TEXT = 1; - static final int CONTINUE = 2; - } - - static class PestControl - { - static final int INFO = 3; - - static final int TIME = 6; - - static final int ACTIVITY_BAR = 12; - static final int ACTIVITY_PROGRESS = 14; - - static final int PURPLE_SHIELD = 15; - static final int BLUE_SHIELD = 16; - static final int YELLOW_SHIELD = 17; - static final int RED_SHIELD = 18; - - static final int PURPLE_ICON = 19; - static final int BLUE_ICON = 20; - static final int YELLOW_ICON = 21; - static final int RED_ICON = 22; - - static final int PURPLE_HEALTH = 23; - static final int BLUE_HEALTH = 24; - static final int YELLOW_HEALTH = 25; - static final int RED_HEALTH = 26; - } - - static class Bank - { - static final int BANK_CONTAINER = 1; - static final int INVENTORY_ITEM_CONTAINER = 3; - static final int BANK_TITLE_BAR = 3; - static final int TUTORIAL_BUTTON = 4; - static final int ITEM_COUNT_TOP = 5; - static final int ITEM_COUNT_BAR = 6; - static final int ITEM_COUNT_BOTTOM = 7; - static final int CONTENT_CONTAINER = 9; - static final int TAB_CONTAINER = 10; - static final int ITEM_CONTAINER = 12; - static final int SCROLLBAR = 13; - static final int UNNOTED_BUTTON = 21; - static final int NOTED_BUTTON = 23; - static final int SEARCH_BUTTON_BACKGROUND = 39; - static final int DEPOSIT_INVENTORY = 41; - static final int DEPOSIT_EQUIPMENT = 43; - static final int INCINERATOR = 45; - static final int INCINERATOR_CONFIRM = 46; - static final int EQUIPMENT_CONTENT_CONTAINER = 68; - static final int SETTINGS_BUTTON = 111; - static final int EQUIPMENT_BUTTON = 112; - } - - static class GrandExchange - { - static final int WINDOW_CONTAINER = 0; - static final int WINDOW_BORDERS = 2; - static final int HISTORY_BUTTON = 3; - static final int BACK_BUTTON = 4; - static final int OFFER1 = 7; - static final int OFFER2 = 8; - static final int OFFER3 = 9; - static final int OFFER4 = 10; - static final int OFFER5 = 11; - static final int OFFER6 = 12; - static final int OFFER7 = 13; - static final int OFFER8 = 14; - static final int OFFER_CONTAINER = 24; - static final int OFFER_DESCRIPTION = 25; - static final int OFFER_PRICE = 26; - static final int OFFER_CONFIRM_BUTTON = 27; - } - - static class Shop - { - static final int ITEMS_CONTAINER = 2; - static final int INVENTORY_ITEM_CONTAINER = 0; - } - - static class Smithing - { - static final int INVENTORY_ITEM_CONTAINER = 0; - - static final int QTY_1 = 3; - static final int QTY_5 = 4; - static final int QTY_10 = 5; - static final int QTY_X = 6; - static final int QTY_ALL = 7; - - static final int DAGGER = 9; - static final int SWORD = 10; - static final int SCIMITAR = 11; - static final int LONG_SWORD = 12; - static final int TWO_H_SWORD = 13; - static final int AXE = 14; - static final int MACE = 15; - static final int WARHAMMER = 16; - static final int BATTLE_AXE = 17; - static final int CLAWS = 18; - static final int CHAIN_BODY = 19; - static final int PLATE_LEGS = 20; - static final int PLATE_SKIRT = 21; - static final int PLATE_BODY = 22; - static final int NAILS = 23; - static final int MED_HELM = 24; - static final int FULL_HELM = 25; - static final int SQ_SHIELD = 26; - static final int KITE_SHIELD = 27; - static final int EXCLUSIVE1 = 28; - static final int DART_TIPS = 29; - static final int ARROW_HEADS = 30; - static final int KNIVES = 31; - static final int EXCLUSIVE2 = 32; - static final int JAVELIN_HEADS = 33; - static final int BOLTS = 34; - static final int LIMBS = 35; - } - - static class Equipment - { - static final int HELMET = 14; - static final int CAPE = 15; - static final int AMULET = 16; - static final int WEAPON = 17; - static final int BODY = 18; - static final int SHIELD = 19; - static final int LEGS = 20; - static final int GLOVES = 21; - static final int BOOTS = 22; - static final int RING = 23; - static final int AMMO = 24; - static final int INVENTORY_ITEM_CONTAINER = 0; - } - - static class Minimap - { - static final int XP_ORB = 1; - static final int HEALTH_ORB = 2; - static final int PRAYER_ORB = 12; - static final int QUICK_PRAYER_ORB = 14; // Has the "Quick-prayers" name - static final int PRAYER_ORB_TEXT = 15; - static final int RUN_ORB = 20; - 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 SPEC_CLICKBOX = 30; - static final int WORLDMAP_ORB = 41; - static final int WIKI_BANNER = 43; - } - - static class Combat - { - static final int WEAPON_NAME = 1; - static final int LEVEL = 3; - static final int STYLE_ONE = 4; - static final int STYLE_TWO = 8; - static final int STYLE_THREE = 12; - static final int STYLE_FOUR = 16; - static final int SPELLS = 20; - static final int DEFENSIVE_SPELL_BOX = 21; - static final int DEFENSIVE_SPELL_ICON = 23; - static final int DEFENSIVE_SPELL_SHIELD = 24; - static final int DEFENSIVE_SPELL_TEXT = 25; - static final int SPELL_BOX = 26; - static final int SPELL_ICON = 28; - static final int SPELL_TEXT = 29; - static final int AUTO_RETALIATE = 30; - static final int SPECIAL_ATTACK_BAR = 34; - static final int SPECIAL_ATTACK_CLICKBOX = 36; - static final int TOOLTIP = 41; - } - - static class BarbarianAssault - { - static class ATK - { - static final int LISTEN_TOP = 7; - static final int LISTEN_BOTTOM = 8; - static final int TO_CALL_WIDGET = 9; - static final int TO_CALL = 10; - static final int ROLE_SPRITE = 11; - static final int ROLE = 12; - } - static class HLR - { - static final int TEAMMATE1 = 18; - static final int TEAMMATE2 = 22; - static final int TEAMMATE3 = 26; - static final int TEAMMATE4 = 30; - } - static class HORN_GLORY - { - static final int ATTACKER = 5; - static final int DEFENDER = 6; - static final int COLLECTOR = 7; - static final int HEALER = 8; - } - static class REWARD_VALUES - { - static final int RUNNERS_PASSED = 14; - static final int HITPOINTS_REPLENISHED = 19; - static final int WRONG_POISON_PACKS_USED = 20; - static final int EGGS_COLLECTED = 21; - static final int FAILED_ATTACKER_ATTACKS = 22; - static final int RUNNERS_PASSED_POINTS = 24; - static final int RANGERS_KILLED = 25; - static final int FIGHTERS_KILLED = 26; - static final int HEALERS_KILLED = 27; - static final int RUNNERS_KILLED = 28; - static final int HITPOINTS_REPLENISHED_POINTS = 29; - static final int WRONG_POISON_PACKS_USED_POINTS = 30; - static final int EGGS_COLLECTED_POINTS = 31; - static final int FAILED_ATTACKER_ATTACKS_POINTS = 32; - static final int BASE_POINTS = 33; - static final int HONOUR_POINTS_REWARD = 49; - } - static final int CORRECT_STYLE = 3; - static final int GAME_WIDGET = 3; - static final int CURRENT_WAVE_WIDGET = 4; - static final int CURRENT_WAVE = 5; - static final int LISTEN_WIDGET = 6; - static final int LISTEN = 7; - static final int TO_CALL_WIDGET = 8; - static final int TO_CALL = 9; - static final int ROLE_SPRITE = 10; - static final int ROLE = 11; - static final int REWARD_TEXT = 57; - } - - static class LevelUp - { - static final int SKILL = 1; - static final int LEVEL = 2; - static final int CONTINUE = 3; - } - - static class TheatreOfBlood - { - static final int RAIDING_PARTY = 9; - static final int ORB_BOX = 10; - static final int BOSS_HEALTH_BAR = 35; - } - - static class TheatreOfBloodParty - { - static final int CONTAINER = 10; - } - - static class LightBox - { - static final int LIGHT_BOX = 1; - static final int LIGHT_BOX_WINDOW = 2; - static final int LIGHT_BULB_CONTAINER = 3; - static final int LIGHT_BOX_BUTTON_CONTAINER = 6; - static final int BUTTON_A = 8; - static final int BUTTON_B = 9; - static final int BUTTON_C = 10; - static final int BUTTON_D = 11; - static final int BUTTON_E = 12; - static final int BUTTON_F = 13; - static final int BUTTON_G = 14; - static final int BUTTON_H = 15; - } - - static class EquipmentWidgetIdentifiers - { - static final int EQUIP_YOUR_CHARACTER = 3; - static final int STAB_ATTACK_BONUS = 24; - static final int SLASH_ATTACK_BONUS = 25; - static final int CRUSH_ATTACK_BONUS = 26; - static final int MAGIC_ATTACK_BONUS = 27; - static final int RANGED_ATTACK_BONUS = 28; - static final int STAB_DEFENCE_BONUS = 30; - static final int SLASH_DEFENCE_BONUS = 31; - static final int CRUSH_DEFENCE_BONUS = 32; - static final int MAGIC_DEFENCE_BONUS = 33; - static final int RANGED_DEFENCE_BONUS = 34; - static final int MELEE_STRENGTH = 36; - static final int RANGED_STRENGTH = 37; - static final int MAGIC_DAMAGE = 38; - static final int PRAYER_BONUS = 39; - static final int UNDEAD_DAMAGE_BONUS = 41; - static final int SLAYER_DAMAGE_BONUS = 42; - static final int WEIGHT = 49; - } - - static class WorldSwitcher - { - static final int CONTAINER = 1; - static final int WORLD_LIST = 16; - static final int LOGOUT_BUTTON = 23; - } - - static class FossilMushroomTeleport - { - static final int ROOT = 2; - static final int HOUSE_ON_HILL = 4; - static final int VERDANT_VALLEY = 8; - static final int SWAMP = 12; - static final int MUSHROOM_MEADOW = 16; - } - - static class SpellBook - { - static final int FILTERED_SPELLS_BOUNDS = 3; - static final int TOOLTIP = 189; - - // NORMAL SPELLS - static final int LUMBRIDGE_HOME_TELEPORT = 5; - static final int WIND_STRIKE = 6; - static final int CONFUSE = 7; - static final int ENCHANT_CROSSBOW_BOLT = 8; - static final int WATER_STRIKE = 9; - static final int LVL_1_ENCHANT = 10; - static final int EARTH_STRIKE = 11; - static final int WEAKEN = 12; - static final int FIRE_STRIKE = 13; - static final int BONES_TO_BANANAS = 14; - static final int WIND_BOLT = 15; - static final int CURSE = 16; - static final int BIND = 17; - static final int LOW_LEVEL_ALCHEMY = 18; - static final int WATER_BOLT = 19; - static final int VARROCK_TELEPORT = 20; - static final int LVL_2_ENCHANT = 21; - static final int EARTH_BOLT = 22; - static final int LUMBRIDGE_TELEPORT = 23; - static final int TELEKINETIC_GRAB = 24; - static final int FIRE_BOLT = 25; - static final int FALADOR_TELEPORT = 26; - static final int CRUMBLE_UNDEAD = 27; - static final int TELEPORT_TO_HOUSE = 28; - static final int WIND_BLAST = 29; - static final int SUPERHEAT_ITEM = 30; - static final int CAMELOT_TELEPORT = 31; - static final int WATER_BLAST = 32; - static final int LVL_3_ENCHANT = 33; - static final int IBAN_BLAST = 34; - static final int SNARE = 35; - static final int MAGIC_DART = 36; - static final int ARDOUGNE_TELEPORT = 37; - static final int EARTH_BLAST = 38; - static final int HIGH_LEVEL_ALCHEMY = 39; - static final int CHARGE_WATER_ORB = 40; - static final int LVL_4_ENCHANT = 41; - static final int WATCHTOWER_TELEPORT = 42; - static final int FIRE_BLAST = 43; - static final int CHARGE_EARTH_ORB = 44; - static final int BONES_TO_PEACHES = 45; - static final int SARADOMIN_STRIKE = 46; - static final int CLAWS_OF_GUTHIX = 47; - static final int FLAMES_OF_ZAMORAK = 48; - static final int TROLLHEIM_TELEPORT = 49; - static final int WIND_WAVE = 50; - static final int CHARGE_FIRE_ORB = 51; - static final int TELEPORT_TO_APE_ATOLL = 52; - static final int WATER_WAVE = 53; - static final int CHARGE_AIR_ORB = 54; - static final int VULNERABILITY = 55; - static final int LVL_5_ENCHANT = 56; - static final int TELEPORT_TO_KOUREND = 57; - static final int EARTH_WAVE = 58; - static final int ENFEEBLE = 59; - static final int TELEOTHER_LUMBRIDGE = 60; - static final int FIRE_WAVE = 61; - static final int ENTANGLE = 62; - static final int STUN = 63; - static final int CHARGE = 64; - static final int WIND_SURGE = 65; - static final int TELEOTHER_FALADOR = 66; - static final int WATER_SURGE = 67; - static final int TELE_BLOCK = 68; - static final int BOUNTY_TARGET_TELEPORT = 69; - static final int LVL_6_ENCHANT = 70; - static final int TELEOTHER_CAMELOT = 71; - static final int EARTH_SURGE = 72; - static final int LVL_7_ENCHANT = 73; - static final int FIRE_SURGE = 74; - - // ANCIENT SPELLS - static final int ICE_RUSH = 75; - static final int ICE_BLITZ = 76; - static final int ICE_BURST = 77; - static final int ICE_BARRAGE = 78; - static final int BLOOD_RUSH = 79; - static final int BLOOD_BLITZ = 80; - static final int BLOOD_BURST = 81; - static final int BLOOD_BARRAGE = 82; - static final int SMOKE_RUSH = 83; - static final int SMOKE_BLITZ = 84; - static final int SMOKE_BURST = 85; - static final int SMOKE_BARRAGE = 86; - static final int SHADOW_RUSH = 87; - static final int SHADOW_BLITZ = 88; - static final int SHADOW_BURST = 89; - static final int SHADOW_BARRAGE = 90; - static final int PADDEWWA_TELEPORT = 91; - static final int SENNTISTEN_TELEPORT = 92; - static final int KHARYRLL_TELEPORT = 93; - static final int LASSAR_TELEPORT = 94; - static final int DAREEYAK_TELEPORT = 95; - static final int CARRALLANGER_TELEPORT = 96; - static final int ANNAKARL_TELEPORT = 97; - static final int GHORROCK_TELEPORT = 98; - static final int EDGEVILLE_HOME_TELEPORT = 99; - - // LUNAR SPELLS - static final int LUNAR_HOME_TELEPORT = 100; - static final int BAKE_PIE = 101; - static final int CURE_PLANT = 102; - static final int MONSTER_EXAMINE = 103; - static final int NPC_CONTACT = 104; - static final int CURE_OTHER = 105; - static final int HUMIDIFY = 106; - static final int MOONCLAN_TELEPORT = 107; - static final int TELE_GROUP_MOONCLAN = 108; - static final int CURE_ME = 109; - static final int HUNTER_KIT = 110; - static final int WATERBIRTH_TELEPORT = 111; - static final int TELE_GROUP_WATERBIRTH = 112; - static final int CURE_GROUP = 113; - static final int STAT_SPY = 114; - static final int BARBARIAN_TELEPORT = 115; - static final int TELE_GROUP_BARBARIAN = 116; - static final int SUPERGLASS_MAKE = 117; - static final int TAN_LEATHER = 118; - static final int KHAZARD_TELEPORT = 119; - static final int TELE_GROUP_KHAZARD = 120; - static final int DREAM = 121; - static final int STRING_JEWELLERY = 122; - static final int STAT_RESTORE_POT_SHARE = 123; - static final int MAGIC_IMBUE = 124; - static final int FERTILE_SOIL = 125; - static final int BOOST_POTION_SHARE = 126; - static final int FISHING_GUILD_TELEPORT = 127; - static final int TELE_GROUP_FISHING_GUILD = 128; - static final int PLANK_MAKE = 129; - static final int CATHERBY_TELEPORT = 130; - static final int TELE_GROUP_CATHERBY = 131; - static final int RECHARGE_DRAGONSTONE = 132; - static final int ICE_PLATEAU_TELEPORT = 133; - static final int TELE_GROUP_ICE_PLATEAU = 134; - static final int ENERGY_TRANSFER = 135; - static final int HEAL_OTHER = 136; - static final int VENGEANCE_OTHER = 137; - static final int VENGEANCE = 138; - static final int HEAL_GROUP = 139; - static final int SPELLBOOK_SWAP = 140; - static final int GEOMANCY = 141; - static final int SPIN_FLAX = 142; - static final int OURANIA_TELEPORT = 143; - - // ARCEUUS SPELLS - static final int ARCEUUS_HOME_TELEPORT = 144; - static final int BATTLEFRONT_TELEPORT = 179; - // HEADS - static final int REANIMATE_GOBLIN = 145; - static final int REANIMATE_MONKEY = 147; - static final int REANIMATE_IMP = 148; - static final int REANIMATE_MINOTAUR = 149; - static final int REANIMATE_SCORPION = 151; - static final int REANIMATE_BEAR = 152; - static final int REANIMATE_UNICORN = 153; - static final int REANIMATE_DOG = 154; - static final int REANIMATE_CHAOS_DRUID = 156; - static final int REANIMATE_GIANT = 158; - static final int REANIMATE_OGRE = 160; - static final int REANIMATE_ELF = 161; - static final int REANIMATE_TROLL = 162; - static final int REANIMATE_HORROR = 164; - static final int REANIMATE_KALPHITE = 165; - static final int REANIMATE_DAGANNOTH = 167; - static final int REANIMATE_BLOODVELD = 168; - static final int REANIMATE_TZHAAR = 170; - static final int REANIMATE_DEMON = 172; - static final int REANIMATE_AVIANSIE = 173; - static final int REANIMATE_ABYSSAL = 176; - static final int REANIMATE_DRAGON = 178; - - } - - static class Pvp - { - static final int FOG_OVERLAY = 1; - static final int PVP_WIDGET_CONTAINER = 54; // OUTDATED? - static final int SKULL = 56; // OUTDATED? - static final int ATTACK_RANGE = 59; // OUTDATED? - static final int BOUNTY_HUNTER_INFO = 6; - static final int KILLDEATH_RATIO = 28; - static final int SKULL_CONTAINER = 48; - static final int SAFE_ZONE = 50; - static final int WILDERNESS_LEVEL = 53; // this can also be the Deadman Mode "Protection" text - } - - static class DialogSprite2 - { - static final int SPRITE1 = 1; - static final int TEXT = 2; - static final int SPRITE2 = 3; - static final int CONTINUE = 4; - } - - static class QuestTab - { - static final int QUEST_TAB = 3; - } - - public static class TradeScreen - { - public static final int FIRST_TRADING_WITH = 31; - public static final int SECOND_ACCEPT_FUNC = 13; - public static final int SECOND_DECLINE_FUNC = 14; - public static final int SECOND_MY_OFFER = 23; - public static final int SECOND_THEIR_OFFER = 24; - public static final int SECOND_ACCEPT_TEXT = 25; - public static final int SECOND_DECLINE_TEXT = 26; - public static final int SECOND_MY_ITEMS = 28; - public static final int SECOND_THEIR_ITEMS = 29; - public static final int SECOND_TRADING_WITH = 30; - } - - public static class DuelConfig - { - public static final int CONFIG_GROUP_IP = 482; - public static final int TITLE = 35; - public static final int OPPONENT_ATT = 9; - public static final int OPPONENT_STR = 13; - public static final int OPPONENT_DEF = 17; - public static final int OPPONENT_HP = 21; - } - - public static class DuelResult - { - public static final int RESULT_GROUP_ID = 372; - public static final int TITLE = 16; - public static final int TOTAL_STAKED = 32; - public static final int TOTAL_TAX = 39; - public static final int WINNINGS = 40; - } - - // Also used for many other interfaces! - static class BankPin - { - static final int CONTAINER = 0; - static final int TOP_LEFT_TEXT = 2; - static final int FIRST_ENTERED = 3; - static final int SECOND_ENTERED = 4; - static final int THIRD_ENTERED = 5; - static final int FOURTH_ENTERED = 6; - static final int INSTRUCTION_TEXT = 10; - static final int EXIT_BUTTON = 13; - static final int FORGOT_BUTTON = 15; - static final int BUTTON_1 = 16; - static final int BUTTON_2 = 18; - static final int BUTTON_3 = 20; - static final int BUTTON_4 = 22; - static final int BUTTON_5 = 24; - static final int BUTTON_6 = 26; - static final int BUTTON_7 = 28; - static final int BUTTON_8 = 30; - static final int BUTTON_9 = 32; - static final int BUTTON_10 = 34; - } - - static class JewelBox - { - static final int DUEL_RING = 2; - static final int GAME_NECK = 3; - static final int COMB_BRAC = 4; - static final int SKIL_NECK = 5; - static final int RING_OFGP = 6; - static final int AMUL_GLOR = 7; // yes - } - - static class Options - { - static final int CAMERA_ZOOM_SLIDER_HANDLE = 15; - static final int MUSIC_SLIDER = 37; - static final int SOUND_EFFECT_SLIDER = 43; - static final int AREA_SOUND_SLIDER = 49; - } - - static class GauntletMap - { - static final int CONTAINER = 4; - } -} diff --git a/runelite-api/src/main/java/com/openosrs/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/com/openosrs/api/widgets/WidgetInfo.java deleted file mode 100644 index 57e164273e..0000000000 --- a/runelite-api/src/main/java/com/openosrs/api/widgets/WidgetInfo.java +++ /dev/null @@ -1,488 +0,0 @@ -package com.openosrs.api.widgets; - - -public enum WidgetInfo -{ - WORLD_MAP_BUTTON_BORDER(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.WORLDMAP_ORB), - - EQUIPMENT_HELMET(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.HELMET), - EQUIPMENT_CAPE(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.CAPE), - EQUIPMENT_AMULET(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.AMULET), - EQUIPMENT_WEAPON(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.WEAPON), - EQUIPMENT_BODY(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.BODY), - EQUIPMENT_SHIELD(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.SHIELD), - EQUIPMENT_LEGS(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.LEGS), - EQUIPMENT_GLOVES(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.GLOVES), - EQUIPMENT_BOOTS(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.BOOTS), - EQUIPMENT_RING(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.RING), - EQUIPMENT_AMMO(WidgetID.EQUIPMENT_GROUP_ID, WidgetID.Equipment.AMMO), - - MINIGAME_DIALOG(WidgetID.DIALOG_MINIGAME_GROUP_ID, 0), - MINIGAME_DIALOG_TEXT(WidgetID.DIALOG_MINIGAME_GROUP_ID, WidgetID.MinigameDialog.TEXT), - MINIGAME_DIALOG_CONTINUE(WidgetID.DIALOG_MINIGAME_GROUP_ID, WidgetID.MinigameDialog.CONTINUE), - PEST_CONTROL_EXCHANGE_WINDOW(WidgetID.PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID, 0), - PEST_CONTROL_EXCHANGE_WINDOW_POINTS(WidgetID.PEST_CONTROL_EXCHANGE_WINDOW_GROUP_ID, WidgetID.PestControlExchangeWindow.POINTS), - - PEST_CONTROL_BOAT_INFO_POINTS(WidgetID.PEST_CONTROL_BOAT_GROUP_ID, WidgetID.PestControlBoat.POINTS), - PEST_CONTROL_INFO_TIME(WidgetID.PEST_CONTROL_GROUP_ID, WidgetID.PestControl.TIME), - - BANK_UNNOTED_BUTTON(WidgetID.BANK_GROUP_ID, WidgetID.Bank.UNNOTED_BUTTON), - BANK_NOTED_BUTTON(WidgetID.BANK_GROUP_ID, WidgetID.Bank.NOTED_BUTTON), - - GRAND_EXCHANGE_HISTORY_BUTTON(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.HISTORY_BUTTON), - GRAND_EXCHANGE_BACK_BUTTON(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.BACK_BUTTON), - GRAND_EXCHANGE_OFFER1(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER1), - GRAND_EXCHANGE_OFFER2(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER2), - GRAND_EXCHANGE_OFFER3(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER3), - GRAND_EXCHANGE_OFFER4(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER4), - GRAND_EXCHANGE_OFFER5(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER5), - GRAND_EXCHANGE_OFFER6(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER6), - GRAND_EXCHANGE_OFFER7(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER7), - GRAND_EXCHANGE_OFFER8(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER8), - - GRAND_EXCHANGE_OFFER_CONFIRM_BUTTON(WidgetID.GRAND_EXCHANGE_GROUP_ID, WidgetID.GrandExchange.OFFER_CONFIRM_BUTTON), - - SMITHING_ANVIL_DAGGER(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.DAGGER), - SMITHING_ANVIL_SWORD(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.SWORD), - SMITHING_ANVIL_SCIMITAR(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.SCIMITAR), - SMITHING_ANVIL_LONG_SWORD(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.LONG_SWORD), - SMITHING_ANVIL_TWO_H_SWORD(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.TWO_H_SWORD), - SMITHING_ANVIL_AXE(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.AXE), - SMITHING_ANVIL_MACE(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.MACE), - SMITHING_ANVIL_WARHAMMER(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.WARHAMMER), - SMITHING_ANVIL_BATTLE_AXE(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.BATTLE_AXE), - SMITHING_ANVIL_CLAWS(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.CLAWS), - SMITHING_ANVIL_CHAIN_BODY(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.CHAIN_BODY), - SMITHING_ANVIL_PLATE_LEGS(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.PLATE_LEGS), - SMITHING_ANVIL_PLATE_SKIRT(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.PLATE_SKIRT), - SMITHING_ANVIL_PLATE_BODY(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.PLATE_BODY), - SMITHING_ANVIL_NAILS(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.NAILS), - SMITHING_ANVIL_MED_HELM(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.MED_HELM), - SMITHING_ANVIL_FULL_HELM(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.FULL_HELM), - SMITHING_ANVIL_SQ_SHIELD(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.SQ_SHIELD), - SMITHING_ANVIL_KITE_SHIELD(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.KITE_SHIELD), - SMITHING_ANVIL_DART_TIPS(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.DART_TIPS), - SMITHING_ANVIL_ARROW_HEADS(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.ARROW_HEADS), - SMITHING_ANVIL_KNIVES(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.KNIVES), - SMITHING_ANVIL_JAVELIN_HEADS(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.JAVELIN_HEADS), - SMITHING_ANVIL_BOLTS(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.BOLTS), - SMITHING_ANVIL_LIMBS(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.LIMBS), - SMITHING_ANVIL_EXCLUSIVE1(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.EXCLUSIVE1), - SMITHING_ANVIL_EXCLUSIVE2(WidgetID.SMITHING_GROUP_ID, WidgetID.Smithing.EXCLUSIVE2), - - MINIMAP_SPEC_CLICKBOX(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.SPEC_CLICKBOX), - - MINIMAP_WORLD_ORB(WidgetID.MINIMAP_GROUP_ID, WidgetID.Minimap.WORLDMAP_ORB), - - RESIZABLE_VIEWPORT_BOTTOM_LINE_MAGIC_TAB(WidgetID.RESIZABLE_VIEWPORT_BOTTOM_LINE_GROUP_ID, WidgetID.ResizableViewportBottomLine.SPELL_TAB), - - COMBAT_WEAPON(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.WEAPON_NAME), - - COMBAT_SPECIAL_ATTACK(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.SPECIAL_ATTACK_BAR), - COMBAT_SPECIAL_ATTACK_CLICKBOX(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.SPECIAL_ATTACK_CLICKBOX), - COMBAT_TOOLTIP(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.TOOLTIP), - - MULTI_SKILL_MENU(WidgetID.MULTISKILL_MENU_GROUP_ID, 0), - - DIALOG2_SPRITE(WidgetID.DIALOG_SPRITE2_ID, 0), - DIALOG2_SPRITE_SPRITE1(WidgetID.DIALOG_SPRITE2_ID, WidgetID.DialogSprite2.SPRITE1), - DIALOG2_SPRITE_SPRITE2(WidgetID.DIALOG_SPRITE2_ID, WidgetID.DialogSprite2.SPRITE2), - DIALOG2_SPRITE_TEXT(WidgetID.DIALOG_SPRITE2_ID, WidgetID.DialogSprite2.TEXT), - DIALOG2_SPRITE_CONTINUE(WidgetID.DIALOG_SPRITE2_ID, WidgetID.DialogSprite2.CONTINUE), - - DIALOG_PLAYER_NAME(WidgetID.DIALOG_PLAYER_GROUP_ID, WidgetID.DialogPlayer.NAME), - DIALOG_PLAYER_TEXT(WidgetID.DIALOG_PLAYER_GROUP_ID, WidgetID.DialogPlayer.TEXT), - DIALOG_PLAYER_HEAD_MODEL(WidgetID.DIALOG_PLAYER_GROUP_ID, WidgetID.DialogPlayer.HEAD_MODEL), - DIALOG_PLAYER_CONTINUE(WidgetID.DIALOG_PLAYER_GROUP_ID, WidgetID.DialogPlayer.CONTINUE), - - DIALOG_NOTIFICATION_TEXT(WidgetID.DIALOG_NOTIFICATION_GROUP_ID, WidgetID.DialogNotification.TEXT), - DIALOG_NOTIFICATION_CONTINUE(WidgetID.DIALOG_NOTIFICATION_GROUP_ID, WidgetID.DialogNotification.CONTINUE), - - DIALOG_OPTION_TEXT(WidgetID.DIALOG_OPTION_GROUP_ID, WidgetID.DialogOption.TEXT), - DIALOG_OPTION_OPTION1(WidgetID.DIALOG_OPTION_GROUP_ID, WidgetID.DialogOption.OPTION1), - DIALOG_OPTION_OPTION2(WidgetID.DIALOG_OPTION_GROUP_ID, WidgetID.DialogOption.OPTION2), - DIALOG_OPTION_OPTION3(WidgetID.DIALOG_OPTION_GROUP_ID, WidgetID.DialogOption.OPTION3), - DIALOG_OPTION_OPTION4(WidgetID.DIALOG_OPTION_GROUP_ID, WidgetID.DialogOption.OPTION4), - DIALOG_OPTION_OPTION5(WidgetID.DIALOG_OPTION_GROUP_ID, WidgetID.DialogOption.OPTION5), - - BA_RUNNERS_PASSED(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.RUNNERS_PASSED), - BA_HITPOINTS_REPLENISHED(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.HITPOINTS_REPLENISHED), - BA_WRONG_POISON_PACKS(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.WRONG_POISON_PACKS_USED), - BA_EGGS_COLLECTED(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.EGGS_COLLECTED), - BA_FAILED_ATTACKER_ATTACKS(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.FAILED_ATTACKER_ATTACKS), - BA_RUNNERS_PASSED_POINTS(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.RUNNERS_PASSED_POINTS), - BA_RANGERS_KILLED(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.RANGERS_KILLED), - BA_FIGHTERS_KILLED(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.FIGHTERS_KILLED), - BA_HEALERS_KILLED(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.HEALERS_KILLED), - BA_RUNNERS_KILLED(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.RUNNERS_KILLED), - BA_HITPOINTS_REPLENISHED_POINTS(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.HITPOINTS_REPLENISHED_POINTS), - BA_WRONG_POISON_PACKS_POINTS(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.WRONG_POISON_PACKS_USED_POINTS), - BA_EGGS_COLLECTED_POINTS(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.EGGS_COLLECTED_POINTS), - BA_FAILED_ATTACKER_ATTACKS_POINTS(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.FAILED_ATTACKER_ATTACKS_POINTS), - BA_HONOUR_POINTS_REWARD(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.HONOUR_POINTS_REWARD), - BA_BASE_POINTS(WidgetID.BA_REWARD_GROUP_ID, WidgetID.BarbarianAssault.REWARD_VALUES.BASE_POINTS), - - LEVEL_UP_CONTINUE(WidgetID.LEVEL_UP_GROUP_ID, WidgetID.LevelUp.CONTINUE), - - THEATRE_OF_BLOOD_PARTY(WidgetID.THEATRE_OF_BLOOD_PARTY_GROUP_ID, WidgetID.TheatreOfBloodParty.CONTAINER), - - LIGHT_BOX_BUTTON_CONTAINER(WidgetID.LIGHT_BOX_GROUP_ID, WidgetID.LightBox.LIGHT_BOX_BUTTON_CONTAINER), - - THEATRE_OF_BLOOD_HEALTH_ORBS(WidgetID.THEATRE_OF_BLOOD_GROUP_ID, WidgetID.TheatreOfBlood.ORB_BOX), - THEATRE_OF_BLOOD_BOSS_HEALTH(WidgetID.THEATRE_OF_BLOOD_GROUP_ID, WidgetID.TheatreOfBlood.BOSS_HEALTH_BAR), - THEATRE_OF_BLOOD_RAIDING_PARTY(WidgetID.THEATRE_OF_BLOOD_GROUP_ID, WidgetID.TheatreOfBlood.RAIDING_PARTY), - - WORLD_SWITCHER_CONTAINER(WidgetID.WORLD_SWITCHER_GROUP_ID, WidgetID.WorldSwitcher.CONTAINER), - - WORLD_SWITCHER_LOGOUT_BUTTON(WidgetID.WORLD_SWITCHER_GROUP_ID, WidgetID.WorldSwitcher.LOGOUT_BUTTON), - - FOSSIL_MUSHROOM_TELEPORT(WidgetID.FOSSIL_ISLAND_MUSHROOM_TELE_GROUP_ID, WidgetID.FossilMushroomTeleport.ROOT), - FOSSIL_MUSHROOM_HOUSE(WidgetID.FOSSIL_ISLAND_MUSHROOM_TELE_GROUP_ID, WidgetID.FossilMushroomTeleport.HOUSE_ON_HILL), - FOSSIL_MUSHROOM_VALLEY(WidgetID.FOSSIL_ISLAND_MUSHROOM_TELE_GROUP_ID, WidgetID.FossilMushroomTeleport.VERDANT_VALLEY), - FOSSIL_MUSHROOM_SWAMP(WidgetID.FOSSIL_ISLAND_MUSHROOM_TELE_GROUP_ID, WidgetID.FossilMushroomTeleport.SWAMP), - FOSSIL_MUSHROOM_MEADOW(WidgetID.FOSSIL_ISLAND_MUSHROOM_TELE_GROUP_ID, WidgetID.FossilMushroomTeleport.MUSHROOM_MEADOW), - - PVP_SKULL(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.SKULL), - PVP_ATTACK_RANGE(WidgetID.PVP_GROUP_ID, WidgetID.Pvp.ATTACK_RANGE), - - SPELLBOOK(WidgetID.SPELLBOOK_GROUP_ID, 0), - SPELLBOOK_FILTERED_BOUNDS(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FILTERED_SPELLS_BOUNDS), - - /* STANDARD SPELL BOOK WIDGETS*/ - SPELL_LUMBRIDGE_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LUMBRIDGE_HOME_TELEPORT), - SPELL_WIND_STRIKE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WIND_STRIKE), - SPELL_CONFUSE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CONFUSE), - SPELL_ENCHANT_CROSSBOW_BOLT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ENCHANT_CROSSBOW_BOLT), - SPELL_WATER_STRIKE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WATER_STRIKE), - SPELL_LVL_1_ENCHANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LVL_1_ENCHANT), - SPELL_EARTH_STRIKE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.EARTH_STRIKE), - SPELL_WEAKEN(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WEAKEN), - SPELL_FIRE_STRIKE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FIRE_STRIKE), - SPELL_BONES_TO_BANANAS(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BONES_TO_BANANAS), - SPELL_WIND_BOLT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WIND_BOLT), - SPELL_CURSE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CURSE), - SPELL_BIND(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BIND), - SPELL_LOW_LEVEL_ALCHEMY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LOW_LEVEL_ALCHEMY), - SPELL_WATER_BOLT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WATER_BOLT), - SPELL_VARROCK_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.VARROCK_TELEPORT), - SPELL_LVL_2_ENCHANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LVL_2_ENCHANT), - SPELL_EARTH_BOLT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.EARTH_BOLT), - SPELL_LUMBRIDGE_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LUMBRIDGE_TELEPORT), - SPELL_TELEKINETIC_GRAB(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELEKINETIC_GRAB), - SPELL_FIRE_BOLT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FIRE_BOLT), - SPELL_FALADOR_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FALADOR_TELEPORT), - SPELL_CRUMBLE_UNDEAD(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CRUMBLE_UNDEAD), - SPELL_TELEPORT_TO_HOUSE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELEPORT_TO_HOUSE), - SPELL_WIND_BLAST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WIND_BLAST), - SPELL_SUPERHEAT_ITEM(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SUPERHEAT_ITEM), - SPELL_CAMELOT_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CAMELOT_TELEPORT), - SPELL_WATER_BLAST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WATER_BLAST), - SPELL_LVL_3_ENCHANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LVL_3_ENCHANT), - SPELL_IBAN_BLAST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.IBAN_BLAST), - SPELL_SNARE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SNARE), - SPELL_MAGIC_DART(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.MAGIC_DART), - SPELL_ARDOUGNE_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ARDOUGNE_TELEPORT), - SPELL_EARTH_BLAST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.EARTH_BLAST), - SPELL_HIGH_LEVEL_ALCHEMY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.HIGH_LEVEL_ALCHEMY), - SPELL_CHARGE_WATER_ORB(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CHARGE_WATER_ORB), - SPELL_LVL_4_ENCHANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LVL_4_ENCHANT), - SPELL_WATCHTOWER_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WATCHTOWER_TELEPORT), - SPELL_FIRE_BLAST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FIRE_BLAST), - SPELL_CHARGE_EARTH_ORB(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CHARGE_EARTH_ORB), - SPELL_BONES_TO_PEACHES(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BONES_TO_PEACHES), - SPELL_SARADOMIN_STRIKE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SARADOMIN_STRIKE), - SPELL_CLAWS_OF_GUTHIX(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CLAWS_OF_GUTHIX), - SPELL_FLAMES_OF_ZAMORAK(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FLAMES_OF_ZAMORAK), - SPELL_TROLLHEIM_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TROLLHEIM_TELEPORT), - SPELL_WIND_WAVE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WIND_WAVE), - SPELL_CHARGE_FIRE_ORB(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CHARGE_FIRE_ORB), - SPELL_TELEPORT_TO_APE_ATOLL(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELEPORT_TO_APE_ATOLL), - SPELL_WATER_WAVE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WATER_WAVE), - SPELL_CHARGE_AIR_ORB(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CHARGE_AIR_ORB), - SPELL_VULNERABILITY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.VULNERABILITY), - SPELL_LVL_5_ENCHANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LVL_5_ENCHANT), - SPELL_TELEPORT_TO_KOUREND(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELEPORT_TO_KOUREND), - SPELL_EARTH_WAVE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.EARTH_WAVE), - SPELL_ENFEEBLE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ENFEEBLE), - SPELL_FIRE_WAVE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FIRE_WAVE), - SPELL_ENTANGLE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ENTANGLE), - SPELL_TELEOTHER_LUMBRIDGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELEOTHER_LUMBRIDGE), - SPELL_STUN(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.STUN), - SPELL_CHARGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CHARGE), - SPELL_WIND_SURGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WIND_SURGE), - SPELL_TELEOTHER_FALADOR(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELEOTHER_FALADOR), - SPELL_WATER_SURGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WATER_SURGE), - SPELL_TELE_BLOCK(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELE_BLOCK), - SPELL_LVL_6_ENCHANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LVL_6_ENCHANT), - SPELL_TELEOTHER_CAMELOT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELEOTHER_CAMELOT), - SPELL_EARTH_SURGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.EARTH_SURGE), - SPELL_LVL_7_ENCHANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LVL_7_ENCHANT), - SPELL_FIRE_SURGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FIRE_SURGE), - SPELL_BOUNTY_TARGET_TELEPORT2(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BOUNTY_TARGET_TELEPORT), - /* END OF STANDARD SPELL BOOK WIDGETS*/ - - /* ANCIENT SPELL BOOK WIDGETS*/ - SPELL_ICE_RUSH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ICE_RUSH), - SPELL_ICE_BLITZ(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ICE_BLITZ), - SPELL_ICE_BURST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ICE_BURST), - SPELL_ICE_BARRAGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ICE_BARRAGE), - SPELL_BLOOD_RUSH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BLOOD_RUSH), - SPELL_BLOOD_BLITZ(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BLOOD_BLITZ), - SPELL_BLOOD_BURST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BLOOD_BURST), - SPELL_BLOOD_BARRAGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BLOOD_BARRAGE), - SPELL_SMOKE_RUSH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SMOKE_RUSH), - SPELL_SMOKE_BLITZ(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SMOKE_BLITZ), - SPELL_SMOKE_BURST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SMOKE_BURST), - SPELL_SMOKE_BARRAGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SMOKE_BARRAGE), - SPELL_SHADOW_RUSH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SHADOW_RUSH), - SPELL_SHADOW_BLITZ(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SHADOW_BLITZ), - SPELL_SHADOW_BURST(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SHADOW_BURST), - SPELL_SHADOW_BARRAGE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SHADOW_BARRAGE), - SPELL_PADDEWWA_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.PADDEWWA_TELEPORT), - SPELL_SENNTISTEN_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SENNTISTEN_TELEPORT), - SPELL_KHARYRLL_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.KHARYRLL_TELEPORT), - SPELL_LASSAR_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LASSAR_TELEPORT), - SPELL_DAREEYAK_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.DAREEYAK_TELEPORT), - SPELL_CARRALLANGER_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CARRALLANGER_TELEPORT), - SPELL_ANNAKARL_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ANNAKARL_TELEPORT), - SPELL_GHORROCK_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.GHORROCK_TELEPORT), - SPELL_EDGEVILLE_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.EDGEVILLE_HOME_TELEPORT), - SPELL_BOUNTY_TARGET_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BOUNTY_TARGET_TELEPORT), - /* END OF ANCIENT SPELL BOOK WIDGETS*/ - - /* LUNAR SPELL BOOK WIDGETS*/ - SPELL_LUNAR_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.LUNAR_HOME_TELEPORT), - SPELL_VENGEANCE_OTHER(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.VENGEANCE_OTHER), - SPELL_VENGEANCE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.VENGEANCE), - SPELL_BOUNTY_TARGET_TELEPORT3(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BOUNTY_TARGET_TELEPORT), - SPELL_BAKE_PIE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BAKE_PIE), - SPELL_CURE_PLANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CURE_PLANT), - SPELL_MONSTER_EXAMINE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.MONSTER_EXAMINE), - SPELL_NPC_CONTACT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.NPC_CONTACT), - SPELL_CURE_OTHER(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CURE_OTHER), - SPELL_HUMIDIFY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.HUMIDIFY), - SPELL_MOONCLAN_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.MOONCLAN_TELEPORT), - SPELL_TELE_GROUP_MOONCLAN(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELE_GROUP_MOONCLAN), - SPELL_CURE_ME(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CURE_ME), - SPELL_HUNTER_KIT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.HUNTER_KIT), - SPELL_WATERBIRTH_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.WATERBIRTH_TELEPORT), - SPELL_TELE_GROUP_WATERBIRTH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELE_GROUP_WATERBIRTH), - SPELL_CURE_GROUP(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CURE_GROUP), - SPELL_STAT_SPY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.STAT_SPY), - SPELL_BARBARIAN_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BARBARIAN_TELEPORT), - SPELL_TELE_GROUP_BARBARIAN(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELE_GROUP_BARBARIAN), - SPELL_SUPERGLASS_MAKE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SUPERGLASS_MAKE), - SPELL_TAN_LEATHER(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TAN_LEATHER), - SPELL_KHAZARD_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.KHAZARD_TELEPORT), - SPELL_TELE_GROUP_KHAZARD(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELE_GROUP_KHAZARD), - SPELL_DREAM(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.DREAM), - SPELL_STRING_JEWELLERY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.STRING_JEWELLERY), - SPELL_STAT_RESTORE_POT_SHARE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.STAT_RESTORE_POT_SHARE), - SPELL_MAGIC_IMBUE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.MAGIC_IMBUE), - SPELL_FERTILE_SOIL(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FERTILE_SOIL), - SPELL_BOOST_POTION_SHARE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BOOST_POTION_SHARE), - SPELL_FISHING_GUILD_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.FISHING_GUILD_TELEPORT), - SPELL_TELE_GROUP_FISHING_GUILD(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELE_GROUP_FISHING_GUILD), - SPELL_PLANK_MAKE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.PLANK_MAKE), - SPELL_CATHERBY_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.CATHERBY_TELEPORT), - SPELL_TELE_GROUP_CATHERBY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELE_GROUP_CATHERBY), - SPELL_RECHARGE_DRAGONSTONE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.RECHARGE_DRAGONSTONE), - SPELL_ICE_PLATEAU_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ICE_PLATEAU_TELEPORT), - SPELL_TELE_GROUP_ICE_PLATEAU(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TELE_GROUP_ICE_PLATEAU), - SPELL_ENERGY_TRANSFER(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ENERGY_TRANSFER), - SPELL_HEAL_OTHER(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.HEAL_OTHER), - SPELL_HEAL_GROUP(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.HEAL_GROUP), - SPELL_SPELLBOOK_SWAP(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SPELLBOOK_SWAP), - SPELL_GEOMANCY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.GEOMANCY), - SPELL_SPIN_FLAX(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.SPIN_FLAX), - SPELL_OURANIA_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.OURANIA_TELEPORT), - /* END OF LUNAR SPELL BOOK WIDGETS*/ - SPELL_TOOLTIP(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.TOOLTIP), - /* ARCEUUS SPELL BOOK WIDGETS*/ - SPELL_KOUREND_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, net.runelite.api.widgets.WidgetID.StandardSpellBook.KOUREND_HOME_TELEPORT), - SPELL_ARCEUUS_HOME_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.ARCEUUS_HOME_TELEPORT), - SPELL_BATTLEFRONT_TELEPORT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.BATTLEFRONT_TELEPORT), - SPELL_REANIMATE_GOBLIN(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_GOBLIN), - SPELL_REANIMATE_MONKEY(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_MONKEY), - SPELL_REANIMATE_IMP(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_IMP), - SPELL_REANIMATE_MINOTAUR(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_MINOTAUR), - SPELL_REANIMATE_SCORPION(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_SCORPION), - SPELL_REANIMATE_BEAR(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_BEAR), - SPELL_REANIMATE_UNICORN(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_UNICORN), - SPELL_REANIMATE_DOG(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_DOG), - SPELL_REANIMATE_CHAOS_DRUID(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_CHAOS_DRUID), - SPELL_REANIMATE_GIANT(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_GIANT), - SPELL_REANIMATE_OGRE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_OGRE), - SPELL_REANIMATE_ELF(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_ELF), - SPELL_REANIMATE_TROLL(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_TROLL), - SPELL_REANIMATE_HORROR(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_HORROR), - SPELL_REANIMATE_KALPHITE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_KALPHITE), - SPELL_REANIMATE_DAGANNOTH(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_DAGANNOTH), - SPELL_REANIMATE_BLOODVELD(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_BLOODVELD), - SPELL_REANIMATE_TZHAAR(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_TZHAAR), - SPELL_REANIMATE_DEMON(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_DEMON), - SPELL_REANIMATE_AVIANSIE(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_AVIANSIE), - SPELL_REANIMATE_ABYSSAL(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_ABYSSAL), - SPELL_REANIMATE_DRAGON(WidgetID.SPELLBOOK_GROUP_ID, WidgetID.SpellBook.REANIMATE_DRAGON), - /* END OF ARCEUUS SPELL BOOK WIDGETS*/ - - MULTICOMBAT_FIXED(WidgetID.FIXED_VIEWPORT_GROUP_ID, WidgetID.FixedViewport.MULTICOMBAT_INDICATOR), - MULTICOMBAT_RESIZEABLE(WidgetID.RESIZABLE_VIEWPORT_BOTTOM_LINE_GROUP_ID, WidgetID.ResizableViewport.MULTICOMBAT_INDICATOR), - - FULLSCREEN_MAP_ROOT(WidgetID.FULLSCREEN_CONTAINER_TLI, WidgetID.FullScreenMap.ROOT), - - MUSICTAB_INTERFACE(WidgetID.MUSICTAB_GROUP_ID, 1), - MUSICTAB_SONG_BOX(WidgetID.MUSICTAB_GROUP_ID, 2), - MUSICTAB_ALL_SONGS(WidgetID.MUSICTAB_GROUP_ID, 3), - MUSICTAB_SCROLLBAR(WidgetID.MUSICTAB_GROUP_ID, 4), - MUSICTAB_PLAYING(WidgetID.MUSICTAB_GROUP_ID, 5), - MUSICTAB_CURRENT_SONG_NAME(WidgetID.MUSICTAB_GROUP_ID, 6), - MUSICTAB_AUTO_BUTTON_LISTENER(WidgetID.MUSICTAB_GROUP_ID, 7), - MUSICTAB_AUTO_BUTTON(WidgetID.MUSICTAB_GROUP_ID, 8), - MUSICTAB_MANUAL_BUTTON_LISTENER(WidgetID.MUSICTAB_GROUP_ID, 9), - MUSICTAB_MANUAL_BUTTON(WidgetID.MUSICTAB_GROUP_ID, 10), - MUSICTAB_LOOP_BUTTON_LISTENER(WidgetID.MUSICTAB_GROUP_ID, 11), - MUSICTAB_LOOP_BUTTON(WidgetID.MUSICTAB_GROUP_ID, 12), - MUSICTAB_UNLOCKED_SONGS(WidgetID.MUSICTAB_GROUP_ID, 13), - - QUESTTAB_QUEST_TAB(WidgetID.QUESTTAB_GROUP_ID, WidgetID.QuestTab.QUEST_TAB), - - EQUIPMENT_MELEE_STRENGTH(WidgetID.EQUIPMENT_PAGE_GROUP_ID, WidgetID.EquipmentWidgetIdentifiers.MELEE_STRENGTH), - EQUIPMENT_RANGED_STRENGTH(WidgetID.EQUIPMENT_PAGE_GROUP_ID, WidgetID.EquipmentWidgetIdentifiers.RANGED_STRENGTH), - EQUIPMENT_MAGIC_DAMAGE(WidgetID.EQUIPMENT_PAGE_GROUP_ID, WidgetID.EquipmentWidgetIdentifiers.MAGIC_DAMAGE), - EQUIP_YOUR_CHARACTER(WidgetID.EQUIPMENT_PAGE_GROUP_ID, WidgetID.EquipmentWidgetIdentifiers.EQUIP_YOUR_CHARACTER), - - BANK_PIN_TOP_LEFT_TEXT(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.TOP_LEFT_TEXT), - BANK_PIN_EXIT_BUTTON(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.EXIT_BUTTON), - BANK_PIN_FORGOT_BUTTON(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.FORGOT_BUTTON), - BANK_PIN_FIRST_ENTERED(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.FIRST_ENTERED), - BANK_PIN_SECOND_ENTERED(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.SECOND_ENTERED), - BANK_PIN_THIRD_ENTERED(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.THIRD_ENTERED), - BANK_PIN_FOURTH_ENTERED(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.FOURTH_ENTERED), - BANK_PIN_INSTRUCTION_TEXT(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.INSTRUCTION_TEXT), - BANK_PIN_1(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_1), - BANK_PIN_2(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_2), - BANK_PIN_3(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_3), - BANK_PIN_4(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_4), - BANK_PIN_5(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_5), - BANK_PIN_6(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_6), - BANK_PIN_7(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_7), - BANK_PIN_8(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_8), - BANK_PIN_9(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_9), - BANK_PIN_10(WidgetID.BANK_PIN_GROUP_ID, WidgetID.BankPin.BUTTON_10), - - XP_DROP_1(WidgetID.EXPERIENCE_DROP_GROUP_ID, WidgetID.ExperienceDrop.DROP_1), - XP_DROP_2(WidgetID.EXPERIENCE_DROP_GROUP_ID, WidgetID.ExperienceDrop.DROP_2), - XP_DROP_3(WidgetID.EXPERIENCE_DROP_GROUP_ID, WidgetID.ExperienceDrop.DROP_3), - XP_DROP_4(WidgetID.EXPERIENCE_DROP_GROUP_ID, WidgetID.ExperienceDrop.DROP_4), - XP_DROP_5(WidgetID.EXPERIENCE_DROP_GROUP_ID, WidgetID.ExperienceDrop.DROP_5), - XP_DROP_6(WidgetID.EXPERIENCE_DROP_GROUP_ID, WidgetID.ExperienceDrop.DROP_6), - XP_DROP_7(WidgetID.EXPERIENCE_DROP_GROUP_ID, WidgetID.ExperienceDrop.DROP_7), - - JEWELLERY_BOX_DUEL_RING(WidgetID.JEWELLERY_BOX_GROUP_ID, WidgetID.JewelBox.DUEL_RING), - JEWELLERY_BOX_GAME_NECK(WidgetID.JEWELLERY_BOX_GROUP_ID, WidgetID.JewelBox.GAME_NECK), - JEWELLERY_BOX_COMB_BRAC(WidgetID.JEWELLERY_BOX_GROUP_ID, WidgetID.JewelBox.COMB_BRAC), - JEWELLERY_BOX_SKIL_NECK(WidgetID.JEWELLERY_BOX_GROUP_ID, WidgetID.JewelBox.SKIL_NECK), - JEWELLERY_BOX_RING_OFGP(WidgetID.JEWELLERY_BOX_GROUP_ID, WidgetID.JewelBox.RING_OFGP), - JEWELLERY_BOX_AMUL_GLOR(WidgetID.JEWELLERY_BOX_GROUP_ID, WidgetID.JewelBox.AMUL_GLOR), - OPTIONS_CAMERA_ZOOM_SLIDER_HANDLE(WidgetID.OPTIONS_GROUP_ID, WidgetID.Options.CAMERA_ZOOM_SLIDER_HANDLE), - OPTIONS_MUSIC_SLIDER(WidgetID.OPTIONS_GROUP_ID, WidgetID.Options.MUSIC_SLIDER), - OPTIONS_SOUND_EFFECT_SLIDER(WidgetID.OPTIONS_GROUP_ID, WidgetID.Options.SOUND_EFFECT_SLIDER), - OPTIONS_AREA_SOUND_SLIDER(WidgetID.OPTIONS_GROUP_ID, WidgetID.Options.AREA_SOUND_SLIDER), - - TRADING_WITH(WidgetID.PLAYER_TRADE_SCREEN_GROUP_ID, WidgetID.TradeScreen.FIRST_TRADING_WITH), - SECOND_TRADING_WITH(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_TRADING_WITH), - SECOND_TRADING_WITH_ACCEPT_BUTTON(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_ACCEPT_FUNC), - SECOND_TRADING_WITH_ACCEPT_TEXT(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_ACCEPT_TEXT), - SECOND_TRADING_WITH_DECLINE_BUTTON(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_DECLINE_FUNC), - SECOND_TRADING_WITH_DECLINE_TEXT(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_DECLINE_TEXT), - SECOND_TRADING_WITH_MY_OFFER(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_MY_OFFER), - SECOND_TRADING_WITH_THEIR_OFFER(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_THEIR_OFFER), - SECOND_TRADING_WITH_MY_ITEMS(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_MY_ITEMS), - SECOND_TRADING_WITH_THEIR_ITEMS(WidgetID.PLAYER_TRADE_CONFIRM_GROUP_ID, WidgetID.TradeScreen.SECOND_THEIR_ITEMS), - - GAUNTLET_MAP(WidgetID.GAUNTLET_MAP_GROUP_ID, WidgetID.GauntletMap.CONTAINER), - - SETTINGS_INIT(WidgetID.SETTINGS_GROUP_ID, WidgetID.Settings.INIT), - - SHOP_ITEMS_CONTAINER(WidgetID.SHOP_GROUP_ID, WidgetID.Shop.ITEMS_CONTAINER), - ; - - private final int groupId; - private final int childId; - - WidgetInfo(int groupId, int childId) - { - this.groupId = groupId; - this.childId = childId; - } - - /** - * Gets the ID of the group-child pairing. - * - * @return the ID - */ - public int getId() - { - return groupId << 16 | childId; - } - - /** - * Gets the group ID of the pair. - * - * @return the group ID - */ - public int getGroupId() - { - return groupId; - } - - /** - * Gets the ID of the child in the group. - * - * @return the child ID - */ - public int getChildId() - { - return childId; - } - - /** - * Gets the packed widget ID. - * - * @return the packed ID - */ - public int getPackedId() - { - return groupId << 16 | childId; - } - - public static int PACK(int groupId, int childId) - { - return groupId << 16 | childId; - } - - /** - * Utility method that converts an ID returned by {@link #getId()} back - * to its group ID. - * - * @param id passed group-child ID - * @return the group ID - */ - public static int TO_GROUP(int id) - { - return id >>> 16; - } - - /** - * Utility method that converts an ID returned by {@link #getId()} back - * to its child ID. - * - * @param id passed group-child ID - * @return the child ID - */ - public static int TO_CHILD(int id) - { - return id & 0xFFFF; - } -} 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 ae78f52aa2..bf4fbab150 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -513,15 +513,6 @@ public interface Client extends GameShell @Nullable Widget getWidget(WidgetInfo widget); - /** - * Gets a widget from our extended WidgetInfo - * - * @param widget the widget info - * @return the widget - */ - @Nullable - Widget getWidget(com.openosrs.api.widgets.WidgetInfo widget); - /** * Gets a widget by its raw group ID and child ID. *

@@ -828,7 +819,7 @@ public interface Client extends GameShell * @param varps passed varps * @param varpId the VarpPlayer id * @return the value - * @see VarPlayer#id + * @see VarPlayer#getId() */ int getVarpValue(int[] varps, int varpId); diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index 418c56bb6c..a419be397e 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -101,6 +101,10 @@ public enum Varbits PRAYER_RIGOUR(5464), PRAYER_AUGURY(5465), + RIGOUR_UNLOCKED(5451), + AUGURY_UNLOCKED(5452), + PRESERVE_UNLOCKED(5453), + /** * Diary Entries */ @@ -182,6 +186,10 @@ public enum Varbits * Defensive casting mode */ DEFENSIVE_CASTING_MODE(2668), + /** + * Spells being auto-casted + */ + AUTO_CAST_SPELL(276), /** * Options @@ -397,6 +405,8 @@ public enum Varbits */ QUEST_THE_HAND_IN_THE_SAND(1527), + CAMELOT_TRAINING_ROOM_STATUS(3909), + /** * Daily Tasks (Collection availability) */ @@ -546,6 +556,7 @@ public enum Varbits * 1 = sell */ GE_OFFER_CREATION_TYPE(4397), + GE_OFFER_PRICE_PER_ITEM(4398), /** * The active tab within the quest interface diff --git a/runelite-api/src/main/java/net/runelite/api/events/ActorDeath.java b/runelite-api/src/main/java/net/runelite/api/events/ActorDeath.java index c976f2fe9c..b6e4588c0c 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ActorDeath.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ActorDeath.java @@ -31,7 +31,7 @@ import net.runelite.api.Actor; * An event fired when an actor dies. */ @Value -public class ActorDeath implements Event +public class ActorDeath { Actor actor; } diff --git a/runelite-api/src/main/java/net/runelite/api/events/AnimationChanged.java b/runelite-api/src/main/java/net/runelite/api/events/AnimationChanged.java index 943337f132..5cbc26a97b 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/AnimationChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/AnimationChanged.java @@ -17,7 +17,7 @@ import lombok.Data; * @see net.runelite.api.AnimationID */ @Data -public class AnimationChanged implements Event +public class AnimationChanged { /** * The actor that has entered a new animation. diff --git a/runelite-api/src/main/java/net/runelite/api/events/AreaSoundEffectPlayed.java b/runelite-api/src/main/java/net/runelite/api/events/AreaSoundEffectPlayed.java index c40aab4119..4114cb22ca 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/AreaSoundEffectPlayed.java +++ b/runelite-api/src/main/java/net/runelite/api/events/AreaSoundEffectPlayed.java @@ -29,7 +29,7 @@ import lombok.Data; import net.runelite.api.Actor; @Data -public class AreaSoundEffectPlayed implements Event +public class AreaSoundEffectPlayed { @Nullable private final Actor source; diff --git a/runelite-api/src/main/java/net/runelite/api/events/BeforeMenuRender.java b/runelite-api/src/main/java/net/runelite/api/events/BeforeMenuRender.java index 055fb37a41..5d1e10994b 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/BeforeMenuRender.java +++ b/runelite-api/src/main/java/net/runelite/api/events/BeforeMenuRender.java @@ -27,7 +27,7 @@ package net.runelite.api.events; import lombok.Data; @Data -public class BeforeMenuRender implements Event +public class BeforeMenuRender { private boolean consumed; diff --git a/runelite-api/src/main/java/net/runelite/api/events/BeforeRender.java b/runelite-api/src/main/java/net/runelite/api/events/BeforeRender.java index 70d7551ac0..bee23922dd 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/BeforeRender.java +++ b/runelite-api/src/main/java/net/runelite/api/events/BeforeRender.java @@ -27,7 +27,7 @@ package net.runelite.api.events; /** * Posted at the start of every frame */ -public class BeforeRender implements Event +public class BeforeRender { public static final BeforeRender INSTANCE = new BeforeRender(); diff --git a/runelite-api/src/main/java/net/runelite/api/events/CannonChanged.java b/runelite-api/src/main/java/net/runelite/api/events/CannonChanged.java index 4782c8e8a1..4af81c6ccf 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/CannonChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/CannonChanged.java @@ -31,7 +31,7 @@ import lombok.Value; * an event posted when a cannonball is fired */ @Value -public class CannonChanged implements Event +public class CannonChanged { /** * The projectile id. diff --git a/runelite-api/src/main/java/net/runelite/api/events/CannonPlaced.java b/runelite-api/src/main/java/net/runelite/api/events/CannonPlaced.java index 8d0aa636c0..8b3f4402bd 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/CannonPlaced.java +++ b/runelite-api/src/main/java/net/runelite/api/events/CannonPlaced.java @@ -33,7 +33,7 @@ import net.runelite.api.coords.WorldPoint; * an event posted when a cannonball is fired */ @Value -public class CannonPlaced implements Event +public class CannonPlaced { /** * Cannon placed or picked up. diff --git a/runelite-api/src/main/java/net/runelite/api/events/CanvasSizeChanged.java b/runelite-api/src/main/java/net/runelite/api/events/CanvasSizeChanged.java index 66ad5dbcec..0efef18d0d 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/CanvasSizeChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/CanvasSizeChanged.java @@ -27,7 +27,7 @@ package net.runelite.api.events; /** * An event posted when the canvas size might have changed. */ -public class CanvasSizeChanged implements Event +public class CanvasSizeChanged { public static final CanvasSizeChanged INSTANCE = new CanvasSizeChanged(); diff --git a/runelite-api/src/main/java/net/runelite/api/events/ChatMessage.java b/runelite-api/src/main/java/net/runelite/api/events/ChatMessage.java index 3c6230e6e9..fccf5a98d8 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ChatMessage.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ChatMessage.java @@ -41,7 +41,7 @@ import lombok.NoArgsConstructor; @Data @AllArgsConstructor @NoArgsConstructor -public class ChatMessage implements Event +public class ChatMessage { /** * The underlying MessageNode for the message. diff --git a/runelite-api/src/main/java/net/runelite/api/events/ClientTick.java b/runelite-api/src/main/java/net/runelite/api/events/ClientTick.java index 9ffef0d573..6874f4145c 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ClientTick.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ClientTick.java @@ -27,7 +27,7 @@ package net.runelite.api.events; /** * Posted every client tick */ -public class ClientTick implements Event +public class ClientTick { public static final ClientTick INSTANCE = new ClientTick(); diff --git a/runelite-api/src/main/java/net/runelite/api/events/CommandExecuted.java b/runelite-api/src/main/java/net/runelite/api/events/CommandExecuted.java index a66acb9d5c..fe524a7e76 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/CommandExecuted.java +++ b/runelite-api/src/main/java/net/runelite/api/events/CommandExecuted.java @@ -42,7 +42,7 @@ import lombok.Value; * will set command to "" and arguments to ["hello", "world!"]. */ @Value -public class CommandExecuted implements Event +public class CommandExecuted { /** * The name of the command entered. diff --git a/runelite-api/src/main/java/net/runelite/api/events/ConfigButtonClicked.java b/runelite-api/src/main/java/net/runelite/api/events/ConfigButtonClicked.java index 7aeb4ffa84..24556b1dff 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ConfigButtonClicked.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ConfigButtonClicked.java @@ -1,10 +1,9 @@ package net.runelite.api.events; import lombok.Data; -import net.runelite.api.events.Event; @Data -public class ConfigButtonClicked implements Event +public class ConfigButtonClicked { private String group, key; } diff --git a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectChanged.java b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectChanged.java index a64a1c5c47..a18683d7b4 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectChanged.java @@ -33,7 +33,7 @@ import lombok.Data; * has been modified. */ @Data -public class DecorativeObjectChanged implements Event +public class DecorativeObjectChanged { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectDespawned.java index 6760c0d05d..94c06488c7 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectDespawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectDespawned.java @@ -33,7 +33,7 @@ import lombok.Data; * is removed. */ @Data -public class DecorativeObjectDespawned implements Event +public class DecorativeObjectDespawned { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectSpawned.java index 37d0a5fab2..fe8d17862f 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectSpawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/DecorativeObjectSpawned.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where a {@link DecorativeObject} is attached to a {@link Tile}. */ @Data -public class DecorativeObjectSpawned implements Event +public class DecorativeObjectSpawned { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/DialogProcessed.java b/runelite-api/src/main/java/net/runelite/api/events/DialogProcessed.java index e86b64af01..c94d82b194 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/DialogProcessed.java +++ b/runelite-api/src/main/java/net/runelite/api/events/DialogProcessed.java @@ -4,7 +4,7 @@ import lombok.Value; import net.runelite.api.DialogOption; @Value -public class DialogProcessed implements Event +public class DialogProcessed { DialogOption dialogOption; } \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/events/DraggingWidgetChanged.java b/runelite-api/src/main/java/net/runelite/api/events/DraggingWidgetChanged.java index 0f9bf09691..8aef02d570 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/DraggingWidgetChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/DraggingWidgetChanged.java @@ -31,7 +31,7 @@ import lombok.Data; * the cursor. */ @Data -public class DraggingWidgetChanged implements Event +public class DraggingWidgetChanged { /** * Whether a widget is currently being dragged. diff --git a/runelite-api/src/main/java/net/runelite/api/events/DynamicObjectAnimationChanged.java b/runelite-api/src/main/java/net/runelite/api/events/DynamicObjectAnimationChanged.java index 382cb706fb..1e4571e4fe 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/DynamicObjectAnimationChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/DynamicObjectAnimationChanged.java @@ -3,7 +3,7 @@ package net.runelite.api.events; import lombok.Data; @Data -public class DynamicObjectAnimationChanged implements Event +public class DynamicObjectAnimationChanged { /** * The object that has entered a new animation. diff --git a/runelite-api/src/main/java/net/runelite/api/events/Event.java b/runelite-api/src/main/java/net/runelite/api/events/Event.java deleted file mode 100644 index b031dce09e..0000000000 --- a/runelite-api/src/main/java/net/runelite/api/events/Event.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.runelite.api.events; - -public interface Event {} diff --git a/runelite-api/src/main/java/net/runelite/api/events/FakeXpDrop.java b/runelite-api/src/main/java/net/runelite/api/events/FakeXpDrop.java index 0aac13728f..07a98b0eb4 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/FakeXpDrop.java +++ b/runelite-api/src/main/java/net/runelite/api/events/FakeXpDrop.java @@ -28,7 +28,7 @@ import lombok.Value; import net.runelite.api.Skill; @Value -public class FakeXpDrop implements Event +public class FakeXpDrop { Skill skill; int xp; diff --git a/runelite-api/src/main/java/net/runelite/api/events/FocusChanged.java b/runelite-api/src/main/java/net/runelite/api/events/FocusChanged.java index 3f190fd331..fd9fcb6466 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/FocusChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/FocusChanged.java @@ -37,7 +37,7 @@ import lombok.Data; * */ @Data -public class FocusChanged implements Event +public class FocusChanged { /** * The new focus state. diff --git a/runelite-api/src/main/java/net/runelite/api/events/FriendAdded.java b/runelite-api/src/main/java/net/runelite/api/events/FriendAdded.java index c7c2160759..37be825080 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/FriendAdded.java +++ b/runelite-api/src/main/java/net/runelite/api/events/FriendAdded.java @@ -6,7 +6,7 @@ import lombok.Value; * An event where a request to add a friend is sent to the server. */ @Value -public class FriendAdded implements Event +public class FriendAdded { /** * The name of the added friend. diff --git a/runelite-api/src/main/java/net/runelite/api/events/FriendsChatChanged.java b/runelite-api/src/main/java/net/runelite/api/events/FriendsChatChanged.java index 12c203769a..271eb83e49 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/FriendsChatChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/FriendsChatChanged.java @@ -30,7 +30,7 @@ import lombok.Value; * An event where the client has joined or left a friends chat. */ @Value -public class FriendsChatChanged implements Event +public class FriendsChatChanged { /** * Whether or not the client is now in a friends chat. diff --git a/runelite-api/src/main/java/net/runelite/api/events/FriendsChatMemberJoined.java b/runelite-api/src/main/java/net/runelite/api/events/FriendsChatMemberJoined.java index 7adbf159fe..e5a0cdb8a0 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/FriendsChatMemberJoined.java +++ b/runelite-api/src/main/java/net/runelite/api/events/FriendsChatMemberJoined.java @@ -28,7 +28,7 @@ import lombok.Value; import net.runelite.api.FriendsChatMember; @Value -public class FriendsChatMemberJoined implements Event +public class FriendsChatMemberJoined { /** * The member that joined diff --git a/runelite-api/src/main/java/net/runelite/api/events/FriendsChatMemberLeft.java b/runelite-api/src/main/java/net/runelite/api/events/FriendsChatMemberLeft.java index 49530eb870..0970b762c8 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/FriendsChatMemberLeft.java +++ b/runelite-api/src/main/java/net/runelite/api/events/FriendsChatMemberLeft.java @@ -28,7 +28,7 @@ import lombok.Value; import net.runelite.api.FriendsChatMember; @Value -public class FriendsChatMemberLeft implements Event +public class FriendsChatMemberLeft { /** * The member that left diff --git a/runelite-api/src/main/java/net/runelite/api/events/GameObjectChanged.java b/runelite-api/src/main/java/net/runelite/api/events/GameObjectChanged.java index dee1f261ea..117ad81f60 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GameObjectChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GameObjectChanged.java @@ -32,7 +32,7 @@ import net.runelite.api.Tile; * An event where a {@link GameObject} on a {@link Tile} has been replaced. */ @Data -public class GameObjectChanged implements Event +public class GameObjectChanged { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GameObjectDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/GameObjectDespawned.java index ed7e11ae2f..5d2e9fad1a 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GameObjectDespawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GameObjectDespawned.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where a {@link GameObject} on a {@link Tile} is removed. */ @Data -public class GameObjectDespawned implements Event +public class GameObjectDespawned { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GameObjectSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/GameObjectSpawned.java index df2b4fcca4..dc9fdbdd24 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GameObjectSpawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GameObjectSpawned.java @@ -32,7 +32,7 @@ import net.runelite.api.Tile; * An event where a {@link GameObject} is added to a {@link Tile}. */ @Data -public class GameObjectSpawned implements Event +public class GameObjectSpawned { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GameStateChanged.java b/runelite-api/src/main/java/net/runelite/api/events/GameStateChanged.java index eb9612211c..2a0b61fc40 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GameStateChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GameStateChanged.java @@ -31,7 +31,7 @@ import lombok.Data; * An event where the clients game state has changed. */ @Data -public class GameStateChanged implements Event +public class GameStateChanged { /** * The new game state. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GameTick.java b/runelite-api/src/main/java/net/runelite/api/events/GameTick.java index ce3fbaf25f..499b2ae83c 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GameTick.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GameTick.java @@ -41,7 +41,7 @@ package net.runelite.api.events; * Note that occurrences that take place purely on the client, such as right * click menus, are independent of the game tick. */ -public class GameTick implements Event +public class GameTick { public static final GameTick INSTANCE = new GameTick(); diff --git a/runelite-api/src/main/java/net/runelite/api/events/GrandExchangeOfferChanged.java b/runelite-api/src/main/java/net/runelite/api/events/GrandExchangeOfferChanged.java index 52ae0e6d92..88e4aa1fac 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GrandExchangeOfferChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GrandExchangeOfferChanged.java @@ -41,7 +41,7 @@ import lombok.Data; * can change into. */ @Data -public class GrandExchangeOfferChanged implements Event +public class GrandExchangeOfferChanged { /** * The offer that has been modified. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GrandExchangeSearched.java b/runelite-api/src/main/java/net/runelite/api/events/GrandExchangeSearched.java index b357240149..aa96680d1c 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GrandExchangeSearched.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GrandExchangeSearched.java @@ -31,7 +31,7 @@ import lombok.Data; * An event where the Grand Exchange has been searched. */ @Data -public class GrandExchangeSearched implements Event +public class GrandExchangeSearched { /** * Whether or not the event has been consumed by a subscriber. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GraphicChanged.java b/runelite-api/src/main/java/net/runelite/api/events/GraphicChanged.java index ee7b781c82..775487c6d0 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GraphicChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GraphicChanged.java @@ -19,7 +19,7 @@ import net.runelite.api.Actor; * @see net.runelite.api.GraphicID */ @Data -public class GraphicChanged implements Event +public class GraphicChanged { /** * The actor that has had their graphic changed. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GraphicsObjectCreated.java b/runelite-api/src/main/java/net/runelite/api/events/GraphicsObjectCreated.java index b074e63d40..280cc77624 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GraphicsObjectCreated.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GraphicsObjectCreated.java @@ -31,7 +31,7 @@ import lombok.Value; * An event where a new {@link GraphicsObject} has been created. */ @Value -public class GraphicsObjectCreated implements Event +public class GraphicsObjectCreated { /** * The newly created graphics object. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectChanged.java b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectChanged.java index c58b46575d..8fa97ae503 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectChanged.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where the {@link GroundObject} on a {@link Tile} has been changed. */ @Data -public class GroundObjectChanged implements Event +public class GroundObjectChanged { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectDespawned.java index 6e793f025e..54bb6dfbe0 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectDespawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectDespawned.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where a {@link GroundObject} on a {@link Tile} has been removed. */ @Data -public class GroundObjectDespawned implements Event +public class GroundObjectDespawned { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectSpawned.java index 2ccce09136..af1f15ab6f 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/GroundObjectSpawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/GroundObjectSpawned.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where a {@link GroundObject} is added to a {@link Tile}. */ @Data -public class GroundObjectSpawned implements Event +public class GroundObjectSpawned { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/HitsplatApplied.java b/runelite-api/src/main/java/net/runelite/api/events/HitsplatApplied.java index d8b22ee2bc..f96a0f08d7 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/HitsplatApplied.java +++ b/runelite-api/src/main/java/net/runelite/api/events/HitsplatApplied.java @@ -36,7 +36,7 @@ import lombok.Data; * visible hitsplats. */ @Data -public class HitsplatApplied implements Event +public class HitsplatApplied { /** * The actor the hitsplat was applied to. diff --git a/runelite-api/src/main/java/net/runelite/api/events/InteractChanged.java b/runelite-api/src/main/java/net/runelite/api/events/InteractChanged.java index 1e08180b88..fcd2e54a43 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/InteractChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/InteractChanged.java @@ -28,7 +28,7 @@ import net.runelite.api.Actor; import lombok.Data; @Data -public class InteractChanged implements Event +public class InteractChanged { private Actor actor; } diff --git a/runelite-api/src/main/java/net/runelite/api/events/InteractingChanged.java b/runelite-api/src/main/java/net/runelite/api/events/InteractingChanged.java index a4f22a3583..ef1da8baca 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/InteractingChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/InteractingChanged.java @@ -7,7 +7,7 @@ import lombok.Value; * An event called when the actor an actor is interacting with changes */ @Value -public class InteractingChanged implements Event +public class InteractingChanged { Actor source; diff --git a/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java b/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java index 82dc290dd0..5a5ddef151 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ItemContainerChanged.java @@ -39,7 +39,7 @@ import lombok.Value; * */ @Value -public class ItemContainerChanged implements Event +public class ItemContainerChanged { /** * The modified container's ID. diff --git a/runelite-api/src/main/java/net/runelite/api/events/ItemDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/ItemDespawned.java index 870a39f29f..22082bfedc 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ItemDespawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ItemDespawned.java @@ -34,7 +34,7 @@ import lombok.Value; * all item piles are implicitly despawned, and despawn events will not be sent. */ @Value -public class ItemDespawned implements Event +public class ItemDespawned { Tile tile; TileItem item; diff --git a/runelite-api/src/main/java/net/runelite/api/events/ItemQuantityChanged.java b/runelite-api/src/main/java/net/runelite/api/events/ItemQuantityChanged.java index 0e596b8397..7971da5f12 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ItemQuantityChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ItemQuantityChanged.java @@ -33,7 +33,7 @@ import lombok.Value; * Called when the quantity of an item pile changes. */ @Value -public class ItemQuantityChanged implements Event +public class ItemQuantityChanged { TileItem item; Tile tile; diff --git a/runelite-api/src/main/java/net/runelite/api/events/ItemSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/ItemSpawned.java index 9daa54077d..af3c034abc 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ItemSpawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ItemSpawned.java @@ -34,7 +34,7 @@ import lombok.Value; * all item piles are implicitly reset and a new spawn event will be sent. */ @Value -public class ItemSpawned implements Event +public class ItemSpawned { Tile tile; TileItem item; diff --git a/runelite-api/src/main/java/net/runelite/api/events/Menu.java b/runelite-api/src/main/java/net/runelite/api/events/Menu.java index d344397221..d7a5af40cb 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/Menu.java +++ b/runelite-api/src/main/java/net/runelite/api/events/Menu.java @@ -4,7 +4,7 @@ package net.runelite.api.events; * Gets sent before menu handling code is ran, once per client tick. * Can be consumed, skipping this method this tick. */ -public class Menu implements Event +public class Menu { public static final Menu MENU = new Menu(); diff --git a/runelite-api/src/main/java/net/runelite/api/events/MenuEntryAdded.java b/runelite-api/src/main/java/net/runelite/api/events/MenuEntryAdded.java index 7b9c0d3fb3..727476a035 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/MenuEntryAdded.java +++ b/runelite-api/src/main/java/net/runelite/api/events/MenuEntryAdded.java @@ -30,7 +30,7 @@ import net.runelite.api.MenuEntry; /** * An event when a new entry is added to a right-click menu. */ -public class MenuEntryAdded extends MenuEntry implements Event +public class MenuEntryAdded extends MenuEntry { public MenuEntryAdded(String option, String target, int identifier, int opcode, int param0, int param1, boolean forceLeftClick) { diff --git a/runelite-api/src/main/java/net/runelite/api/events/MenuOpened.java b/runelite-api/src/main/java/net/runelite/api/events/MenuOpened.java index ee31b0ad04..6d34000cea 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/MenuOpened.java +++ b/runelite-api/src/main/java/net/runelite/api/events/MenuOpened.java @@ -34,7 +34,7 @@ import lombok.Data; * An event where a menu has been opened. */ @Data -public class MenuOpened implements Event, Iterable +public class MenuOpened implements Iterable { /** * This should be set to true if anything about the menu diff --git a/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java b/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java index 0ed4fb0df1..2b4db1ce3a 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java +++ b/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java @@ -41,7 +41,7 @@ import net.runelite.api.MenuEntry; * it seems that this event still triggers with the "Cancel" action. */ @Getter -public class MenuOptionClicked extends MenuEntry implements Event +public class MenuOptionClicked extends MenuEntry { public MenuOptionClicked(String option, String target, int identifier, int opcode, int param0, int param1, boolean forceLeftClick) { diff --git a/runelite-api/src/main/java/net/runelite/api/events/MenuShouldLeftClick.java b/runelite-api/src/main/java/net/runelite/api/events/MenuShouldLeftClick.java index 9652721791..c36372ff2c 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/MenuShouldLeftClick.java +++ b/runelite-api/src/main/java/net/runelite/api/events/MenuShouldLeftClick.java @@ -31,7 +31,7 @@ import lombok.Data; * opened on left click. */ @Data -public class MenuShouldLeftClick implements Event +public class MenuShouldLeftClick { /** * If set to true, the menu will open on left click. diff --git a/runelite-api/src/main/java/net/runelite/api/events/NameableNameChanged.java b/runelite-api/src/main/java/net/runelite/api/events/NameableNameChanged.java index fcf0556e71..18b106ec9f 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/NameableNameChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/NameableNameChanged.java @@ -31,7 +31,7 @@ import lombok.Value; * An event where a {@link Nameable} has had their name changed. */ @Value -public class NameableNameChanged implements Event +public class NameableNameChanged { /** * The nameable that changed names. diff --git a/runelite-api/src/main/java/net/runelite/api/events/NpcActionChanged.java b/runelite-api/src/main/java/net/runelite/api/events/NpcActionChanged.java index 20f831e328..7539821167 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/NpcActionChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/NpcActionChanged.java @@ -31,7 +31,7 @@ import net.runelite.api.NPCComposition; * An event where an action of an {@link NPCComposition} has changed. */ @Data -public class NpcActionChanged implements Event +public class NpcActionChanged { /** * The NPC composition that has been changed. diff --git a/runelite-api/src/main/java/net/runelite/api/events/NpcChanged.java b/runelite-api/src/main/java/net/runelite/api/events/NpcChanged.java index 606ebcbb7e..f01da68ae2 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/NpcChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/NpcChanged.java @@ -32,7 +32,7 @@ import net.runelite.api.NPCComposition; * Fires after the composition of an {@link NPC} changes. */ @Value -public class NpcChanged implements Event +public class NpcChanged { /** * The NPC of which the composition changed. diff --git a/runelite-api/src/main/java/net/runelite/api/events/NpcDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/NpcDespawned.java index 6b7bd48b29..9d2fed4200 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/NpcDespawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/NpcDespawned.java @@ -32,7 +32,7 @@ import lombok.Value; * An event where an {@link NPC} has despawned. */ @Value -public class NpcDespawned implements Event +public class NpcDespawned { /** * The despawned NPC. diff --git a/runelite-api/src/main/java/net/runelite/api/events/NpcSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/NpcSpawned.java index 0bb3c3f3cb..aab89fffb8 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/NpcSpawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/NpcSpawned.java @@ -32,7 +32,7 @@ import lombok.Value; * An event where an {@link NPC} has spawned. */ @Value -public class NpcSpawned implements Event +public class NpcSpawned { /** * The spawned NPC. diff --git a/runelite-api/src/main/java/net/runelite/api/events/OverheadPrayerChanged.java b/runelite-api/src/main/java/net/runelite/api/events/OverheadPrayerChanged.java new file mode 100644 index 0000000000..46bf0f4a5c --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/OverheadPrayerChanged.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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.events; + +import lombok.Data; +import net.runelite.api.HeadIcon; +import net.runelite.api.Player; + +@Data +public class OverheadPrayerChanged +{ + private final Player player; + + private final HeadIcon oldHeadIcon; + + private final HeadIcon newHeadIcon; +} diff --git a/runelite-api/src/main/java/net/runelite/api/events/OverheadTextChanged.java b/runelite-api/src/main/java/net/runelite/api/events/OverheadTextChanged.java index 78df69b9de..9e77178776 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/OverheadTextChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/OverheadTextChanged.java @@ -4,7 +4,7 @@ import net.runelite.api.Actor; import lombok.Value; @Value -public class OverheadTextChanged implements Event +public class OverheadTextChanged { Actor actor; diff --git a/runelite-api/src/main/java/net/runelite/api/events/PlayerDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/PlayerDespawned.java index 0970c08e9b..ed5f2a9057 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/PlayerDespawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/PlayerDespawned.java @@ -34,7 +34,7 @@ import lombok.Value; * Note: This event does not get called for the local player. */ @Value -public class PlayerDespawned implements Event +public class PlayerDespawned { /** * The despawned player. diff --git a/runelite-api/src/main/java/net/runelite/api/events/PlayerMenuOptionClicked.java b/runelite-api/src/main/java/net/runelite/api/events/PlayerMenuOptionClicked.java index 14a85b6025..dcfb0421ca 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/PlayerMenuOptionClicked.java +++ b/runelite-api/src/main/java/net/runelite/api/events/PlayerMenuOptionClicked.java @@ -31,7 +31,7 @@ import lombok.Data; * been clicked (ie. HiScore Lookup). */ @Data -public class PlayerMenuOptionClicked implements Event +public class PlayerMenuOptionClicked { /** * The menu option clicked. diff --git a/runelite-api/src/main/java/net/runelite/api/events/PlayerMenuOptionsChanged.java b/runelite-api/src/main/java/net/runelite/api/events/PlayerMenuOptionsChanged.java index a75af8acf3..d33aa4d06c 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/PlayerMenuOptionsChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/PlayerMenuOptionsChanged.java @@ -27,7 +27,7 @@ package net.runelite.api.events; import lombok.Data; @Data -public class PlayerMenuOptionsChanged implements Event +public class PlayerMenuOptionsChanged { /** * Index in playerOptions which changed. diff --git a/runelite-api/src/main/java/net/runelite/api/events/PlayerSkullChanged.java b/runelite-api/src/main/java/net/runelite/api/events/PlayerSkullChanged.java new file mode 100644 index 0000000000..931c415ed1 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/PlayerSkullChanged.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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.events; + +import lombok.Data; +import net.runelite.api.Player; +import net.runelite.api.SkullIcon; + +@Data +public class PlayerSkullChanged +{ + private final Player player; + + private final SkullIcon oldSkullIcon; + + private final SkullIcon newSkullIcon; +} diff --git a/runelite-api/src/main/java/net/runelite/api/events/PlayerSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/PlayerSpawned.java index 9ba53bbc4f..73e1932285 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/PlayerSpawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/PlayerSpawned.java @@ -32,7 +32,7 @@ import lombok.Value; * An event where a {@link Player} has spawned. */ @Value -public class PlayerSpawned implements Event +public class PlayerSpawned { /** * The spawned player. diff --git a/runelite-api/src/main/java/net/runelite/api/events/PostHealthBar.java b/runelite-api/src/main/java/net/runelite/api/events/PostHealthBar.java index 1f327ec13a..c8896ff945 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/PostHealthBar.java +++ b/runelite-api/src/main/java/net/runelite/api/events/PostHealthBar.java @@ -28,7 +28,7 @@ import net.runelite.api.HealthBar; import lombok.Data; @Data -public class PostHealthBar implements Event +public class PostHealthBar { private HealthBar healthBar; } diff --git a/runelite-api/src/main/java/net/runelite/api/events/PostItemComposition.java b/runelite-api/src/main/java/net/runelite/api/events/PostItemComposition.java index 14ec4d197c..1e9000cfd1 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/PostItemComposition.java +++ b/runelite-api/src/main/java/net/runelite/api/events/PostItemComposition.java @@ -32,7 +32,7 @@ import net.runelite.api.ItemComposition; * its data is initialized. */ @Data -public class PostItemComposition implements Event +public class PostItemComposition { /** * The newly created item. diff --git a/runelite-api/src/main/java/net/runelite/api/events/ProjectileMoved.java b/runelite-api/src/main/java/net/runelite/api/events/ProjectileMoved.java index 82590778be..70d73d7026 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ProjectileMoved.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ProjectileMoved.java @@ -35,7 +35,7 @@ import lombok.Data; * once (ie. AoE from Lizardman Shaman). */ @Data -public class ProjectileMoved implements Event +public class ProjectileMoved { /** * The projectile being moved. diff --git a/runelite-api/src/main/java/net/runelite/api/events/ProjectileSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/ProjectileSpawned.java index 7c11ea215f..7b7bda43ad 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ProjectileSpawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ProjectileSpawned.java @@ -32,7 +32,7 @@ import lombok.Data; * An event called whenever a {@link Projectile} has spawned. */ @Data -public class ProjectileSpawned implements Event +public class ProjectileSpawned { /** * The spawned projectile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/ResizeableChanged.java b/runelite-api/src/main/java/net/runelite/api/events/ResizeableChanged.java index ecc5c0d380..146f92ac38 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ResizeableChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ResizeableChanged.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where the game has changed from fixed to resizable mode or vice versa. */ @Data -public class ResizeableChanged implements Event +public class ResizeableChanged { /** * Whether the game is in resizable mode. diff --git a/runelite-api/src/main/java/net/runelite/api/events/ScriptCallbackEvent.java b/runelite-api/src/main/java/net/runelite/api/events/ScriptCallbackEvent.java index 6461cad9e7..716f7bbe90 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ScriptCallbackEvent.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ScriptCallbackEvent.java @@ -31,7 +31,7 @@ import lombok.Data; * A callback from a runelite_callback opcode in a cs2 */ @Data -public class ScriptCallbackEvent implements Event +public class ScriptCallbackEvent { /** * The script that is currently being executed diff --git a/runelite-api/src/main/java/net/runelite/api/events/ScriptPostFired.java b/runelite-api/src/main/java/net/runelite/api/events/ScriptPostFired.java index 75677640c1..55338bd87a 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ScriptPostFired.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ScriptPostFired.java @@ -30,7 +30,7 @@ import lombok.Value; * An event that is fired after the designated script is ran */ @Value -public class ScriptPostFired implements Event +public class ScriptPostFired { /** * The script id of the invoked script diff --git a/runelite-api/src/main/java/net/runelite/api/events/ScriptPreFired.java b/runelite-api/src/main/java/net/runelite/api/events/ScriptPreFired.java index 55c58ac4a3..f8e9549281 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/ScriptPreFired.java +++ b/runelite-api/src/main/java/net/runelite/api/events/ScriptPreFired.java @@ -31,7 +31,7 @@ import net.runelite.api.ScriptEvent; * An event that is fired before the designated script is ran */ @Value -public class ScriptPreFired implements Event +public class ScriptPreFired { /** * The script id of the invoked script diff --git a/runelite-api/src/main/java/net/runelite/api/events/SoundEffectPlayed.java b/runelite-api/src/main/java/net/runelite/api/events/SoundEffectPlayed.java index b2edb71708..7c111d1d8d 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/SoundEffectPlayed.java +++ b/runelite-api/src/main/java/net/runelite/api/events/SoundEffectPlayed.java @@ -29,7 +29,7 @@ import lombok.Data; import net.runelite.api.Actor; @Data -public class SoundEffectPlayed implements Event +public class SoundEffectPlayed { @Nullable private final Actor source; diff --git a/runelite-api/src/main/java/net/runelite/api/events/StatChanged.java b/runelite-api/src/main/java/net/runelite/api/events/StatChanged.java index b329e9dbbc..809636f446 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/StatChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/StatChanged.java @@ -31,7 +31,7 @@ import net.runelite.api.Skill; * An event where the experience, level, or boosted level of a {@link Skill} has been modified. */ @Value -public class StatChanged implements Event +public class StatChanged { Skill skill; int xp; diff --git a/runelite-api/src/main/java/net/runelite/api/events/UsernameChanged.java b/runelite-api/src/main/java/net/runelite/api/events/UsernameChanged.java index 802df61b63..3dea001150 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/UsernameChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/UsernameChanged.java @@ -30,7 +30,7 @@ package net.runelite.api.events; * This event triggers for every character change to the username * in the login screen. */ -public class UsernameChanged implements Event +public class UsernameChanged { public static final UsernameChanged INSTANCE = new UsernameChanged(); diff --git a/runelite-api/src/main/java/net/runelite/api/events/VarClientIntChanged.java b/runelite-api/src/main/java/net/runelite/api/events/VarClientIntChanged.java index adc2be50e1..521e8a62ae 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/VarClientIntChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/VarClientIntChanged.java @@ -32,7 +32,7 @@ import lombok.Value; * @see net.runelite.api.VarClientInt */ @Value -public class VarClientIntChanged implements Event +public class VarClientIntChanged { int index; } diff --git a/runelite-api/src/main/java/net/runelite/api/events/VarClientStrChanged.java b/runelite-api/src/main/java/net/runelite/api/events/VarClientStrChanged.java index c63e088470..2fff4da47e 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/VarClientStrChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/VarClientStrChanged.java @@ -32,7 +32,7 @@ import lombok.Value; * @see net.runelite.api.VarClientStr */ @Value -public class VarClientStrChanged implements Event +public class VarClientStrChanged { int index; } diff --git a/runelite-api/src/main/java/net/runelite/api/events/VarbitChanged.java b/runelite-api/src/main/java/net/runelite/api/events/VarbitChanged.java index 945222c072..fbac6d5e01 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/VarbitChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/VarbitChanged.java @@ -37,7 +37,7 @@ import lombok.Data; * if the VarPlayer has special engine behavior assigned to it. */ @Data -public class VarbitChanged implements Event +public class VarbitChanged { /** * Index in the varp array that was changed. diff --git a/runelite-api/src/main/java/net/runelite/api/events/VolumeChanged.java b/runelite-api/src/main/java/net/runelite/api/events/VolumeChanged.java index 38701712c3..30e87b9c79 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/VolumeChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/VolumeChanged.java @@ -28,7 +28,7 @@ package net.runelite.api.events; import lombok.Value; @Value -public class VolumeChanged implements Event +public class VolumeChanged { public enum Type { diff --git a/runelite-api/src/main/java/net/runelite/api/events/WallObjectChanged.java b/runelite-api/src/main/java/net/runelite/api/events/WallObjectChanged.java index 6cf5fd8f25..bea7686602 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WallObjectChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WallObjectChanged.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where the {@link WallObject} of a {@link Tile} has been changed. */ @Data -public class WallObjectChanged implements Event +public class WallObjectChanged { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/WallObjectDespawned.java b/runelite-api/src/main/java/net/runelite/api/events/WallObjectDespawned.java index 8a510a7bae..6ac609a46f 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WallObjectDespawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WallObjectDespawned.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where a {@link WallObject} on a {@link Tile} has been removed. */ @Data -public class WallObjectDespawned implements Event +public class WallObjectDespawned { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/WallObjectSpawned.java b/runelite-api/src/main/java/net/runelite/api/events/WallObjectSpawned.java index a1da8542a4..dc22e07f7d 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WallObjectSpawned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WallObjectSpawned.java @@ -32,7 +32,7 @@ import lombok.Data; * An event where a {@link WallObject} is added to a {@link Tile}. */ @Data -public class WallObjectSpawned implements Event +public class WallObjectSpawned { /** * The affected tile. diff --git a/runelite-api/src/main/java/net/runelite/api/events/WidgetLoaded.java b/runelite-api/src/main/java/net/runelite/api/events/WidgetLoaded.java index 5f337603d1..c71bc41634 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WidgetLoaded.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WidgetLoaded.java @@ -30,7 +30,7 @@ import lombok.Data; * An event where a {@link net.runelite.api.widgets.Widget} has been loaded. */ @Data -public class WidgetLoaded implements Event +public class WidgetLoaded { /** * The group ID of the loaded widget. diff --git a/runelite-api/src/main/java/net/runelite/api/events/WidgetMenuOptionClicked.java b/runelite-api/src/main/java/net/runelite/api/events/WidgetMenuOptionClicked.java index 492767d832..c0e3596085 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WidgetMenuOptionClicked.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WidgetMenuOptionClicked.java @@ -31,7 +31,7 @@ import lombok.Data; * A MenuManager widget menu was clicked. This event is NOT fired for non-MenuManager menu options */ @Data -public class WidgetMenuOptionClicked implements Event +public class WidgetMenuOptionClicked { /** * The clicked menu option. diff --git a/runelite-api/src/main/java/net/runelite/api/events/WidgetPositioned.java b/runelite-api/src/main/java/net/runelite/api/events/WidgetPositioned.java index 4835db0aa4..6f58e9fe07 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WidgetPositioned.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WidgetPositioned.java @@ -28,7 +28,7 @@ package net.runelite.api.events; * An event where the position of a {@link net.runelite.api.widgets.Widget} * relative to its parent has changed. */ -public class WidgetPositioned implements Event +public class WidgetPositioned { public static final WidgetPositioned INSTANCE = new WidgetPositioned(); diff --git a/runelite-api/src/main/java/net/runelite/api/events/WidgetPressed.java b/runelite-api/src/main/java/net/runelite/api/events/WidgetPressed.java index b7fd18af4c..cabceb377b 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WidgetPressed.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WidgetPressed.java @@ -31,7 +31,7 @@ import lombok.Data; * An event where a draggable widget has been pressed. */ @Data -public class WidgetPressed implements Event +public class WidgetPressed { public static final WidgetPressed INSTANCE = new WidgetPressed(); diff --git a/runelite-api/src/main/java/net/runelite/api/events/WorldListLoad.java b/runelite-api/src/main/java/net/runelite/api/events/WorldListLoad.java index ab0d05e5c1..c45f4cf1ae 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WorldListLoad.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WorldListLoad.java @@ -31,7 +31,7 @@ import lombok.Value; * Event when the world list is loaded for the world switcher */ @Value -public class WorldListLoad implements Event +public class WorldListLoad { World[] worlds; } diff --git a/runelite-api/src/main/java/net/runelite/api/events/player/headicon/OverheadPrayerChanged.java b/runelite-api/src/main/java/net/runelite/api/events/player/headicon/OverheadPrayerChanged.java deleted file mode 100644 index 687a17f6dd..0000000000 --- a/runelite-api/src/main/java/net/runelite/api/events/player/headicon/OverheadPrayerChanged.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.runelite.api.events.player.headicon; - -import lombok.Data; -import net.runelite.api.HeadIcon; -import net.runelite.api.Player; -import net.runelite.api.events.Event; - -@Data -public class OverheadPrayerChanged implements Event -{ - private final Player player; - - private final HeadIcon oldHeadIcon; - - private final HeadIcon newHeadIcon; - -} diff --git a/runelite-api/src/main/java/net/runelite/api/events/player/headicon/PlayerSkullChanged.java b/runelite-api/src/main/java/net/runelite/api/events/player/headicon/PlayerSkullChanged.java deleted file mode 100644 index b66c558888..0000000000 --- a/runelite-api/src/main/java/net/runelite/api/events/player/headicon/PlayerSkullChanged.java +++ /dev/null @@ -1,17 +0,0 @@ -package net.runelite.api.events.player.headicon; - -import lombok.Data; -import net.runelite.api.Player; -import net.runelite.api.SkullIcon; -import net.runelite.api.events.Event; - -@Data -public class PlayerSkullChanged implements Event -{ - private final Player player; - - private final SkullIcon oldSkullIcon; - - private final SkullIcon newSkullIcon; - -} diff --git a/runelite-api/src/main/java/net/runelite/api/kit/KitType.java b/runelite-api/src/main/java/net/runelite/api/kit/KitType.java index da8f66f46b..17d58ca3dc 100644 --- a/runelite-api/src/main/java/net/runelite/api/kit/KitType.java +++ b/runelite-api/src/main/java/net/runelite/api/kit/KitType.java @@ -26,7 +26,6 @@ package net.runelite.api.kit; import lombok.AllArgsConstructor; import lombok.Getter; -import com.openosrs.api.widgets.WidgetInfo; /** * Represents an equipment slot in a players composition. @@ -40,20 +39,20 @@ import com.openosrs.api.widgets.WidgetInfo; @AllArgsConstructor public enum KitType { - HEAD("Head", 0, WidgetInfo.EQUIPMENT_HELMET), - CAPE("Cape", 1, WidgetInfo.EQUIPMENT_CAPE), - AMULET("Amulet", 2, WidgetInfo.EQUIPMENT_AMULET), - WEAPON("Weapon", 3, WidgetInfo.EQUIPMENT_WEAPON), - TORSO("Torso", 4, WidgetInfo.EQUIPMENT_BODY), - SHIELD("Shield", 5, WidgetInfo.EQUIPMENT_SHIELD), - ARMS("Arms", 6, null), - LEGS("Legs", 7, WidgetInfo.EQUIPMENT_LEGS), - HAIR("Hair", 8, null), - HANDS("Hands", 9, WidgetInfo.EQUIPMENT_GLOVES), - BOOTS("Boots", 10, WidgetInfo.EQUIPMENT_BOOTS), - JAW("Jaw", 11, null), - RING("Ring", 12, WidgetInfo.EQUIPMENT_RING), - AMMUNITION("Ammo", 13, WidgetInfo.EQUIPMENT_AMMO); + HEAD("Head", 0), + CAPE("Cape", 1), + AMULET("Amulet", 2), + WEAPON("Weapon", 3), + TORSO("Torso", 4), + SHIELD("Shield", 5), + ARMS("Arms", 6), + LEGS("Legs", 7), + HAIR("Hair", 8), + HANDS("Hands", 9), + BOOTS("Boots", 10), + JAW("Jaw", 11), + RING("Ring", 12), + AMMUNITION("Ammo", 13); private final String name; @@ -61,6 +60,4 @@ public enum KitType * Gets the raw equipment index for use in {PlayerAppearance#getEquipmentIds()}. */ private final int index; - - private final WidgetInfo widgetInfo; } \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java b/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java index a061123d82..415661f6bf 100644 --- a/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java +++ b/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java @@ -32,7 +32,7 @@ import java.util.stream.Collectors; import net.runelite.api.Client; import net.runelite.api.QueryResults; import net.runelite.api.widgets.Widget; -import com.openosrs.api.widgets.WidgetInfo; +import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetItem; public class ShopItemQuery extends WidgetItemQuery @@ -50,7 +50,7 @@ public class ShopItemQuery extends WidgetItemQuery private Collection getShopItems(Client client) { Collection widgetItems = new ArrayList<>(); - Widget shop = client.getWidget(WidgetInfo.SHOP_ITEMS_CONTAINER); + Widget shop = client.getWidget(300, 2); if (shop != null && !shop.isHidden()) { Widget[] children = shop.getDynamicChildren(); diff --git a/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java b/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java index 88af60539d..8430967ed5 100644 --- a/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java +++ b/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java @@ -1,27 +1,33 @@ package com.openosrs.client; -import com.openosrs.client.game.NPCStats; - import java.io.File; +import java.io.IOException; +import java.util.Properties; import java.util.UUID; public class OpenOSRS { public static final File OPENOSRS_DIR = new File(System.getProperty("user.home"), ".openosrs"); public static final File EXTERNALPLUGIN_DIR = new File(OPENOSRS_DIR, "plugins"); - public static final String SYSTEM_VERSION = "0.0.1"; + public static final String SYSTEM_VERSION; public static String uuid = UUID.randomUUID().toString(); - public static void init() + static { + Properties properties = new Properties(); try { - NPCStats.loadStats(); + properties.load(OpenOSRS.class.getResourceAsStream("/openosrs.properties")); } - catch (Exception e) + catch (IOException e) { e.printStackTrace(); } + SYSTEM_VERSION = properties.getProperty("oprs.version", "0.0.0"); + } + + public static void preload() + { } } diff --git a/runelite-client/src/main/java/com/openosrs/client/config/OpenOSRSConfig.java b/runelite-client/src/main/java/com/openosrs/client/config/OpenOSRSConfig.java index 9c416f5708..29d8d6712b 100644 --- a/runelite-client/src/main/java/com/openosrs/client/config/OpenOSRSConfig.java +++ b/runelite-client/src/main/java/com/openosrs/client/config/OpenOSRSConfig.java @@ -35,7 +35,7 @@ import net.runelite.client.config.ConfigItem; import net.runelite.client.config.Keybind; import net.runelite.client.config.Range; import net.runelite.client.config.Units; -import com.openosrs.client.plugins.ExternalPluginManager; +import net.runelite.client.plugins.OPRSExternalPluginManager; @ConfigGroup("openosrs") public interface OpenOSRSConfig extends Config @@ -136,7 +136,7 @@ public interface OpenOSRSConfig extends Config ) default String getExternalRepositories() { - return ExternalPluginManager.DEFAULT_PLUGIN_REPOS; + return OPRSExternalPluginManager.DEFAULT_PLUGIN_REPOS; } @ConfigItem( diff --git a/runelite-client/src/main/java/com/openosrs/client/events/ExternalPluginChanged.java b/runelite-client/src/main/java/com/openosrs/client/events/ExternalPluginChanged.java index c7bc255929..19640710f2 100644 --- a/runelite-client/src/main/java/com/openosrs/client/events/ExternalPluginChanged.java +++ b/runelite-client/src/main/java/com/openosrs/client/events/ExternalPluginChanged.java @@ -25,11 +25,10 @@ package com.openosrs.client.events; import lombok.Data; -import net.runelite.api.events.Event; import net.runelite.client.plugins.Plugin; @Data -public class ExternalPluginChanged implements Event +public class ExternalPluginChanged { private final String pluginId; private final Plugin plugin; diff --git a/runelite-client/src/main/java/com/openosrs/client/events/ExternalPluginsLoaded.java b/runelite-client/src/main/java/com/openosrs/client/events/ExternalPluginsLoaded.java index 75f3910d81..8b1f718ef7 100644 --- a/runelite-client/src/main/java/com/openosrs/client/events/ExternalPluginsLoaded.java +++ b/runelite-client/src/main/java/com/openosrs/client/events/ExternalPluginsLoaded.java @@ -25,8 +25,7 @@ package com.openosrs.client.events; import lombok.Data; -import net.runelite.api.events.Event; @Data -public class ExternalPluginsLoaded implements Event +public class ExternalPluginsLoaded {} diff --git a/runelite-client/src/main/java/com/openosrs/client/events/ExternalRepositoryChanged.java b/runelite-client/src/main/java/com/openosrs/client/events/ExternalRepositoryChanged.java index ff4e46c795..712601f30a 100644 --- a/runelite-client/src/main/java/com/openosrs/client/events/ExternalRepositoryChanged.java +++ b/runelite-client/src/main/java/com/openosrs/client/events/ExternalRepositoryChanged.java @@ -25,10 +25,9 @@ package com.openosrs.client.events; import lombok.Data; -import net.runelite.api.events.Event; @Data -public class ExternalRepositoryChanged implements Event +public class ExternalRepositoryChanged { private final String owner; private final boolean added; diff --git a/runelite-client/src/main/java/com/openosrs/client/game/NPCStats.java b/runelite-client/src/main/java/com/openosrs/client/game/NPCStats.java deleted file mode 100644 index 93071976a7..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/game/NPCStats.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2019, TheStonedTurtle - * 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 com.openosrs.client.game; - -import com.google.common.collect.ImmutableMap; -import com.google.gson.TypeAdapter; -import com.google.gson.stream.JsonReader; -import com.google.gson.stream.JsonWriter; -import java.io.IOException; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; - -import lombok.Builder; -import lombok.Value; - -@Value -@Builder(builderClassName = "Builder") -public class NPCStats -{ - public static ImmutableMap statsMap; - - String name; - - int hitpoints; - int combatLevel; - int slayerLevel; - int attackSpeed; - - int attackLevel; - int strengthLevel; - int defenceLevel; - int rangeLevel; - int magicLevel; - - int stab; - int slash; - int crush; - int range; - int magic; - - int stabDef; - int slashDef; - int crushDef; - int rangeDef; - int magicDef; - - int bonusAttack; - int bonusStrength; - int bonusRangeStrength; - int bonusMagicDamage; - - boolean poisonImmune; - boolean venomImmune; - - boolean dragon; - boolean demon; - boolean undead; - - /** - * Based off the formula found here: http://services.runescape.com/m=forum/c=PLuJ4cy6gtA/forums.ws?317,318,712,65587452,209,337584542#209 - * - * @return bonus XP modifier - */ - public double calculateXpModifier() - { - final double averageLevel = Math.floor((attackLevel + strengthLevel + defenceLevel + hitpoints) / 4); - final double averageDefBonus = Math.floor((stabDef + slashDef + crushDef) / 3); - - return (1 + Math.floor(averageLevel * (averageDefBonus + bonusStrength + bonusAttack) / 5120) / 40); - } - - // Because this class is here we can't add the TypeAdapter to gson (easily) - // doesn't mean we can't use one to do it a bit quicker - public static final TypeAdapter NPC_STATS_TYPE_ADAPTER = new TypeAdapter() - { - @Override - public void write(JsonWriter out, NPCStats value) - { - throw new UnsupportedOperationException("Not supported"); - } - - @Override - public NPCStats read(JsonReader in) throws IOException - { - in.beginObject(); - NPCStats.Builder builder = NPCStats.builder(); - - // Name is the only one that's guaranteed - in.skipValue(); - builder.name(in.nextString()); - - while (in.hasNext()) - { - switch (in.nextName()) - { - case "hitpoints": - builder.hitpoints(in.nextInt()); - break; - case "combatLevel": - builder.combatLevel(in.nextInt()); - break; - case "slayerLevel": - builder.slayerLevel(in.nextInt()); - break; - case "attackSpeed": - builder.attackSpeed(in.nextInt()); - break; - case "attackLevel": - builder.attackLevel(in.nextInt()); - break; - case "strengthLevel": - builder.strengthLevel(in.nextInt()); - break; - case "defenceLevel": - builder.defenceLevel(in.nextInt()); - break; - case "rangeLevel": - builder.rangeLevel(in.nextInt()); - break; - case "magicLevel": - builder.magicLevel(in.nextInt()); - break; - case "stab": - builder.stab(in.nextInt()); - break; - case "slash": - builder.slash(in.nextInt()); - break; - case "crush": - builder.crush(in.nextInt()); - break; - case "range": - builder.range(in.nextInt()); - break; - case "magic": - builder.magic(in.nextInt()); - break; - case "stabDef": - builder.stabDef(in.nextInt()); - break; - case "slashDef": - builder.slashDef(in.nextInt()); - break; - case "crushDef": - builder.crushDef(in.nextInt()); - break; - case "rangeDef": - builder.rangeDef(in.nextInt()); - break; - case "magicDef": - builder.magicDef(in.nextInt()); - break; - case "bonusAttack": - builder.bonusAttack(in.nextInt()); - break; - case "bonusStrength": - builder.bonusStrength(in.nextInt()); - break; - case "bonusRangeStrength": - builder.bonusRangeStrength(in.nextInt()); - break; - case "bonusMagicDamage": - builder.bonusMagicDamage(in.nextInt()); - break; - case "poisonImmune": - builder.poisonImmune(in.nextBoolean()); - break; - case "venomImmune": - builder.venomImmune(in.nextBoolean()); - break; - case "dragon": - builder.dragon(in.nextBoolean()); - break; - case "demon": - builder.demon(in.nextBoolean()); - break; - case "undead": - builder.undead(in.nextBoolean()); - break; - } - } - - in.endObject(); - return builder.build(); - } - }; - - public static void loadStats() throws IOException - { - try (JsonReader reader = new JsonReader(new InputStreamReader(NPCStats.class.getResourceAsStream("npc_stats.json"), StandardCharsets.UTF_8))) - { - ImmutableMap.Builder builder = ImmutableMap.builderWithExpectedSize(2821); - reader.beginObject(); - - while (reader.hasNext()) - { - builder.put( - Integer.parseInt(reader.nextName()), - NPCStats.NPC_STATS_TYPE_ADAPTER.read(reader) - ); - } - - reader.endObject(); - statsMap = builder.build(); - } - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginClasspath.java b/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginClasspath.java deleted file mode 100644 index 0e0e72842b..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginClasspath.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.openosrs.client.plugins; - -import org.pf4j.DevelopmentPluginClasspath; - -class ExternalPluginClasspath extends DevelopmentPluginClasspath -{ - static final String GRADLE_DEPS_PATH = "build/deps"; - - ExternalPluginClasspath() - { - addJarsDirectories(GRADLE_DEPS_PATH); - } -} diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginFileFilter.java b/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginFileFilter.java deleted file mode 100644 index 39b261d861..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginFileFilter.java +++ /dev/null @@ -1,61 +0,0 @@ -package com.openosrs.client.plugins; - -import java.io.File; -import java.io.FileFilter; -import java.util.Arrays; -import java.util.List; - -/** - * Determines whether a {@link File} is an external plugin folder. To be considered a plugin a folder must: - *

- * * Must not be a blacklisted name - * * Have a {@code .gradle.kts} file in the root named after the folder - * * Have a {@code MANIFEST.MF} located at {@code build/tmp/jar/MANIFEST.MF} - */ -public class ExternalPluginFileFilter implements FileFilter -{ - private static final List blacklist = Arrays.asList( - ".git", - "build", - "target", - "release" - ); - - private static final List buildFiles = Arrays.asList( - "%s.gradle.kts", - "%s.gradle" - ); - - @Override - public boolean accept(File pathName) - { - // Check if this path looks like a plugin development directory - if (!pathName.isDirectory()) - { - return false; - } - - String dirName = pathName.getName(); - if (blacklist.contains(dirName)) - { - return false; - } - - // Check if the plugin directory has a MANIFEST.MF which si required for loading - if (!new File(pathName, ExternalPluginManager.DEVELOPMENT_MANIFEST_PATH).exists()) - { - return false; - } - - // By convention plugins their directory is $name and they have a $name.gradle.kts or $name.gradle file in their root - for (String buildFile : buildFiles) - { - if (new File(pathName, String.format(buildFile, dirName)).exists()) - { - return true; - } - } - - return false; - } -} diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/Plugin.java b/runelite-client/src/main/java/com/openosrs/client/plugins/Plugin.java deleted file mode 100644 index 81e661de09..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/Plugin.java +++ /dev/null @@ -1,9 +0,0 @@ -package com.openosrs.client.plugins; - -import com.google.inject.Injector; -import org.pf4j.ExtensionPoint; - -public class Plugin extends net.runelite.client.plugins.Plugin implements ExtensionPoint -{ - public Injector injector; -} diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/OpenOSRSPlugin.java b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/OpenOSRSPlugin.java index 4ad7f21184..54e540d0f6 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/OpenOSRSPlugin.java +++ b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/OpenOSRSPlugin.java @@ -27,8 +27,8 @@ package com.openosrs.client.plugins.openosrs; import ch.qos.logback.classic.Logger; -import com.openosrs.client.plugins.openosrs.externals.ExternalPluginManagerPanel; import com.openosrs.client.config.OpenOSRSConfig; +import com.openosrs.client.plugins.openosrs.externals.ExternalPluginManagerPanel; import java.awt.image.BufferedImage; import javax.inject.Inject; import javax.inject.Singleton; @@ -78,6 +78,10 @@ public class OpenOSRSPlugin extends Plugin @Override public void hotkeyPressed() { + if (client == null) + { + return; + } detach = !detach; client.setOculusOrbState(detach ? 1 : 0); client.setOculusOrbNormalSpeed(detach ? 36 : 12); diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java index 43211002e5..57ba0b7bd1 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java +++ b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java @@ -1,6 +1,6 @@ package com.openosrs.client.plugins.openosrs.externals; -import com.openosrs.client.plugins.ExternalPluginManager; +import net.runelite.client.plugins.OPRSExternalPluginManager; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -46,12 +46,12 @@ public class ExternalPluginManagerPanel extends PluginPanel ADD_HOVER_ICON_GH = new ImageIcon(ImageUtil.alphaOffset(addIconGh, 0.53f)); } - private final ExternalPluginManager externalPluginManager; + private final OPRSExternalPluginManager externalPluginManager; private final ScheduledExecutorService executor; private final EventBus eventBus; @Inject - private ExternalPluginManagerPanel(ExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) + private ExternalPluginManagerPanel(OPRSExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) { super(false); @@ -134,7 +134,7 @@ public class ExternalPluginManagerPanel extends PluginPanel return; } - if (ExternalPluginManager.testGHRepository(owner.getText(), name.getText())) + if (OPRSExternalPluginManager.testGHRepository(owner.getText(), name.getText())) { JOptionPane.showMessageDialog(ClientUI.getFrame(), "This doesn't appear to be a valid repository.", "Error!", JOptionPane.ERROR_MESSAGE); @@ -222,7 +222,7 @@ public class ExternalPluginManagerPanel extends PluginPanel return; } - if (ExternalPluginManager.testRepository(urlActual)) + if (OPRSExternalPluginManager.testRepository(urlActual)) { JOptionPane.showMessageDialog(ClientUI.getFrame(), "This doesn't appear to be a valid repository.", "Error!", JOptionPane.ERROR_MESSAGE); diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java index fa3af31ca2..f8c7377c63 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java +++ b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java @@ -1,11 +1,10 @@ package com.openosrs.client.plugins.openosrs.externals; -import com.openosrs.client.plugins.ExternalPluginManager; +import net.runelite.client.plugins.OPRSExternalPluginManager; import com.google.gson.JsonSyntaxException; import com.openosrs.client.events.ExternalPluginChanged; import com.openosrs.client.events.ExternalRepositoryChanged; -import com.openosrs.client.util.DeferredDocumentChangedListener; -import com.openosrs.client.util.ImageUtil; +import net.runelite.client.util.DeferredDocumentChangedListener; import com.openosrs.client.util.SwingUtil; import java.awt.BorderLayout; import java.awt.Color; @@ -45,6 +44,7 @@ import net.runelite.client.ui.FontManager; import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.IconTextField; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; +import net.runelite.client.util.ImageUtil; import org.pf4j.update.PluginInfo; import org.pf4j.update.UpdateManager; import org.pf4j.update.UpdateRepository; @@ -82,7 +82,7 @@ public class PluginsPanel extends JPanel DELETE_HOVER_ICON_GRAY = new ImageIcon(ImageUtil.alphaOffset(ImageUtil.grayscaleImage(deleteImg), 0.53f)); } - private final ExternalPluginManager externalPluginManager; + private final OPRSExternalPluginManager externalPluginManager; private final UpdateManager updateManager; private final ScheduledExecutorService executor; private final EventBus eventBus; @@ -97,7 +97,7 @@ public class PluginsPanel extends JPanel private JComboBox filterComboBox; private Set deps; - PluginsPanel(ExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) + PluginsPanel(OPRSExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) { this.externalPluginManager = externalPluginManager; this.updateManager = externalPluginManager.getUpdateManager(); diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java index 9a1171d726..cffc8d989f 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java +++ b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java @@ -1,8 +1,7 @@ package com.openosrs.client.plugins.openosrs.externals; -import com.openosrs.client.plugins.ExternalPluginManager; +import net.runelite.client.plugins.OPRSExternalPluginManager; import com.openosrs.client.ui.JMultilineLabel; -import com.openosrs.client.util.ImageUtil; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -19,6 +18,7 @@ import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; +import net.runelite.client.util.ImageUtil; import net.runelite.client.util.LinkBrowser; import org.pf4j.update.PluginInfo; import org.pf4j.update.UpdateRepository; @@ -53,7 +53,7 @@ public class RepositoryBox extends JPanel DISCORD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(discordImg, 0.53f)); } - RepositoryBox(ExternalPluginManager externalPluginManager, UpdateRepository updateRepository) + RepositoryBox(OPRSExternalPluginManager externalPluginManager, UpdateRepository updateRepository) { setLayout(new BorderLayout()); setBackground(ColorScheme.DARKER_GRAY_COLOR); diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryPanel.java b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryPanel.java index 1f8973aa88..6bd5181a51 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryPanel.java +++ b/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryPanel.java @@ -1,6 +1,6 @@ package com.openosrs.client.plugins.openosrs.externals; -import com.openosrs.client.plugins.ExternalPluginManager; +import net.runelite.client.plugins.OPRSExternalPluginManager; import com.openosrs.client.events.ExternalRepositoryChanged; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; @@ -18,11 +18,11 @@ public class RepositoryPanel extends JPanel @Inject public EventBus eventBus; - private final ExternalPluginManager externalPluginManager; + private final OPRSExternalPluginManager externalPluginManager; private final GridBagConstraints c = new GridBagConstraints(); - RepositoryPanel(ExternalPluginManager externalPluginManager, EventBus eventBus) + RepositoryPanel(OPRSExternalPluginManager externalPluginManager, EventBus eventBus) { this.externalPluginManager = externalPluginManager; diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/components/InfoPanel.java b/runelite-client/src/main/java/com/openosrs/client/ui/components/InfoPanel.java index 07efae32cb..7d5043bd55 100644 --- a/runelite-client/src/main/java/com/openosrs/client/ui/components/InfoPanel.java +++ b/runelite-client/src/main/java/com/openosrs/client/ui/components/InfoPanel.java @@ -24,6 +24,7 @@ */ package com.openosrs.client.ui.components; +import com.openosrs.client.OpenOSRS; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; @@ -88,7 +89,7 @@ public class InfoPanel extends JPanel c.weighty = 0; // OpenOSRS version - this.add(createPanelTextButton("OpenOSRS Version: " + RuneLiteProperties.getLauncherVersion()), c); + this.add(createPanelTextButton("OpenOSRS Version: " + OpenOSRS.SYSTEM_VERSION), c); c.gridy++; final JLabel logsFolder = createPanelButton("Open logs folder", null, () -> LinkBrowser.openLocalFile(LOGS_DIR)); diff --git a/runelite-client/src/main/java/com/openosrs/client/util/ColorUtil.java b/runelite-client/src/main/java/com/openosrs/client/util/ColorUtil.java deleted file mode 100644 index 57f68c4703..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/util/ColorUtil.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.openosrs.client.util; - -import java.awt.Color; - -public class ColorUtil extends net.runelite.client.util.ColorUtil -{ - /** - * Modifies the alpha component on a Color - * - * @param color The color to set the alpha value on - * @param alpha The alpha value to set on the color - * @return color - */ - public static int setAlphaComponent(Color color, int alpha) - { - return setAlphaComponent(color.getRGB(), alpha); - } - - /** - * Modifies the alpha component on a Color - * - * @param color The color to set the alpha value on - * @param alpha The alpha value to set on the color - * @return color - */ - public static int setAlphaComponent(int color, int alpha) - { - if (alpha < 0 || alpha > 255) - { - throw new IllegalArgumentException("alpha must be between 0 and 255."); - } - return (color & 0x00ffffff) | (alpha << 24); - } -} diff --git a/runelite-client/src/main/java/com/openosrs/client/util/DeferredDocumentChangedListener.java b/runelite-client/src/main/java/com/openosrs/client/util/DeferredDocumentChangedListener.java deleted file mode 100644 index b9728e03be..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/util/DeferredDocumentChangedListener.java +++ /dev/null @@ -1,58 +0,0 @@ -package com.openosrs.client.util; - -import java.util.ArrayList; -import java.util.List; -import javax.swing.Timer; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.event.DocumentEvent; -import javax.swing.event.DocumentListener; - -public class DeferredDocumentChangedListener implements DocumentListener -{ - private final Timer timer; - private final List listeners; - - public DeferredDocumentChangedListener() - { - listeners = new ArrayList<>(25); - timer = new Timer(200, e -> fireStateChanged()); - timer.setRepeats(false); - } - - public void addChangeListener(ChangeListener listener) - { - listeners.add(listener); - } - - private void fireStateChanged() - { - if (!listeners.isEmpty()) - { - ChangeEvent evt = new ChangeEvent(this); - for (ChangeListener listener : listeners) - { - listener.stateChanged(evt); - } - } - } - - @Override - public void insertUpdate(DocumentEvent e) - { - timer.restart(); - } - - @Override - public void removeUpdate(DocumentEvent e) - { - timer.restart(); - } - - @Override - public void changedUpdate(DocumentEvent e) - { - timer.restart(); - } - -} diff --git a/runelite-client/src/main/java/com/openosrs/client/util/ImageUtil.java b/runelite-client/src/main/java/com/openosrs/client/util/ImageUtil.java deleted file mode 100644 index 3282e61c98..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/util/ImageUtil.java +++ /dev/null @@ -1,59 +0,0 @@ -package com.openosrs.client.util; - -import java.awt.Color; -import java.awt.image.BufferedImage; -import java.awt.image.WritableRaster; -import java.util.function.Predicate; - -public class ImageUtil extends net.runelite.client.util.ImageUtil -{ - /** - * Recolors pixels of the given image with the given color based on a given recolor condition - * predicate. - * - * @param image The image which should have its non-transparent pixels recolored. - * @param color The color with which to recolor pixels. - * @param recolorCondition The condition on which to recolor pixels with the given color. - * @return The given image with all pixels fulfilling the recolor condition predicate - * set to the given color. - */ - public static BufferedImage recolorImage(final BufferedImage image, final Color color, final Predicate recolorCondition) - { - final BufferedImage recoloredImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); - for (int x = 0; x < recoloredImage.getWidth(); x++) - { - for (int y = 0; y < recoloredImage.getHeight(); y++) - { - final Color pixelColor = new Color(image.getRGB(x, y), true); - if (!recolorCondition.test(pixelColor)) - { - recoloredImage.setRGB(x, y, image.getRGB(x, y)); - continue; - } - - recoloredImage.setRGB(x, y, color.getRGB()); - } - } - return recoloredImage; - } - - public static BufferedImage recolorImage(BufferedImage image, final Color color) - { - int width = image.getWidth(); - int height = image.getHeight(); - WritableRaster raster = image.getRaster(); - - for (int xx = 0; xx < width; xx++) - { - for (int yy = 0; yy < height; yy++) - { - int[] pixels = raster.getPixel(xx, yy, (int[]) null); - pixels[0] = color.getRed(); - pixels[1] = color.getGreen(); - pixels[2] = color.getBlue(); - raster.setPixel(xx, yy, pixels); - } - } - return image; - } -} 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 6630da1ad3..d0222a198f 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -70,6 +70,7 @@ import net.runelite.client.game.ItemManager; import net.runelite.client.game.LootManager; import net.runelite.client.game.chatbox.ChatboxPanelManager; import net.runelite.client.menus.MenuManager; +import net.runelite.client.plugins.OPRSExternalPluginManager; import net.runelite.client.rs.ClientLoader; import net.runelite.client.rs.ClientUpdateCheckMode; import net.runelite.client.ui.ClientUI; @@ -114,7 +115,7 @@ public class RuneLite private ExternalPluginManager externalPluginManager; @Inject - private com.openosrs.client.plugins.ExternalPluginManager oprsExternalPluginManager; + private OPRSExternalPluginManager oprsExternalPluginManager; @Inject private EventBus eventBus; @@ -241,6 +242,8 @@ public class RuneLite } }); + OpenOSRS.preload(); + OkHttpClient.Builder okHttpClientBuilder = RuneLiteAPI.CLIENT.newBuilder() .cache(new Cache(new File(CACHE_DIR, "okhttp"), MAX_OKHTTP_CACHE_SIZE)); @@ -286,8 +289,6 @@ public class RuneLite injector.getInstance(RuneLite.class).start(); - OpenOSRS.init(); - final long end = System.currentTimeMillis(); final RuntimeMXBean rb = ManagementFactory.getRuntimeMXBean(); final long uptime = rb.getUptime(); diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPf4jPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPf4jPluginManager.java similarity index 82% rename from runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPf4jPluginManager.java rename to runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPf4jPluginManager.java index e151ba3df2..e9f9f99127 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPf4jPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPf4jPluginManager.java @@ -1,4 +1,29 @@ -package com.openosrs.client.plugins; +/* + * Copyright (c) 2021, ThatGamerBlue + * 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; import com.openosrs.client.OpenOSRS; import java.io.Closeable; @@ -33,11 +58,11 @@ import org.pf4j.PluginWrapper; import org.pf4j.RuntimeMode; @Slf4j -class ExternalPf4jPluginManager extends DefaultPluginManager +class OPRSExternalPf4jPluginManager extends DefaultPluginManager { - private final ExternalPluginManager externalPluginManager; + private final OPRSExternalPluginManager externalPluginManager; - public ExternalPf4jPluginManager(ExternalPluginManager externalPluginManager) + public OPRSExternalPf4jPluginManager(OPRSExternalPluginManager externalPluginManager) { super(OpenOSRS.EXTERNALPLUGIN_DIR.toPath()); this.externalPluginManager = externalPluginManager; @@ -55,7 +80,7 @@ class ExternalPf4jPluginManager extends DefaultPluginManager // The superclass performs a find, which is slow in development mode since we're pointing // at a sources directory, which can have a lot of files. The external plugin template // will always output the manifest at the following location, so we can hardcode this path. - return pluginPath.resolve(ExternalPluginManager.DEVELOPMENT_MANIFEST_PATH); + return pluginPath.resolve(OPRSExternalPluginManager.DEVELOPMENT_MANIFEST_PATH); } return super.getManifestPath(pluginPath); @@ -78,7 +103,7 @@ class ExternalPf4jPluginManager extends DefaultPluginManager protected PluginLoader createPluginLoader() { return new CompoundPluginLoader() - .add(new BasePluginLoader(this, new ExternalPluginClasspath()), this::isDevelopment) + .add(new BasePluginLoader(this, new OPRSExternalPluginClasspath()), this::isDevelopment) .add(new JarPluginLoader(this), this::isNotDevelopment); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginClasspath.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginClasspath.java new file mode 100644 index 0000000000..a44aa7e8f7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginClasspath.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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; + +import org.pf4j.DevelopmentPluginClasspath; + +class OPRSExternalPluginClasspath extends DevelopmentPluginClasspath +{ + static final String GRADLE_DEPS_PATH = "build/deps"; + + OPRSExternalPluginClasspath() + { + addJarsDirectories(GRADLE_DEPS_PATH); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginFileFilter.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginFileFilter.java new file mode 100644 index 0000000000..01ace2846e --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginFileFilter.java @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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; + +import java.io.File; +import java.io.FileFilter; +import java.util.Arrays; +import java.util.List; + +/** + * Determines whether a {@link File} is an external plugin folder. To be considered a plugin a folder must: + *

+ * * Must not be a blacklisted name + * * Have a {@code .gradle.kts} file in the root named after the folder + * * Have a {@code MANIFEST.MF} located at {@code build/tmp/jar/MANIFEST.MF} + */ +public class OPRSExternalPluginFileFilter implements FileFilter +{ + private static final List blacklist = Arrays.asList( + ".git", + "build", + "target", + "release" + ); + + private static final List buildFiles = Arrays.asList( + "%s.gradle.kts", + "%s.gradle" + ); + + @Override + public boolean accept(File pathName) + { + // Check if this path looks like a plugin development directory + if (!pathName.isDirectory()) + { + return false; + } + + String dirName = pathName.getName(); + if (blacklist.contains(dirName)) + { + return false; + } + + // Check if the plugin directory has a MANIFEST.MF which si required for loading + if (!new File(pathName, OPRSExternalPluginManager.DEVELOPMENT_MANIFEST_PATH).exists()) + { + return false; + } + + // By convention plugins their directory is $name and they have a $name.gradle.kts or $name.gradle file in their root + for (String buildFile : buildFiles) + { + if (new File(pathName, String.format(buildFile, dirName)).exists()) + { + return true; + } + } + + return false; + } +} diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java similarity index 98% rename from runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginManager.java rename to runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java index add932f725..79ee6f1f53 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Owain van Brakel + * Copyright (c) 2021, ThatGamerBlue * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,7 +22,7 @@ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -package com.openosrs.client.plugins; +package net.runelite.client.plugins; import com.google.common.collect.Lists; import com.google.common.graph.GraphBuilder; @@ -75,9 +75,6 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; import net.runelite.client.events.ConfigChanged; -import net.runelite.client.plugins.PluginDescriptor; -import net.runelite.client.plugins.PluginInstantiationException; -import net.runelite.client.plugins.PluginManager; import net.runelite.client.ui.ClientUI; import org.jgroups.Message; import org.pf4j.DefaultPluginManager; @@ -92,9 +89,9 @@ import org.pf4j.update.UpdateRepository; @Slf4j @Singleton -public class ExternalPluginManager +public class OPRSExternalPluginManager { - public static final String DEFAULT_PLUGIN_REPOS = "OpenOSRS:https://raw.githubusercontent.com/zeruth/runelite-plugins-release/master/"; + public static final String DEFAULT_PLUGIN_REPOS = "OpenOSRS:https://raw.githubusercontent.com/open-osrs/plugins-v2/master/"; static final String DEVELOPMENT_MANIFEST_PATH = "build/tmp/jar/MANIFEST.MF"; public static ArrayList pluginClassLoaders = new ArrayList<>(); @@ -115,7 +112,7 @@ public class ExternalPluginManager private final boolean safeMode; @Inject - public ExternalPluginManager( + public OPRSExternalPluginManager( @Named("safeMode") final boolean safeMode, PluginManager pluginManager, OpenOSRSConfig openOSRSConfig, @@ -143,7 +140,7 @@ public class ExternalPluginManager private void initPluginManager() { - externalPluginManager = new ExternalPf4jPluginManager(this); + externalPluginManager = new OPRSExternalPf4jPluginManager(this); externalPluginManager.setSystemVersion(SYSTEM_VERSION); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java index a36a1f40e6..23a9dbf5ce 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java @@ -27,8 +27,9 @@ package net.runelite.client.plugins; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.Module; +import org.pf4j.ExtensionPoint; -public abstract class Plugin implements Module +public abstract class Plugin implements Module, ExtensionPoint { protected Injector injector; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java index 185a5e7ecc..60f6256048 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginToggleButton.java @@ -29,8 +29,8 @@ import java.awt.Dimension; import java.awt.image.BufferedImage; import javax.swing.ImageIcon; import javax.swing.JToggleButton; -import com.openosrs.client.util.ImageUtil; import net.runelite.client.ui.ColorScheme; +import net.runelite.client.util.ImageUtil; import net.runelite.client.util.SwingUtil; class PluginToggleButton extends JToggleButton 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 c59fa78798..2386bb8c45 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 @@ -27,6 +27,7 @@ package net.runelite.client.plugins.info; import com.google.common.base.MoreObjects; import com.google.inject.Inject; +import com.openosrs.client.OpenOSRS; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; @@ -135,8 +136,11 @@ public class InfoPanel extends PluginPanel final Font smallFont = FontManager.getRunescapeSmallFont(); - JLabel version = new JLabel(htmlLabel("RuneLite version: ", runeliteVersion)); - version.setFont(smallFont); + JLabel rlVersion = new JLabel(htmlLabel("RuneLite version: ", runeliteVersion)); + rlVersion.setFont(smallFont); + + JLabel oprsVersion = new JLabel(htmlLabel("OpenOSRS version: ", OpenOSRS.SYSTEM_VERSION)); + oprsVersion.setFont(smallFont); JLabel revision = new JLabel(); revision.setFont(smallFont); @@ -147,7 +151,7 @@ public class InfoPanel extends PluginPanel engineVer = String.format("Rev %d", client.getRevision()); } - revision.setText(htmlLabel("Oldschool revision: ", engineVer)); + revision.setText(htmlLabel("OldSchool revision: ", engineVer)); JLabel launcher = new JLabel(htmlLabel("Launcher version: ", MoreObjects .firstNonNull(RuneLiteProperties.getLauncherVersion(), "Unknown"))); @@ -170,7 +174,8 @@ public class InfoPanel extends PluginPanel } }); - versionPanel.add(version); + versionPanel.add(rlVersion); + versionPanel.add(oprsVersion); versionPanel.add(revision); versionPanel.add(launcher); versionPanel.add(Box.createGlue()); diff --git a/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java b/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java new file mode 100644 index 0000000000..64758b3acd --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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.util; + +import java.util.ArrayList; +import java.util.List; +import javax.swing.Timer; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; +import javax.swing.event.DocumentEvent; +import javax.swing.event.DocumentListener; + +public class DeferredDocumentChangedListener implements DocumentListener +{ + private final Timer timer; + private final List listeners; + + public DeferredDocumentChangedListener() + { + listeners = new ArrayList<>(25); + timer = new Timer(200, e -> fireStateChanged()); + timer.setRepeats(false); + } + + public void addChangeListener(ChangeListener listener) + { + listeners.add(listener); + } + + private void fireStateChanged() + { + if (!listeners.isEmpty()) + { + ChangeEvent evt = new ChangeEvent(this); + for (ChangeListener listener : listeners) + { + listener.stateChanged(evt); + } + } + } + + @Override + public void insertUpdate(DocumentEvent e) + { + timer.restart(); + } + + @Override + public void removeUpdate(DocumentEvent e) + { + timer.restart(); + } + + @Override + public void changedUpdate(DocumentEvent e) + { + timer.restart(); + } + +} 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 3efeabcfa4..488eb67108 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 @@ -34,10 +34,12 @@ import java.awt.image.BufferedImage; import java.awt.image.DirectColorModel; import java.awt.image.PixelGrabber; import java.awt.image.RescaleOp; +import java.awt.image.WritableRaster; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.function.Predicate; import javax.imageio.ImageIO; import javax.swing.GrayFilter; import lombok.extern.slf4j.Slf4j; @@ -524,4 +526,54 @@ public class ImageUtil return sprite; } + + /** + * Recolors pixels of the given image with the given color based on a given recolor condition + * predicate. + * + * @param image The image which should have its non-transparent pixels recolored. + * @param color The color with which to recolor pixels. + * @param recolorCondition The condition on which to recolor pixels with the given color. + * @return The given image with all pixels fulfilling the recolor condition predicate + * set to the given color. + */ + public static BufferedImage recolorImage(final BufferedImage image, final Color color, final Predicate recolorCondition) + { + final BufferedImage recoloredImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB); + for (int x = 0; x < recoloredImage.getWidth(); x++) + { + for (int y = 0; y < recoloredImage.getHeight(); y++) + { + final Color pixelColor = new Color(image.getRGB(x, y), true); + if (!recolorCondition.test(pixelColor)) + { + recoloredImage.setRGB(x, y, image.getRGB(x, y)); + continue; + } + + recoloredImage.setRGB(x, y, color.getRGB()); + } + } + return recoloredImage; + } + + public static BufferedImage recolorImage(BufferedImage image, final Color color) + { + int width = image.getWidth(); + int height = image.getHeight(); + WritableRaster raster = image.getRaster(); + + for (int xx = 0; xx < width; xx++) + { + for (int yy = 0; yy < height; yy++) + { + int[] pixels = raster.getPixel(xx, yy, (int[]) null); + pixels[0] = color.getRed(); + pixels[1] = color.getGreen(); + pixels[2] = color.getBlue(); + raster.setPixel(xx, yy, pixels); + } + } + return image; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java b/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java new file mode 100644 index 0000000000..877c98b3d2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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.util; + +import java.awt.Polygon; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.api.Varbits; +import net.runelite.api.WorldType; +import net.runelite.api.coords.WorldPoint; +import net.runelite.api.geometry.Cuboid; + +public class PvPUtil +{ + private static final Polygon NOT_WILDERNESS_BLACK_KNIGHTS = new Polygon( // this is black knights castle + new int[]{2994, 2995, 2996, 2996, 2994, 2994, 2997, 2998, 2998, 2999, 3000, 3001, 3002, 3003, 3004, 3005, 3005, + 3005, 3019, 3020, 3022, 3023, 3024, 3025, 3026, 3026, 3027, 3027, 3028, 3028, 3029, 3029, 3030, 3030, 3031, + 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3037}, + new int[]{3525, 3526, 3527, 3529, 3529, 3534, 3534, 3535, 3536, 3537, 3538, 3539, 3540, 3541, 3542, 3543, 3544, + 3545, 3545, 3546, 3546, 3545, 3544, 3543, 3543, 3542, 3541, 3540, 3539, 3537, 3536, 3535, 3534, 3533, 3532, + 3531, 3530, 3529, 3528, 3527, 3526, 3526, 3525}, + 43 + ); + private static final Cuboid MAIN_WILDERNESS_CUBOID = new Cuboid(2944, 3525, 0, 3391, 4351, 3); + private static final Cuboid GOD_WARS_WILDERNESS_CUBOID = new Cuboid(3008, 10112, 0, 3071, 10175, 3); + private static final Cuboid WILDERNESS_UNDERGROUND_CUBOID = new Cuboid(2944, 9920, 0, 3391, 10879, 3); + + /** + * Gets the wilderness level based on a world point + * Java reimplementation of clientscript 384 [proc,wilderness_level] + * + * @param point the point in the world to get the wilderness level for + * @return the int representing the wilderness level + */ + public static int getWildernessLevelFrom(WorldPoint point) + { + if (MAIN_WILDERNESS_CUBOID.contains(point)) + { + if (NOT_WILDERNESS_BLACK_KNIGHTS.contains(point.getX(), point.getY())) + { + return 0; + } + + return ((point.getY() - 3520) / 8) + 1; // calc(((coordz(coord) - (55 * 64)) / 8) + 1) + } + else if (GOD_WARS_WILDERNESS_CUBOID.contains(point)) + { + return ((point.getY() - 9920) / 8) - 1; // calc(((coordz(coord) - (155 * 64)) / 8) - 1) + } + else if (WILDERNESS_UNDERGROUND_CUBOID.contains(point)) + { + return ((point.getY() - 9920) / 8) + 1; // calc(((coordz(coord) - (155 * 64)) / 8) + 1) + } + return 0; + } + + /** + * Determines if another player is attackable based off of wilderness level and combat levels + * + * @param client The client of the local player + * @param player the player to determine attackability + * @return returns true if the player is attackable, false otherwise + */ + public static boolean isAttackable(Client client, Player player) + { + int wildernessLevel = 0; + + if (WorldType.isDeadmanWorld(client.getWorldType())) + { + return true; + } + if (WorldType.isPvpWorld(client.getWorldType())) + { + wildernessLevel += 15; + } + if (client.getVar(Varbits.IN_WILDERNESS) == 1) + { + wildernessLevel += getWildernessLevelFrom(client.getLocalPlayer().getWorldLocation()); + } + return wildernessLevel != 0 && Math.abs(client.getLocalPlayer().getCombatLevel() - player.getCombatLevel()) <= wildernessLevel; + } +} diff --git a/runelite-client/src/main/resources/com/openosrs/client/game/npc_stats.json b/runelite-client/src/main/resources/com/openosrs/client/game/npc_stats.json deleted file mode 100644 index 7838d269b1..0000000000 --- a/runelite-client/src/main/resources/com/openosrs/client/game/npc_stats.json +++ /dev/null @@ -1 +0,0 @@ -{"1":{"name":"Molanisk","hitpoints":52,"combatLevel":51,"slayerLevel":39,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":50,"rangeLevel":1,"stabDef":45,"slashDef":45,"crushDef":35,"rangeDef":55,"magicDef":30},"2":{"name":"Aberrant spectre","hitpoints":90,"combatLevel":96,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":90,"rangeLevel":1,"magicLevel":105,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":15},"3":{"name":"Aberrant spectre","hitpoints":90,"combatLevel":96,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":90,"rangeLevel":1,"magicLevel":105,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":15},"4":{"name":"Aberrant spectre","hitpoints":90,"combatLevel":96,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":90,"rangeLevel":1,"magicLevel":105,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":15},"5":{"name":"Aberrant spectre","hitpoints":90,"combatLevel":96,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":90,"rangeLevel":1,"magicLevel":105,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":15},"6":{"name":"Aberrant spectre","hitpoints":90,"combatLevel":96,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":90,"rangeLevel":1,"magicLevel":105,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":15},"7":{"name":"Aberrant spectre","hitpoints":90,"combatLevel":96,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":90,"rangeLevel":1,"magicLevel":105,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":15},"8":{"name":"Nechryael","hitpoints":105,"combatLevel":115,"slayerLevel":80,"attackSpeed":4,"attackLevel":97,"strengthLevel":97,"defenceLevel":105,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20},"10":{"name":"Death spawn","hitpoints":60,"combatLevel":46,"attackSpeed":4,"attackLevel":67,"strengthLevel":7,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20},"11":{"name":"Nechryael","hitpoints":105,"combatLevel":115,"slayerLevel":80,"attackSpeed":4,"attackLevel":97,"strengthLevel":97,"defenceLevel":105,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20},"26":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"27":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"28":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"29":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"30":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"31":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"32":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"33":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"34":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"35":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"36":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"37":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"38":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"39":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"40":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"41":{"name":"Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"42":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"43":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"44":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"45":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"46":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"47":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"48":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"49":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"50":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"51":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"52":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"53":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"54":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"55":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"56":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"57":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"58":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"59":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"60":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"61":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"62":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"63":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"64":{"name":"Zombie","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":8,"crushDef":12,"rangeDef":10,"magicDef":10,"bonusAttack":5,"bonusStrength":7},"65":{"name":"Zombie","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":8,"crushDef":12,"rangeDef":10,"magicDef":10,"bonusAttack":5,"bonusStrength":7},"66":{"name":"Zombie","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":8,"crushDef":12,"rangeDef":10,"magicDef":10,"bonusAttack":5,"bonusStrength":7},"67":{"name":"Zombie","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":8,"crushDef":12,"rangeDef":10,"magicDef":10,"bonusAttack":5,"bonusStrength":7},"68":{"name":"Zombie","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":19,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":8,"crushDef":12,"rangeDef":10,"magicDef":10,"bonusAttack":5,"bonusStrength":7},"69":{"name":"Summoned Zombie","hitpoints":22,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":9,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"70":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"71":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"72":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"73":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"74":{"name":"Skeleton","hitpoints":24,"combatLevel":21,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"75":{"name":"Skeleton","hitpoints":24,"combatLevel":21,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"76":{"name":"Skeleton","hitpoints":24,"combatLevel":21,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"77":{"name":"Skeleton","hitpoints":17,"combatLevel":25,"attackSpeed":4,"attackLevel":24,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"78":{"name":"Skeleton","hitpoints":17,"combatLevel":25,"attackSpeed":4,"attackLevel":24,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"79":{"name":"Skeleton","hitpoints":17,"combatLevel":25,"attackSpeed":4,"attackLevel":24,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"80":{"name":"Skeleton","hitpoints":17,"combatLevel":25,"attackSpeed":4,"attackLevel":24,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"81":{"name":"Skeleton","hitpoints":17,"combatLevel":25,"attackSpeed":4,"attackLevel":24,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"82":{"name":"Skeleton","hitpoints":59,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":35,"defenceLevel":36,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"83":{"name":"Skeleton","hitpoints":59,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":35,"defenceLevel":36,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"84":{"name":"Skeleton Mage","hitpoints":17,"combatLevel":16,"attackSpeed":4,"attackLevel":14,"strengthLevel":14,"defenceLevel":14,"rangeLevel":1,"magicLevel":18},"85":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"86":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"87":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"88":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"89":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"90":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"91":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"92":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"93":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"94":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"95":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"96":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"97":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"98":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"99":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"100":{"name":"Rock Crab","hitpoints":50,"combatLevel":13,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"101":{"name":"Rocks","hitpoints":50,"combatLevel":13,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"102":{"name":"Rock Crab","hitpoints":50,"combatLevel":13,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"103":{"name":"Rocks","hitpoints":50,"combatLevel":13,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"104":{"name":"Hellhound","hitpoints":116,"combatLevel":122,"attackSpeed":4,"attackLevel":105,"strengthLevel":104,"defenceLevel":102,"rangeLevel":1,"magicLevel":1},"105":{"name":"Hellhound","hitpoints":116,"combatLevel":122,"attackSpeed":4,"attackLevel":105,"strengthLevel":104,"defenceLevel":102,"rangeLevel":1,"magicLevel":1},"106":{"name":"Wolf","hitpoints":69,"combatLevel":64,"attackSpeed":4,"attackLevel":50,"strengthLevel":55,"defenceLevel":52,"rangeLevel":1,"magicLevel":1},"107":{"name":"White wolf","hitpoints":34,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":16,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"108":{"name":"White wolf","hitpoints":44,"combatLevel":38,"attackSpeed":4,"attackLevel":30,"strengthLevel":31,"defenceLevel":32,"rangeLevel":1,"magicLevel":1},"109":{"name":"Big Wolf","hitpoints":74,"combatLevel":73,"attackSpeed":4,"attackLevel":60,"strengthLevel":61,"defenceLevel":62,"rangeLevel":1,"magicLevel":1},"110":{"name":"Wolf","hitpoints":34,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":16,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"112":{"name":"Wild dog","hitpoints":62,"combatLevel":63,"attackSpeed":4,"attackLevel":53,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1},"113":{"name":"Wild dog","hitpoints":62,"combatLevel":63,"attackSpeed":4,"attackLevel":53,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1},"114":{"name":"Guard dog","hitpoints":49,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":36,"defenceLevel":37,"rangeLevel":1,"magicLevel":1},"115":{"name":"Big Wolf","hitpoints":74,"combatLevel":73,"attackSpeed":4,"attackLevel":60,"strengthLevel":61,"defenceLevel":62,"rangeLevel":1,"magicLevel":1},"116":{"name":"Wolf","hitpoints":34,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":16,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"117":{"name":"Wolf","hitpoints":34,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":16,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"130":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1},"131":{"name":"Guard dog","hitpoints":49,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":36,"defenceLevel":37,"rangeLevel":1,"magicLevel":1},"132":{"name":"Hobgoblin","hitpoints":62,"combatLevel":54,"attackSpeed":6,"attackLevel":45,"strengthLevel":43,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":1,"bonusAttack":8,"bonusStrength":10},"133":{"name":"Troll","hitpoints":120,"combatLevel":91,"attackSpeed":4,"attackLevel":50,"strengthLevel":100,"defenceLevel":50,"stabDef":25,"slashDef":25,"crushDef":40,"rangeDef":200,"magicDef":200,"bonusAttack":40,"bonusStrength":70},"134":{"name":"Huge spider","hitpoints":90,"combatLevel":81,"attackSpeed":4,"attackLevel":59,"strengthLevel":70,"defenceLevel":69,"rangeLevel":1,"magicLevel":1},"135":{"name":"Hellhound","hitpoints":116,"combatLevel":122,"attackSpeed":4,"attackLevel":105,"strengthLevel":104,"defenceLevel":102,"rangeLevel":1,"magicLevel":1},"136":{"name":"Ogre","hitpoints":60,"combatLevel":53,"attackSpeed":6,"attackLevel":43,"strengthLevel":43,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":20},"137":{"name":"Baby red dragon","hitpoints":65,"combatLevel":65,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":55,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"138":{"name":"Kalphite Soldier","hitpoints":90,"combatLevel":85,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":5,"rangeDef":50,"magicDef":50},"139":{"name":"Steel dragon","hitpoints":210,"combatLevel":246,"attackSpeed":4,"attackLevel":215,"strengthLevel":215,"defenceLevel":215,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"140":{"name":"Dagannoth","hitpoints":142,"combatLevel":135,"attackSpeed":4,"attackLevel":114,"strengthLevel":117,"defenceLevel":98,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50,"bonusAttack":50,"bonusRangeStrength":50},"141":{"name":"Tok-Xil","hitpoints":60,"combatLevel":135,"attackSpeed":4,"attackLevel":120,"strengthLevel":180,"defenceLevel":90,"rangeLevel":180,"magicLevel":90},"142":{"name":"Demon","hitpoints":79,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"143":{"name":"Rocnar","hitpoints":100,"combatLevel":97,"attackSpeed":6,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"bonusAttack":22,"bonusStrength":20},"231":{"name":"Wolf","hitpoints":34,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":16,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"232":{"name":"Jungle Wolf","hitpoints":69,"combatLevel":64,"attackSpeed":4,"attackLevel":50,"strengthLevel":55,"defenceLevel":52,"rangeLevel":1,"magicLevel":1},"239":{"name":"King Black Dragon","hitpoints":240,"combatLevel":276,"attackSpeed":4,"attackLevel":240,"strengthLevel":240,"defenceLevel":240,"rangeLevel":1,"magicLevel":240,"stabDef":70,"slashDef":90,"crushDef":90,"rangeDef":70,"magicDef":80},"240":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"241":{"name":"Baby blue dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"242":{"name":"Baby blue dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"243":{"name":"Baby blue dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"244":{"name":"Baby red dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"245":{"name":"Baby red dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"246":{"name":"Baby red dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"247":{"name":"Red dragon","hitpoints":140,"combatLevel":152,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"248":{"name":"Red dragon","hitpoints":140,"combatLevel":152,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"249":{"name":"Red dragon","hitpoints":140,"combatLevel":152,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"250":{"name":"Red dragon","hitpoints":140,"combatLevel":152,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"251":{"name":"Red dragon","hitpoints":140,"combatLevel":152,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"252":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"253":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"254":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"255":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"256":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"257":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"258":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"259":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"260":{"name":"Green dragon","hitpoints":75,"combatLevel":79,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"261":{"name":"Green dragon","hitpoints":75,"combatLevel":79,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"262":{"name":"Green dragon","hitpoints":75,"combatLevel":79,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"263":{"name":"Green dragon","hitpoints":75,"combatLevel":79,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"264":{"name":"Green dragon","hitpoints":75,"combatLevel":79,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"265":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"266":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"267":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"268":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"269":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"270":{"name":"Bronze dragon","hitpoints":122,"combatLevel":131,"attackSpeed":4,"attackLevel":112,"strengthLevel":112,"defenceLevel":112,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"271":{"name":"Bronze dragon","hitpoints":122,"combatLevel":131,"attackSpeed":4,"attackLevel":112,"strengthLevel":112,"defenceLevel":112,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"272":{"name":"Iron dragon","hitpoints":165,"combatLevel":189,"attackSpeed":4,"attackLevel":165,"strengthLevel":165,"defenceLevel":165,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"273":{"name":"Iron dragon","hitpoints":165,"combatLevel":189,"attackSpeed":4,"attackLevel":165,"strengthLevel":165,"defenceLevel":165,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"274":{"name":"Steel dragon","hitpoints":210,"combatLevel":246,"attackSpeed":4,"attackLevel":215,"strengthLevel":215,"defenceLevel":215,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"275":{"name":"Steel dragon","hitpoints":210,"combatLevel":246,"attackSpeed":4,"attackLevel":215,"strengthLevel":215,"defenceLevel":215,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"289":{"name":"Ghoul","hitpoints":50,"combatLevel":42,"attackSpeed":6,"attackLevel":30,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"290":{"name":"Dwarf","hitpoints":16,"combatLevel":10,"attackSpeed":5,"attackLevel":8,"strengthLevel":8,"defenceLevel":6,"rangeLevel":1,"magicLevel":1,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"291":{"name":"Chaos dwarf","hitpoints":61,"combatLevel":48,"attackSpeed":4,"attackLevel":38,"strengthLevel":42,"defenceLevel":28,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":34,"crushDef":25,"rangeDef":35,"magicDef":10,"bonusAttack":13,"bonusStrength":9},"292":{"name":"Dwarf","hitpoints":26,"combatLevel":20,"attackSpeed":6,"attackLevel":15,"strengthLevel":16,"defenceLevel":16,"rangeLevel":1,"magicLevel":1,"magicDef":5,"bonusAttack":11,"bonusStrength":9},"294":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"295":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"296":{"name":"Dwarf","hitpoints":16,"combatLevel":10,"attackSpeed":5,"attackLevel":8,"strengthLevel":8,"defenceLevel":6,"rangeLevel":1,"magicLevel":1,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"299":{"name":"Gunthor the brave","hitpoints":35,"combatLevel":29,"attackSpeed":6,"attackLevel":22,"strengthLevel":22,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":10,"rangeDef":11,"bonusAttack":8,"bonusStrength":13},"300":{"name":"Jailer","hitpoints":47,"combatLevel":47,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":79,"slashDef":63,"crushDef":47},"301":{"name":"Black Heather","hitpoints":37,"combatLevel":34,"attackSpeed":4,"attackLevel":32,"strengthLevel":26,"defenceLevel":27,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":8,"crushDef":10,"bonusAttack":9,"bonusStrength":9},"302":{"name":"Donny the lad","hitpoints":37,"combatLevel":34,"attackSpeed":4,"attackLevel":32,"strengthLevel":26,"defenceLevel":27,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":8,"crushDef":10,"bonusAttack":9,"bonusStrength":9},"303":{"name":"Speedy Keith","hitpoints":37,"combatLevel":34,"attackSpeed":4,"attackLevel":32,"strengthLevel":26,"defenceLevel":27,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":8,"crushDef":10,"bonusAttack":9,"bonusStrength":9},"304":{"name":"Salarin the twisted","hitpoints":70,"combatLevel":70,"attackSpeed":4,"attackLevel":58,"strengthLevel":58,"defenceLevel":62,"rangeLevel":1,"magicLevel":1},"319":{"name":"Corporeal Beast","hitpoints":2000,"combatLevel":785,"attackSpeed":4,"attackLevel":320,"strengthLevel":320,"defenceLevel":310,"rangeLevel":150,"magicLevel":350,"stabDef":25,"slashDef":200,"crushDef":100,"rangeDef":230,"magicDef":150,"bonusAttack":50,"poisonImmune":true,"venomImmune":true},"320":{"name":"Dark energy core","hitpoints":25,"combatLevel":75,"attackSpeed":2,"attackLevel":1,"strengthLevel":1,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10},"397":{"name":"Town Guard","hitpoints":22,"combatLevel":22,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":26,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5,"bonusAttack":6,"bonusStrength":10},"398":{"name":"Town Guard","hitpoints":22,"combatLevel":22,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":26,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5,"bonusAttack":6,"bonusStrength":10},"399":{"name":"Town Guard","hitpoints":22,"combatLevel":19,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5,"bonusAttack":6,"bonusStrength":10},"400":{"name":"Town Guard","hitpoints":22,"combatLevel":19,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5,"bonusAttack":6,"bonusStrength":10},"406":{"name":"Cave crawler","hitpoints":22,"combatLevel":23,"slayerLevel":10,"attackSpeed":4,"attackLevel":22,"strengthLevel":18,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"407":{"name":"Cave crawler","hitpoints":22,"combatLevel":23,"slayerLevel":10,"attackSpeed":4,"attackLevel":22,"strengthLevel":18,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"408":{"name":"Cave crawler","hitpoints":22,"combatLevel":23,"slayerLevel":10,"attackSpeed":4,"attackLevel":22,"strengthLevel":18,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"409":{"name":"Cave crawler","hitpoints":22,"combatLevel":23,"slayerLevel":10,"attackSpeed":4,"attackLevel":22,"strengthLevel":18,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"410":{"name":"Kurask","hitpoints":97,"combatLevel":106,"slayerLevel":70,"attackSpeed":4,"attackLevel":67,"strengthLevel":105,"defenceLevel":105,"rangeLevel":1,"magicLevel":1,"slashDef":20,"crushDef":20,"poisonImmune":true,"venomImmune":true},"411":{"name":"Kurask","hitpoints":97,"combatLevel":106,"slayerLevel":70,"attackSpeed":4,"attackLevel":67,"strengthLevel":105,"defenceLevel":105,"rangeLevel":1,"magicLevel":1,"slashDef":20,"crushDef":20,"poisonImmune":true,"venomImmune":true},"412":{"name":"Gargoyle","hitpoints":105,"combatLevel":111,"slayerLevel":75,"attackSpeed":4,"attackLevel":75,"strengthLevel":105,"defenceLevel":107,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":20,"magicDef":20},"413":{"name":"Gargoyle","hitpoints":105,"combatLevel":111,"slayerLevel":75,"attackSpeed":4,"attackLevel":75,"strengthLevel":105,"defenceLevel":107,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":20,"magicDef":20},"414":{"name":"Banshee","hitpoints":22,"combatLevel":23,"slayerLevel":15,"attackSpeed":4,"attackLevel":22,"strengthLevel":15,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"415":{"name":"Abyssal demon","hitpoints":150,"combatLevel":124,"slayerLevel":85,"attackSpeed":4,"attackLevel":97,"strengthLevel":67,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20},"416":{"name":"Abyssal demon","hitpoints":150,"combatLevel":124,"slayerLevel":85,"attackSpeed":4,"attackLevel":97,"strengthLevel":67,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20},"417":{"name":"Basilisk","hitpoints":75,"combatLevel":61,"slayerLevel":40,"attackSpeed":4,"attackLevel":30,"strengthLevel":45,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"magicDef":20},"418":{"name":"Basilisk","hitpoints":75,"combatLevel":61,"slayerLevel":40,"attackSpeed":4,"attackLevel":30,"strengthLevel":45,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"magicDef":20},"419":{"name":"Cockatrice","hitpoints":37,"combatLevel":37,"slayerLevel":25,"attackSpeed":4,"attackLevel":22,"strengthLevel":37,"defenceLevel":37,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"magicDef":10},"420":{"name":"Cockatrice","hitpoints":37,"combatLevel":37,"slayerLevel":25,"attackSpeed":4,"attackLevel":22,"strengthLevel":37,"defenceLevel":37,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"magicDef":10},"421":{"name":"Rockslug","hitpoints":27,"combatLevel":29,"slayerLevel":20,"attackSpeed":4,"attackLevel":22,"strengthLevel":27,"defenceLevel":27,"rangeLevel":1,"magicLevel":1},"422":{"name":"Rockslug","hitpoints":27,"combatLevel":29,"slayerLevel":20,"attackSpeed":4,"attackLevel":22,"strengthLevel":27,"defenceLevel":27,"rangeLevel":1,"magicLevel":1},"423":{"name":"Dust devil","hitpoints":105,"combatLevel":93,"slayerLevel":65,"attackSpeed":4,"attackLevel":105,"strengthLevel":70,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"427":{"name":"Turoth","hitpoints":81,"combatLevel":89,"slayerLevel":55,"attackSpeed":4,"attackLevel":58,"strengthLevel":88,"defenceLevel":88,"rangeLevel":1,"magicLevel":1,"slashDef":20,"crushDef":20},"428":{"name":"Turoth","hitpoints":79,"combatLevel":87,"slayerLevel":55,"attackSpeed":4,"attackLevel":56,"strengthLevel":86,"defenceLevel":86,"rangeLevel":1,"magicLevel":1,"slashDef":20,"crushDef":20},"429":{"name":"Turoth","hitpoints":77,"combatLevel":85,"slayerLevel":55,"attackSpeed":4,"attackLevel":54,"strengthLevel":84,"defenceLevel":84,"rangeLevel":1,"magicLevel":1,"slashDef":20,"crushDef":20},"430":{"name":"Turoth","hitpoints":76,"combatLevel":83,"slayerLevel":55,"attackSpeed":4,"attackLevel":53,"strengthLevel":83,"defenceLevel":83,"rangeLevel":1,"magicLevel":1,"slashDef":20,"crushDef":20},"433":{"name":"Pyrefiend","hitpoints":45,"combatLevel":43,"slayerLevel":30,"attackSpeed":4,"attackLevel":52,"strengthLevel":30,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10},"434":{"name":"Pyrefiend","hitpoints":45,"combatLevel":43,"slayerLevel":30,"attackSpeed":4,"attackLevel":52,"strengthLevel":30,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10},"435":{"name":"Pyrefiend","hitpoints":45,"combatLevel":43,"slayerLevel":30,"attackSpeed":4,"attackLevel":52,"strengthLevel":30,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10},"436":{"name":"Pyrefiend","hitpoints":45,"combatLevel":43,"slayerLevel":30,"attackSpeed":4,"attackLevel":52,"strengthLevel":30,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10},"437":{"name":"Jelly","hitpoints":75,"combatLevel":78,"slayerLevel":52,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":120,"rangeLevel":1,"magicLevel":45},"438":{"name":"Jelly","hitpoints":75,"combatLevel":78,"slayerLevel":52,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":120,"rangeLevel":1,"magicLevel":45},"439":{"name":"Jelly","hitpoints":75,"combatLevel":78,"slayerLevel":52,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":120,"rangeLevel":1,"magicLevel":45},"440":{"name":"Jelly","hitpoints":75,"combatLevel":78,"slayerLevel":52,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":120,"rangeLevel":1,"magicLevel":45},"441":{"name":"Jelly","hitpoints":75,"combatLevel":78,"slayerLevel":52,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":120,"rangeLevel":1,"magicLevel":45},"442":{"name":"Jelly","hitpoints":75,"combatLevel":78,"slayerLevel":52,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":120,"rangeLevel":1,"magicLevel":45},"443":{"name":"Infernal Mage","hitpoints":60,"combatLevel":66,"slayerLevel":45,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":60,"rangeLevel":1,"magicLevel":75,"magicDef":40},"444":{"name":"Infernal Mage","hitpoints":60,"combatLevel":66,"slayerLevel":45,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":60,"rangeLevel":1,"magicLevel":75,"magicDef":40},"445":{"name":"Infernal Mage","hitpoints":60,"combatLevel":66,"slayerLevel":45,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":60,"rangeLevel":1,"magicLevel":75,"magicDef":40},"446":{"name":"Infernal Mage","hitpoints":60,"combatLevel":66,"slayerLevel":45,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":60,"rangeLevel":1,"magicLevel":75,"magicDef":40},"447":{"name":"Infernal Mage","hitpoints":60,"combatLevel":66,"slayerLevel":45,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":60,"rangeLevel":1,"magicLevel":75,"magicDef":40},"448":{"name":"Crawling Hand","hitpoints":16,"combatLevel":8,"slayerLevel":5,"attackSpeed":4,"attackLevel":8,"strengthLevel":4,"defenceLevel":4,"rangeLevel":1,"magicLevel":1},"449":{"name":"Crawling Hand","hitpoints":15,"combatLevel":7,"slayerLevel":5,"attackSpeed":4,"attackLevel":7,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"450":{"name":"Crawling Hand","hitpoints":15,"combatLevel":7,"slayerLevel":5,"attackSpeed":4,"attackLevel":7,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"451":{"name":"Crawling Hand","hitpoints":15,"combatLevel":7,"slayerLevel":5,"attackSpeed":4,"attackLevel":7,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"452":{"name":"Crawling Hand","hitpoints":15,"combatLevel":7,"slayerLevel":5,"attackSpeed":4,"attackLevel":7,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"453":{"name":"Crawling Hand","hitpoints":19,"combatLevel":12,"slayerLevel":5,"attackSpeed":4,"attackLevel":11,"strengthLevel":7,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"454":{"name":"Crawling Hand","hitpoints":19,"combatLevel":12,"slayerLevel":5,"attackSpeed":4,"attackLevel":11,"strengthLevel":7,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"458":{"name":"Lizard","hitpoints":40,"combatLevel":42,"slayerLevel":22,"attackSpeed":4,"attackLevel":38,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"459":{"name":"Desert Lizard","hitpoints":25,"combatLevel":24,"slayerLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":22,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"460":{"name":"Desert Lizard","hitpoints":25,"combatLevel":24,"slayerLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":22,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"461":{"name":"Desert Lizard","hitpoints":25,"combatLevel":24,"slayerLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":22,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"462":{"name":"Small Lizard","hitpoints":15,"combatLevel":12,"slayerLevel":22,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"slashDef":5,"crushDef":5},"463":{"name":"Small Lizard","hitpoints":15,"combatLevel":12,"slayerLevel":22,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"slashDef":5,"crushDef":5},"464":{"name":"Harpie Bug Swarm","hitpoints":25,"combatLevel":46,"slayerLevel":33,"attackSpeed":4,"attackLevel":54,"strengthLevel":46,"defenceLevel":32,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":5,"crushDef":10,"rangeDef":10,"magicDef":5},"465":{"name":"Skeletal Wyvern","hitpoints":200,"combatLevel":140,"slayerLevel":72,"attackSpeed":6,"attackLevel":125,"strengthLevel":116,"defenceLevel":120,"rangeLevel":120,"magicLevel":125,"stabDef":140,"slashDef":90,"crushDef":90,"rangeDef":140,"magicDef":80},"466":{"name":"Skeletal Wyvern","hitpoints":200,"combatLevel":140,"slayerLevel":72,"attackSpeed":6,"attackLevel":125,"strengthLevel":116,"defenceLevel":120,"rangeLevel":120,"magicLevel":125,"stabDef":140,"slashDef":90,"crushDef":90,"rangeDef":140,"magicDef":80},"467":{"name":"Skeletal Wyvern","hitpoints":200,"combatLevel":140,"slayerLevel":72,"attackSpeed":6,"attackLevel":125,"strengthLevel":116,"defenceLevel":120,"rangeLevel":120,"magicLevel":125,"stabDef":140,"slashDef":90,"crushDef":90,"rangeDef":140,"magicDef":80},"468":{"name":"Skeletal Wyvern","hitpoints":200,"combatLevel":140,"slayerLevel":72,"attackSpeed":6,"attackLevel":125,"strengthLevel":116,"defenceLevel":120,"rangeLevel":120,"magicLevel":125,"stabDef":140,"slashDef":90,"crushDef":90,"rangeDef":140,"magicDef":80},"469":{"name":"Killerwatt","hitpoints":51,"combatLevel":55,"slayerLevel":37,"attackSpeed":2,"attackLevel":50,"strengthLevel":50,"defenceLevel":40,"rangeLevel":67,"magicLevel":67,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":20,"magicDef":20,"bonusAttack":30},"470":{"name":"Killerwatt","hitpoints":51,"combatLevel":55,"slayerLevel":37,"attackSpeed":2,"attackLevel":50,"strengthLevel":50,"defenceLevel":40,"rangeLevel":67,"magicLevel":67,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":20,"magicDef":20,"bonusAttack":30},"472":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"473":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"474":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"475":{"name":"Hole in the wall","hitpoints":105,"combatLevel":49,"slayerLevel":35,"attackSpeed":4,"attackLevel":30,"strengthLevel":30,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"476":{"name":"Wall beast","hitpoints":105,"combatLevel":49,"slayerLevel":35,"attackSpeed":4,"attackLevel":30,"strengthLevel":30,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"477":{"name":"Giant frog","hitpoints":100,"combatLevel":99,"attackSpeed":4,"attackLevel":100,"strengthLevel":80,"defenceLevel":65,"rangeLevel":1,"magicLevel":1},"478":{"name":"Big frog","hitpoints":25,"combatLevel":24,"attackSpeed":4,"attackLevel":25,"strengthLevel":20,"defenceLevel":15,"rangeLevel":1,"magicLevel":1},"480":{"name":"Cave slime","hitpoints":25,"combatLevel":23,"slayerLevel":17,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":35,"rangeLevel":1,"magicLevel":13},"481":{"name":"Cave bug","hitpoints":5,"combatLevel":6,"slayerLevel":7,"attackSpeed":4,"attackLevel":6,"strengthLevel":5,"defenceLevel":6,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"483":{"name":"Cave bug","hitpoints":93,"combatLevel":96,"slayerLevel":7,"attackSpeed":4,"attackLevel":82,"strengthLevel":80,"defenceLevel":84,"rangeLevel":1,"magicLevel":1,"stabDef":72,"slashDef":59,"crushDef":35,"rangeDef":95,"magicDef":25},"484":{"name":"Bloodveld","hitpoints":120,"combatLevel":76,"slayerLevel":50,"attackSpeed":4,"attackLevel":75,"strengthLevel":45,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"485":{"name":"Bloodveld","hitpoints":120,"combatLevel":76,"slayerLevel":50,"attackSpeed":4,"attackLevel":75,"strengthLevel":45,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"486":{"name":"Bloodveld","hitpoints":120,"combatLevel":76,"slayerLevel":50,"attackSpeed":4,"attackLevel":75,"strengthLevel":45,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"487":{"name":"Bloodveld","hitpoints":120,"combatLevel":76,"slayerLevel":50,"attackSpeed":4,"attackLevel":75,"strengthLevel":45,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"492":{"name":"Cave kraken","hitpoints":125,"combatLevel":127,"slayerLevel":87,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":150,"rangeLevel":1,"magicLevel":120,"rangeDef":100},"493":{"name":"Whirlpool","slayerLevel":87,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"rangeDef":300,"magicDef":130},"494":{"name":"Kraken","hitpoints":255,"combatLevel":291,"slayerLevel":87,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"rangeDef":300,"magicDef":130},"496":{"name":"Whirlpool","slayerLevel":87,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"rangeDef":300,"magicDef":130},"498":{"name":"Smoke devil","hitpoints":185,"combatLevel":160,"slayerLevel":93,"attackSpeed":4,"attackLevel":140,"strengthLevel":130,"defenceLevel":275,"rangeLevel":195,"magicLevel":1,"rangeDef":44,"magicDef":600,"poisonImmune":true,"venomImmune":true},"499":{"name":"Thermonuclear smoke devil","hitpoints":240,"combatLevel":301,"slayerLevel":93,"attackSpeed":2,"attackLevel":230,"strengthLevel":220,"defenceLevel":360,"rangeLevel":310,"magicLevel":1,"stabDef":11,"slashDef":4,"crushDef":9,"rangeDef":900,"magicDef":800,"poisonImmune":true,"venomImmune":true},"505":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"506":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"507":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"508":{"name":"Soulless","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"509":{"name":"Death wing","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"510":{"name":"Dark wizard","hitpoints":24,"combatLevel":20,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":14,"rangeLevel":1,"magicLevel":22,"magicDef":3},"511":{"name":"Invrigar the Necromancer","hitpoints":24,"combatLevel":20,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":14,"rangeLevel":1,"magicLevel":18,"magicDef":3},"512":{"name":"Dark wizard","hitpoints":12,"combatLevel":7,"attackSpeed":4,"attackLevel":5,"strengthLevel":2,"defenceLevel":5,"rangeLevel":1,"magicLevel":6,"magicDef":3},"513":{"name":"Mugger","hitpoints":8,"combatLevel":6,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":5,"rangeLevel":1,"magicLevel":1},"516":{"name":"Black Knight","hitpoints":42,"combatLevel":33,"attackSpeed":5,"attackLevel":25,"strengthLevel":25,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":73,"slashDef":76,"crushDef":70,"rangeDef":72,"bonusAttack":18,"bonusStrength":16},"517":{"name":"Black Knight","hitpoints":42,"combatLevel":33,"attackSpeed":5,"attackLevel":25,"strengthLevel":25,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":73,"slashDef":76,"crushDef":70,"rangeDef":72,"bonusAttack":18,"bonusStrength":16},"518":{"name":"Highwayman","hitpoints":13,"combatLevel":5,"attackSpeed":4,"attackLevel":2,"strengthLevel":2,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":6,"bonusStrength":7},"519":{"name":"Highwayman","hitpoints":13,"combatLevel":5,"attackSpeed":4,"attackLevel":2,"strengthLevel":2,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":6,"bonusStrength":7},"520":{"name":"Chaos druid","hitpoints":20,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":12,"rangeLevel":1,"magicLevel":10},"521":{"name":"Pirate","hitpoints":20,"combatLevel":23,"attackSpeed":5,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"slashDef":1,"bonusAttack":9,"bonusStrength":10},"522":{"name":"Pirate","hitpoints":20,"combatLevel":23,"attackSpeed":5,"attackLevel":21,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":2,"bonusAttack":8,"bonusStrength":10},"523":{"name":"Pirate","hitpoints":23,"combatLevel":26,"attackSpeed":5,"attackLevel":23,"strengthLevel":23,"defenceLevel":23,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":2,"bonusAttack":8,"bonusStrength":10},"525":{"name":"Thug","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":7,"strengthLevel":5,"defenceLevel":9,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":3,"crushDef":3,"bonusAttack":5,"bonusStrength":5},"526":{"name":"Rogue","hitpoints":17,"combatLevel":15,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":13,"rangeLevel":1,"magicLevel":1,"stabDef":6,"slashDef":9,"crushDef":11,"bonusAttack":5,"bonusStrength":5},"527":{"name":"Monk of Zamorak","hitpoints":20,"combatLevel":22,"attackSpeed":4,"attackLevel":18,"strengthLevel":18,"defenceLevel":22,"rangeLevel":1,"magicLevel":25},"528":{"name":"Monk of Zamorak","hitpoints":10,"combatLevel":17,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":12,"rangeLevel":1,"magicLevel":25},"529":{"name":"Monk of Zamorak","hitpoints":40,"combatLevel":45,"attackSpeed":4,"attackLevel":38,"strengthLevel":38,"defenceLevel":42,"rangeLevel":1,"magicLevel":40},"530":{"name":"Tribesman","hitpoints":39,"combatLevel":32,"attackSpeed":4,"attackLevel":23,"strengthLevel":27,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":6,"crushDef":6,"bonusAttack":8,"bonusStrength":5},"531":{"name":"Dark warrior","hitpoints":17,"combatLevel":8,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":5,"rangeLevel":1,"magicLevel":1,"stabDef":96,"slashDef":79,"crushDef":59,"bonusAttack":20,"bonusStrength":16},"532":{"name":"Chaos druid warrior","hitpoints":40,"combatLevel":37,"attackSpeed":5,"attackLevel":32,"strengthLevel":34,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":13,"slashDef":17,"crushDef":14,"rangeDef":14,"bonusAttack":9,"bonusStrength":5},"537":{"name":"Zygomite","hitpoints":65,"combatLevel":74,"slayerLevel":57,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":65,"magicLevel":65,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":20,"magicDef":20,"bonusAttack":30},"561":{"name":"Sorebones","hitpoints":52,"combatLevel":57,"attackSpeed":4,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"562":{"name":"Sorebones","hitpoints":52,"combatLevel":57,"attackSpeed":4,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"563":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"564":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"565":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"566":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"567":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"568":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"569":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"570":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"571":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"572":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"573":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"574":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"575":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"576":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"577":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"578":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"579":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"580":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"581":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"582":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"583":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"584":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"585":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"586":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"587":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"588":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"589":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"590":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"591":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"592":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"593":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"594":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"595":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"596":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"597":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"598":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"599":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"600":{"name":"Barrelchest","hitpoints":134,"combatLevel":190,"attackSpeed":5,"attackLevel":170,"strengthLevel":145,"defenceLevel":140,"rangeLevel":1,"magicLevel":90,"bonusAttack":80,"bonusStrength":80},"613":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"614":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"615":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"616":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"617":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"618":{"name":"Zombie pirate","hitpoints":52,"combatLevel":57,"attackSpeed":3,"attackLevel":49,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"619":{"name":"Zombie swab","hitpoints":50,"combatLevel":55,"attackSpeed":4,"attackLevel":45,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"620":{"name":"Zombie swab","hitpoints":50,"combatLevel":55,"attackSpeed":4,"attackLevel":45,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"621":{"name":"Zombie swab","hitpoints":50,"combatLevel":55,"attackSpeed":4,"attackLevel":45,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"622":{"name":"Zombie swab","hitpoints":50,"combatLevel":55,"attackSpeed":4,"attackLevel":45,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"623":{"name":"Zombie swab","hitpoints":50,"combatLevel":55,"attackSpeed":4,"attackLevel":45,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"624":{"name":"Zombie swab","hitpoints":50,"combatLevel":55,"attackSpeed":4,"attackLevel":45,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"625":{"name":"Evil spirit","hitpoints":90,"combatLevel":150,"attackSpeed":4,"attackLevel":170,"strengthLevel":146,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"626":{"name":"Fever spider","hitpoints":40,"combatLevel":49,"slayerLevel":42,"attackSpeed":4,"attackLevel":60,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":15,"crushDef":10,"rangeDef":15,"magicDef":15},"639":{"name":"Tyras guard","hitpoints":110,"combatLevel":110,"attackSpeed":5,"attackLevel":85,"strengthLevel":95,"defenceLevel":100,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":30},"642":{"name":"Arrg","hitpoints":140,"combatLevel":113,"attackSpeed":4,"attackLevel":70,"strengthLevel":140,"defenceLevel":40,"rangeLevel":70,"stabDef":35,"slashDef":60,"crushDef":35,"rangeDef":200,"magicDef":200,"bonusAttack":60,"bonusStrength":100},"643":{"name":"Arrg","hitpoints":140,"combatLevel":113,"attackSpeed":4,"attackLevel":70,"strengthLevel":140,"defenceLevel":40,"rangeLevel":70,"stabDef":35,"slashDef":60,"crushDef":35,"rangeDef":200,"magicDef":200,"bonusAttack":60,"bonusStrength":100},"645":{"name":"Ice wolf","hitpoints":70,"combatLevel":96,"attackSpeed":4,"attackLevel":100,"strengthLevel":90,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"646":{"name":"Ice wolf","hitpoints":70,"combatLevel":96,"attackSpeed":4,"attackLevel":100,"strengthLevel":90,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"647":{"name":"Ice wolf","hitpoints":70,"combatLevel":132,"attackSpeed":4,"attackLevel":150,"strengthLevel":150,"defenceLevel":70},"648":{"name":"Ice troll","hitpoints":80,"combatLevel":124,"attackSpeed":4,"attackLevel":140,"strengthLevel":120,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"649":{"name":"Ice troll","hitpoints":80,"combatLevel":123,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"650":{"name":"Ice troll","hitpoints":100,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"651":{"name":"Ice troll","hitpoints":80,"combatLevel":121,"attackSpeed":4,"attackLevel":100,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"652":{"name":"Ice troll","hitpoints":100,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"653":{"name":"Ice troll","hitpoints":100,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"654":{"name":"Ice troll","hitpoints":80,"combatLevel":121,"attackSpeed":4,"attackLevel":100,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"655":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"656":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"657":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"658":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"659":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"660":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"661":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"662":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"663":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"664":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"665":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"666":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"667":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"668":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"674":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"677":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"678":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"680":{"name":"Giant skeleton","hitpoints":70,"combatLevel":80,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"681":{"name":"Giant skeleton","hitpoints":70,"combatLevel":80,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"682":{"name":"Damis","hitpoints":90,"combatLevel":103,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":80},"683":{"name":"Damis","hitpoints":200,"combatLevel":174,"attackSpeed":3,"attackLevel":160,"strengthLevel":100,"defenceLevel":160,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":120,"magicDef":80,"bonusStrength":100},"685":{"name":"Stranger","hitpoints":80,"combatLevel":95,"attackSpeed":4,"attackLevel":90,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"magic":1,"stabDef":81,"slashDef":93,"crushDef":98,"rangeDef":82,"magicDef":1,"bonusStrength":40},"690":{"name":"Bandit","hitpoints":65,"combatLevel":74,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1},"695":{"name":"Bandit","hitpoints":50,"combatLevel":57,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"699":{"name":"Ice troll","hitpoints":80,"combatLevel":124,"attackSpeed":4,"attackLevel":140,"strengthLevel":120,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"700":{"name":"Ice troll","hitpoints":80,"combatLevel":123,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"701":{"name":"Ice troll","hitpoints":100,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"702":{"name":"Ice troll","hitpoints":80,"combatLevel":121,"attackSpeed":4,"attackLevel":100,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"703":{"name":"Ice troll","hitpoints":100,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"704":{"name":"Ice troll","hitpoints":100,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"705":{"name":"Ice troll","hitpoints":80,"combatLevel":121,"attackSpeed":4,"attackLevel":100,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"710":{"name":"Ice wolf","hitpoints":70,"combatLevel":132,"attackSpeed":4,"attackLevel":150,"strengthLevel":150,"defenceLevel":70},"711":{"name":"Ice wolf","hitpoints":70,"combatLevel":132,"attackSpeed":4,"attackLevel":150,"strengthLevel":150,"defenceLevel":70},"712":{"name":"Ice wolf","hitpoints":70,"combatLevel":132,"attackSpeed":4,"attackLevel":150,"strengthLevel":150,"defenceLevel":70},"713":{"name":"Ice wolf","hitpoints":70,"combatLevel":132,"attackSpeed":4,"attackLevel":150,"strengthLevel":150,"defenceLevel":70},"714":{"name":"Ice wolf","hitpoints":70,"combatLevel":132,"attackSpeed":4,"attackLevel":150,"strengthLevel":150,"defenceLevel":70},"715":{"name":"Ice wolf","hitpoints":70,"combatLevel":132,"attackSpeed":4,"attackLevel":150,"strengthLevel":150,"defenceLevel":70},"717":{"name":"Mummy","hitpoints":1,"combatLevel":96,"attackSpeed":4,"attackLevel":255,"strengthLevel":2,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100},"720":{"name":"Mummy","hitpoints":90,"combatLevel":103,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":90,"bonusStrength":90},"721":{"name":"Mummy","hitpoints":90,"combatLevel":103,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":90,"bonusStrength":90},"722":{"name":"Mummy","hitpoints":90,"combatLevel":103,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":90,"bonusStrength":90},"723":{"name":"Mummy","hitpoints":90,"combatLevel":103,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":90,"bonusStrength":90},"724":{"name":"Mummy","hitpoints":10,"combatLevel":98,"attackSpeed":4,"attackLevel":255,"strengthLevel":2,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100},"725":{"name":"Mummy","hitpoints":10,"combatLevel":98,"attackSpeed":4,"attackLevel":255,"strengthLevel":2,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100},"726":{"name":"Mummy","hitpoints":10,"combatLevel":98,"attackSpeed":4,"attackLevel":255,"strengthLevel":2,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100},"727":{"name":"Mummy","hitpoints":10,"combatLevel":98,"attackSpeed":4,"attackLevel":255,"strengthLevel":2,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100},"728":{"name":"Mummy","hitpoints":10,"combatLevel":98,"attackSpeed":4,"attackLevel":255,"strengthLevel":2,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100},"729":{"name":"Scarabs","hitpoints":25,"combatLevel":92,"attackSpeed":1,"attackLevel":255,"strengthLevel":2,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":15,"crushDef":15,"rangeDef":15,"magicDef":15},"734":{"name":"Bandit","hitpoints":30,"combatLevel":56,"attackSpeed":4,"attackLevel":59,"strengthLevel":70,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"slashDef":9,"crushDef":8,"magicDef":10,"bonusAttack":9,"bonusStrength":9},"735":{"name":"Bandit","hitpoints":30,"combatLevel":56,"attackSpeed":4,"attackLevel":59,"strengthLevel":70,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"slashDef":9,"crushDef":8,"magicDef":10,"bonusAttack":9,"bonusStrength":9},"736":{"name":"Bandit","hitpoints":30,"combatLevel":41,"attackSpeed":4,"attackLevel":40,"strengthLevel":50,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"slashDef":9,"crushDef":8,"magicDef":10,"bonusAttack":9,"bonusStrength":9},"737":{"name":"Bandit","hitpoints":30,"combatLevel":41,"attackSpeed":4,"attackLevel":40,"strengthLevel":50,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"slashDef":9,"crushDef":8,"magicDef":10,"bonusAttack":9,"bonusStrength":9},"738":{"name":"Bandit champion","hitpoints":50,"combatLevel":70,"attackSpeed":4,"attackLevel":59,"strengthLevel":80,"defenceLevel":50,"stabDef":9,"slashDef":8,"crushDef":10,"bonusAttack":9,"bonusStrength":9,"poisonImmune":true},"762":{"name":"Baby Roc","hitpoints":50,"combatLevel":75,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":150},"763":{"name":"Giant Roc","hitpoints":250,"combatLevel":172,"attackSpeed":6,"attackLevel":130,"strengthLevel":130,"defenceLevel":100,"rangeLevel":130,"magicLevel":1,"magicDef":150},"764":{"name":"Shadow","hitpoints":15,"combatLevel":73,"attackSpeed":4,"attackLevel":90,"strengthLevel":120,"defenceLevel":5,"rangeLevel":1,"magicLevel":90,"stabDef":30,"slashDef":30,"crushDef":30,"rangeDef":30,"magicDef":30},"773":{"name":"Skraeling","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"774":{"name":"Skraeling","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"785":{"name":"Me","hitpoints":45,"combatLevel":79,"attackSpeed":4,"attackLevel":75,"strengthLevel":78,"defenceLevel":74,"rangeLevel":1,"magicLevel":1},"786":{"name":"Me","hitpoints":45,"combatLevel":79,"attackSpeed":4,"attackLevel":75,"strengthLevel":78,"defenceLevel":74,"rangeLevel":1,"magicLevel":1},"787":{"name":"Suqah","hitpoints":105,"combatLevel":111,"attackSpeed":6,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":90},"788":{"name":"Suqah","hitpoints":105,"combatLevel":111,"attackSpeed":6,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":90},"789":{"name":"Suqah","hitpoints":105,"combatLevel":111,"attackSpeed":6,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":90},"790":{"name":"Suqah","hitpoints":105,"combatLevel":111,"attackSpeed":6,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":90},"791":{"name":"Suqah","hitpoints":105,"combatLevel":111,"attackSpeed":6,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":90},"792":{"name":"Suqah","hitpoints":105,"combatLevel":111,"attackSpeed":6,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":90},"793":{"name":"Suqah","hitpoints":105,"combatLevel":111,"attackSpeed":6,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":90},"794":{"name":"Scarab mage","hitpoints":50,"combatLevel":93,"attackSpeed":15,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":70,"magic":70,"stabDef":40,"slashDef":90,"crushDef":90,"magicDef":34},"795":{"name":"Locust rider","hitpoints":90,"combatLevel":106,"attackSpeed":6,"attackLevel":100,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":40},"796":{"name":"Locust rider","hitpoints":90,"combatLevel":98,"attackSpeed":6,"attackLevel":105,"strengthLevel":90,"defenceLevel":50,"rangeLevel":90,"magicLevel":1,"range":80,"stabDef":50,"slashDef":90,"crushDef":50,"rangeDef":66,"magicDef":34,"bonusRangeStrength":80},"797":{"name":"Giant scarab","hitpoints":130,"combatLevel":191,"attackSpeed":4,"attackLevel":169,"strengthLevel":190,"defenceLevel":169,"rangeLevel":190,"magicLevel":1,"stabDef":70,"slashDef":99,"crushDef":99,"rangeDef":149,"magicDef":159},"798":{"name":"Giant scarab","hitpoints":130,"combatLevel":191,"attackSpeed":4,"attackLevel":169,"strengthLevel":190,"defenceLevel":169,"rangeLevel":190,"magicLevel":1,"stabDef":70,"slashDef":99,"crushDef":99,"rangeDef":149,"magicDef":159},"799":{"name":"Scarab mage","hitpoints":50,"combatLevel":66,"attackSpeed":15,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":70,"magic":70,"stabDef":40,"slashDef":90,"crushDef":90,"magicDef":34},"800":{"name":"Locust rider","hitpoints":90,"combatLevel":68,"attackSpeed":6,"attackLevel":100,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":40},"801":{"name":"Locust rider","hitpoints":90,"combatLevel":68,"attackSpeed":6,"attackLevel":105,"strengthLevel":90,"defenceLevel":50,"rangeLevel":90,"magicLevel":1,"range":80,"stabDef":50,"slashDef":90,"crushDef":50,"rangeDef":66,"magicDef":34,"bonusRangeStrength":80},"817":{"name":"Elvarg","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":70,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"820":{"name":"Wormbrain","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"823":{"name":"Melzar the Mad","hitpoints":44,"combatLevel":43,"attackSpeed":4,"attackLevel":37,"strengthLevel":37,"defenceLevel":34,"rangeLevel":1,"magicLevel":40,"poisonImmune":true,"venomImmune":true},"852":{"name":"Icelord","hitpoints":60,"combatLevel":51,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"853":{"name":"Icelord","hitpoints":60,"combatLevel":51,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"854":{"name":"Icelord","hitpoints":60,"combatLevel":51,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"855":{"name":"Icelord","hitpoints":60,"combatLevel":51,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"866":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"867":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"868":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"869":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"870":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"871":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"872":{"name":"Skogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"873":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"874":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"875":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"876":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"877":{"name":"Zogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"878":{"name":"Skogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"879":{"name":"Skogre","hitpoints":71,"combatLevel":44,"attackSpeed":6,"attackLevel":20,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"880":{"name":"Zombie","hitpoints":50,"combatLevel":39,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"882":{"name":"Slash Bash","hitpoints":100,"combatLevel":111,"attackSpeed":6,"attackLevel":100,"strengthLevel":120,"defenceLevel":60,"rangeLevel":100,"magicLevel":1,"stabDef":30,"slashDef":30,"crushDef":30,"bonusAttack":22},"891":{"name":"Moss Guardian","hitpoints":120,"combatLevel":84,"attackSpeed":6,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"bonusAttack":66,"bonusStrength":62},"911":{"name":"Agrith Naar","hitpoints":95,"combatLevel":100,"attackSpeed":4,"attackLevel":83,"strengthLevel":90,"defenceLevel":82,"rangeLevel":100,"magicLevel":100},"924":{"name":"Skeleton","hitpoints":18,"combatLevel":13,"attackSpeed":4},"925":{"name":"Rock","hitpoints":140,"combatLevel":111,"attackSpeed":4,"attackLevel":70,"strengthLevel":110,"defenceLevel":70,"stabDef":35,"slashDef":60,"crushDef":35,"rangeDef":200,"magicDef":200,"bonusAttack":60,"bonusStrength":100},"926":{"name":"Stick","hitpoints":135,"combatLevel":104,"attackSpeed":6,"attackLevel":60,"strengthLevel":110,"defenceLevel":60,"stabDef":30,"slashDef":30,"crushDef":50,"rangeDef":200,"magicDef":200,"bonusAttack":50,"bonusStrength":80},"927":{"name":"Pee Hat","hitpoints":120,"combatLevel":91,"attackSpeed":6,"attackLevel":50,"strengthLevel":100,"defenceLevel":50,"stabDef":25,"slashDef":25,"crushDef":40,"rangeDef":200,"magicDef":200,"bonusAttack":40,"bonusStrength":70},"928":{"name":"Kraka","hitpoints":120,"combatLevel":91,"attackSpeed":6,"attackLevel":50,"strengthLevel":100,"defenceLevel":50,"stabDef":25,"slashDef":25,"crushDef":40,"rangeDef":200,"magicDef":200,"bonusAttack":40,"bonusStrength":70},"931":{"name":"Thrower Troll","hitpoints":95,"combatLevel":67,"attackSpeed":7,"attackLevel":30,"strengthLevel":80,"defenceLevel":30,"rangeLevel":60,"rangeDef":200,"magicDef":200},"932":{"name":"Thrower Troll","hitpoints":95,"combatLevel":67,"attackSpeed":7,"attackLevel":30,"strengthLevel":80,"defenceLevel":30,"rangeLevel":60,"rangeDef":200,"magicDef":200},"933":{"name":"Thrower Troll","hitpoints":95,"combatLevel":67,"attackSpeed":7,"attackLevel":30,"strengthLevel":80,"defenceLevel":30,"rangeLevel":60,"rangeDef":200,"magicDef":200},"934":{"name":"Thrower Troll","hitpoints":95,"combatLevel":67,"attackSpeed":7,"attackLevel":30,"strengthLevel":80,"defenceLevel":30,"rangeLevel":60,"rangeDef":200,"magicDef":200},"935":{"name":"Thrower Troll","hitpoints":95,"combatLevel":67,"attackSpeed":7,"attackLevel":30,"strengthLevel":80,"defenceLevel":30,"rangeLevel":60,"rangeDef":200,"magicDef":200},"936":{"name":"Mountain troll","hitpoints":90,"combatLevel":69,"attackSpeed":6,"attackLevel":40,"strengthLevel":75,"defenceLevel":40,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"937":{"name":"Mountain troll","hitpoints":90,"combatLevel":69,"attackSpeed":6,"attackLevel":40,"strengthLevel":75,"defenceLevel":40,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"938":{"name":"Mountain troll","hitpoints":90,"combatLevel":69,"attackSpeed":6,"attackLevel":40,"strengthLevel":75,"defenceLevel":40,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"939":{"name":"Mountain troll","hitpoints":90,"combatLevel":69,"attackSpeed":6,"attackLevel":40,"strengthLevel":75,"defenceLevel":40,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"940":{"name":"Mountain troll","hitpoints":90,"combatLevel":69,"attackSpeed":6,"attackLevel":40,"strengthLevel":75,"defenceLevel":40,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"941":{"name":"Mountain troll","hitpoints":90,"combatLevel":69,"attackSpeed":6,"attackLevel":40,"strengthLevel":75,"defenceLevel":40,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"942":{"name":"Mountain troll","hitpoints":90,"combatLevel":69,"attackSpeed":6,"attackLevel":40,"strengthLevel":75,"defenceLevel":40,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"946":{"name":"Ghast","hitpoints":45,"combatLevel":30,"attackSpeed":4,"attackLevel":22,"strengthLevel":22,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"949":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"950":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"951":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"952":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"953":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"955":{"name":"Kalphite Worker","hitpoints":40,"combatLevel":28,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":1,"rangeDef":10,"magicDef":10},"956":{"name":"Kalphite Worker","hitpoints":40,"combatLevel":28,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":1,"rangeDef":10,"magicDef":10},"957":{"name":"Kalphite Soldier","hitpoints":90,"combatLevel":85,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":5,"rangeDef":50,"magicDef":50},"958":{"name":"Kalphite Soldier","hitpoints":90,"combatLevel":85,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":5,"rangeDef":50,"magicDef":50},"959":{"name":"Kalphite Guardian","hitpoints":170,"combatLevel":141,"attackSpeed":4,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":5,"rangeDef":50,"magicDef":50},"960":{"name":"Kalphite Guardian","hitpoints":170,"combatLevel":141,"attackSpeed":4,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":5,"rangeDef":50,"magicDef":50},"961":{"name":"Kalphite Worker","hitpoints":40,"combatLevel":28,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":1,"rangeDef":10,"magicDef":10},"962":{"name":"Kalphite Guardian","hitpoints":170,"combatLevel":141,"attackSpeed":4,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":5,"rangeDef":50,"magicDef":50},"963":{"name":"Kalphite Queen","hitpoints":255,"combatLevel":333,"attackSpeed":4,"attackLevel":300,"strengthLevel":300,"defenceLevel":300,"rangeLevel":1,"magicLevel":150,"stabDef":50,"slashDef":50,"crushDef":10,"rangeDef":100,"magicDef":100},"965":{"name":"Kalphite Queen","hitpoints":255,"combatLevel":333,"attackSpeed":4,"attackLevel":300,"strengthLevel":300,"defenceLevel":300,"rangeLevel":1,"magicLevel":150,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":10,"magicDef":10},"970":{"name":"Dagannoth","hitpoints":70,"combatLevel":74,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"971":{"name":"Dagannoth","hitpoints":70,"combatLevel":74,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"972":{"name":"Dagannoth","hitpoints":70,"combatLevel":74,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"973":{"name":"Dagannoth","hitpoints":120,"combatLevel":92,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1,"rangeDef":50,"magicDef":50},"974":{"name":"Dagannoth","hitpoints":120,"combatLevel":92,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1,"rangeDef":50,"magicDef":50},"975":{"name":"Dagannoth","hitpoints":120,"combatLevel":92,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1,"rangeDef":50,"magicDef":50},"976":{"name":"Dagannoth","hitpoints":160,"combatLevel":100},"977":{"name":"Dagannoth","hitpoints":160,"combatLevel":100},"978":{"name":"Dagannoth","hitpoints":160,"combatLevel":100},"979":{"name":"Dagannoth","hitpoints":160,"combatLevel":100},"980":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"981":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"982":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"983":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"984":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"985":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"986":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"987":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"988":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"990":{"name":"Sigmund","hitpoints":60,"combatLevel":50,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"magic":8,"venomImmune":true},"991":{"name":"Sigmund","hitpoints":60,"combatLevel":50,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"magic":8,"venomImmune":true},"992":{"name":"Sigmund","hitpoints":60,"combatLevel":50,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"magic":8,"venomImmune":true},"993":{"name":"Sigmund","hitpoints":60,"combatLevel":50,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"magic":8,"venomImmune":true},"994":{"name":"Sigmund","hitpoints":60,"combatLevel":50,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"magic":8,"venomImmune":true},"1024":{"name":"Zygomite","hitpoints":75,"combatLevel":86,"slayerLevel":57,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":75,"rangeLevel":75,"magicLevel":75,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":20,"magicDef":20,"bonusAttack":30},"1025":{"name":"Necromancer","hitpoints":40,"combatLevel":26,"attackSpeed":4,"attackLevel":18,"strengthLevel":18,"defenceLevel":18,"rangeLevel":1,"magicLevel":18},"1026":{"name":"Bandit","hitpoints":27,"combatLevel":22,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":11,"bonusStrength":12},"1027":{"name":"Guard Bandit","hitpoints":27,"combatLevel":22,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":8,"crushDef":5,"rangeDef":4,"magicDef":1,"bonusAttack":11,"bonusStrength":12},"1037":{"name":"Snake","hitpoints":25,"combatLevel":35,"attackSpeed":3,"attackLevel":25,"strengthLevel":50,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"1038":{"name":"Monkey","hitpoints":6,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"1039":{"name":"Albino bat","hitpoints":33,"combatLevel":52,"attackSpeed":4,"attackLevel":57,"strengthLevel":57,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"1041":{"name":"Giant mosquito","hitpoints":3,"combatLevel":13,"attackSpeed":12,"attackLevel":5,"strengthLevel":1,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":2,"crushDef":5,"magicDef":2},"1042":{"name":"Jungle horror","hitpoints":45,"combatLevel":70,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"1043":{"name":"Jungle horror","hitpoints":45,"combatLevel":70,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"1044":{"name":"Jungle horror","hitpoints":45,"combatLevel":70,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"1045":{"name":"Jungle horror","hitpoints":45,"combatLevel":70,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"1046":{"name":"Jungle horror","hitpoints":45,"combatLevel":70,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"1047":{"name":"Cave horror","hitpoints":55,"combatLevel":80,"slayerLevel":58,"attackSpeed":4,"attackLevel":80,"strengthLevel":77,"defenceLevel":62,"rangeLevel":1,"magicLevel":80},"1048":{"name":"Cave horror","hitpoints":55,"combatLevel":80,"slayerLevel":58,"attackSpeed":4,"attackLevel":80,"strengthLevel":77,"defenceLevel":62,"rangeLevel":1,"magicLevel":80},"1049":{"name":"Cave horror","hitpoints":55,"combatLevel":80,"slayerLevel":58,"attackSpeed":4,"attackLevel":80,"strengthLevel":77,"defenceLevel":62,"rangeLevel":1,"magicLevel":80},"1050":{"name":"Cave horror","hitpoints":55,"combatLevel":80,"slayerLevel":58,"attackSpeed":4,"attackLevel":80,"strengthLevel":77,"defenceLevel":62,"rangeLevel":1,"magicLevel":80},"1051":{"name":"Cave horror","hitpoints":55,"combatLevel":80,"slayerLevel":58,"attackSpeed":4,"attackLevel":80,"strengthLevel":77,"defenceLevel":62,"rangeLevel":1,"magicLevel":80},"1060":{"name":"Angry bear","combatLevel":40},"1061":{"name":"Angry unicorn","hitpoints":50,"combatLevel":45},"1062":{"name":"Angry giant rat","hitpoints":50,"combatLevel":45},"1063":{"name":"Angry giant rat","hitpoints":50,"combatLevel":45},"1064":{"name":"Angry giant rat","hitpoints":50,"combatLevel":45},"1065":{"name":"Angry goblin","hitpoints":50,"combatLevel":45},"1066":{"name":"Fear reaper","hitpoints":25,"combatLevel":42,"attackSpeed":4,"attackLevel":39,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"1067":{"name":"Confusion beast","hitpoints":28,"combatLevel":43,"attackSpeed":4,"attackLevel":42,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"1068":{"name":"Confusion beast","hitpoints":28,"combatLevel":43,"attackSpeed":4,"attackLevel":42,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"1069":{"name":"Confusion beast","hitpoints":28,"combatLevel":43,"attackSpeed":4,"attackLevel":42,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"1070":{"name":"Confusion beast","hitpoints":28,"combatLevel":43,"attackSpeed":4,"attackLevel":42,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"1071":{"name":"Confusion beast","hitpoints":28,"combatLevel":43,"attackSpeed":4,"attackLevel":42,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"1072":{"name":"Hopeless creature","hitpoints":25,"combatLevel":40,"attackSpeed":4,"attackLevel":38,"strengthLevel":36,"defenceLevel":39,"rangeLevel":1,"magicLevel":1},"1073":{"name":"Hopeless creature","hitpoints":25,"combatLevel":40,"attackSpeed":4,"attackLevel":38,"strengthLevel":36,"defenceLevel":39,"rangeLevel":1,"magicLevel":1},"1074":{"name":"Hopeless creature","hitpoints":25,"combatLevel":40,"attackSpeed":4,"attackLevel":38,"strengthLevel":36,"defenceLevel":39,"rangeLevel":1,"magicLevel":1},"1075":{"name":"Tolna","hitpoints":37,"combatLevel":46,"attackLevel":42,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"rangeDef":50,"magicDef":50},"1076":{"name":"Tolna","hitpoints":37,"combatLevel":46,"attackSpeed":5,"attackLevel":42,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"rangeDef":50,"magicDef":50},"1077":{"name":"Tolna","hitpoints":37,"combatLevel":46,"attackLevel":42,"strengthLevel":41,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"rangeDef":50,"magicDef":50},"1097":{"name":"Sea Snake Young","hitpoints":85,"combatLevel":90,"slayerLevel":40,"attackSpeed":4,"attackLevel":90,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"1098":{"name":"Sea Snake Hatchling","hitpoints":50,"combatLevel":62,"slayerLevel":40,"attackSpeed":4,"attackLevel":60,"strengthLevel":55,"defenceLevel":50,"magicLevel":1},"1101":{"name":"Giant Sea Snake","hitpoints":100,"combatLevel":149,"attackSpeed":4,"attackLevel":170,"strengthLevel":90,"defenceLevel":160,"rangeLevel":130,"magicLevel":1},"1115":{"name":"Mourner","hitpoints":25,"combatLevel":24,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":2,"crushDef":4},"1118":{"name":"Man","hitpoints":13,"combatLevel":4,"attackSpeed":4,"attackLevel":2,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":1,"crushDef":1},"1119":{"name":"Woman","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"1126":{"name":"Barrelchest (hard)","hitpoints":255,"combatLevel":380,"attackSpeed":5,"attackLevel":306,"strengthLevel":261,"defenceLevel":140,"rangeLevel":1,"magicLevel":162,"bonusAttack":80,"bonusStrength":80},"1127":{"name":"Giant scarab (hard)","hitpoints":255,"combatLevel":316,"attackSpeed":4,"attackLevel":305,"strengthLevel":342,"defenceLevel":169,"rangeLevel":342,"magicLevel":1,"stabDef":70,"slashDef":99,"crushDef":99,"rangeDef":149,"magicDef":159},"1128":{"name":"Dessous (hard)","hitpoints":255,"combatLevel":217,"attackSpeed":3,"attackLevel":198,"strengthLevel":198,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":150,"crushDef":150,"bonusAttack":50,"bonusStrength":50},"1129":{"name":"Kamil (hard)","hitpoints":255,"combatLevel":273,"attackSpeed":4,"attackLevel":380,"strengthLevel":160,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":60,"crushDef":35,"bonusAttack":60,"bonusStrength":100},"1130":{"name":"Woman","hitpoints":13,"combatLevel":4,"attackSpeed":4,"attackLevel":2,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":1,"crushDef":1},"1131":{"name":"Woman","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"1134":{"name":"Damis (hard)","hitpoints":198,"combatLevel":200,"attackSpeed":4,"attackLevel":198,"strengthLevel":198,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":80},"1135":{"name":"Damis (hard)","hitpoints":255,"combatLevel":272,"attackSpeed":3,"attackLevel":320,"strengthLevel":200,"defenceLevel":160,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":120,"magicDef":80,"bonusStrength":100},"1139":{"name":"Woman","hitpoints":13,"combatLevel":4,"attackSpeed":4,"attackLevel":2,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":1,"crushDef":1},"1140":{"name":"Woman","hitpoints":13,"combatLevel":12,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"1141":{"name":"Woman","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"1142":{"name":"Woman","hitpoints":23,"combatLevel":14,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"1144":{"name":"Paladin","hitpoints":66,"combatLevel":62,"attackSpeed":5,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":22},"1148":{"name":"Mourner","hitpoints":25,"combatLevel":24,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":1,"crushDef":3},"1153":{"name":"Ogre","hitpoints":60,"combatLevel":63,"attackSpeed":6,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":19,"slashDef":23,"crushDef":24,"bonusAttack":8,"bonusStrength":6},"1163":{"name":"Tree spirit","hitpoints":85,"combatLevel":101,"attackSpeed":4,"attackLevel":90,"strengthLevel":95,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"1173":{"name":"Chicken","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1174":{"name":"Chicken","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1175":{"name":"Rooster","hitpoints":7,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":2,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1204":{"name":"Alomone","hitpoints":25,"combatLevel":13,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":6,"crushDef":8},"1206":{"name":"Clivet","hitpoints":25,"combatLevel":13,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":6,"crushDef":8},"1207":{"name":"Hazeel Cultist","hitpoints":25,"combatLevel":13,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":6,"crushDef":8},"1208":{"name":"Khazard Guard","hitpoints":25,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":18,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":29,"crushDef":21,"bonusAttack":10,"bonusStrength":14},"1210":{"name":"Khazard Guard","hitpoints":25,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":18,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":29,"crushDef":21,"bonusAttack":10,"bonusStrength":14},"1211":{"name":"Khazard Guard","hitpoints":25,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":18,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":29,"crushDef":21,"bonusAttack":10,"bonusStrength":14},"1213":{"name":"General Khazard","hitpoints":170,"combatLevel":112,"attackSpeed":4,"attackLevel":75,"strengthLevel":78,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"1224":{"name":"Bouncer","hitpoints":116,"combatLevel":137,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":1},"1225":{"name":"Khazard Ogre","hitpoints":60,"combatLevel":63,"attackSpeed":6,"attackLevel":54,"strengthLevel":53,"defenceLevel":53,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":20},"1226":{"name":"Khazard Scorpion","hitpoints":40,"combatLevel":44,"attackSpeed":4,"attackLevel":40,"strengthLevel":39,"defenceLevel":34,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"1227":{"name":"Arzinian Avatar of Strength","attackSpeed":4},"1228":{"name":"Arzinian Avatar of Strength","hitpoints":100,"combatLevel":125,"attackSpeed":4,"attackLevel":110,"strengthLevel":100,"defenceLevel":95,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":80,"magicDef":15},"1229":{"name":"Arzinian Avatar of Strength","hitpoints":70,"combatLevel":75,"attackSpeed":4,"attackLevel":70,"strengthLevel":65,"defenceLevel":50,"stabDef":15,"slashDef":15,"crushDef":15,"rangeDef":40,"magicDef":10},"1230":{"name":"Arzinian Avatar of Ranging","attackSpeed":4},"1231":{"name":"Arzinian Avatar of Ranging","hitpoints":100,"combatLevel":125,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":120,"rangeLevel":110,"stabDef":15,"slashDef":15,"crushDef":15,"rangeDef":20,"magicDef":80,"bonusRangeStrength":10},"1232":{"name":"Arzinian Avatar of Ranging","hitpoints":70,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":40,"defenceLevel":75,"rangeLevel":75,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":15,"magicDef":40,"bonusRangeStrength":5},"1233":{"name":"Arzinian Avatar of Magic","attackSpeed":4},"1234":{"name":"Arzinian Avatar of Magic","hitpoints":100,"combatLevel":125,"attackSpeed":4,"attackLevel":10,"strengthLevel":90,"defenceLevel":120,"magicLevel":120,"stabDef":80,"slashDef":80,"crushDef":80,"rangeDef":15,"magicDef":20},"1235":{"name":"Arzinian Avatar of Magic","hitpoints":70,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":50,"defenceLevel":75,"magicLevel":75,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":10,"magicDef":15},"1261":{"name":"Ram","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1262":{"name":"Ram","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1263":{"name":"Ram","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1264":{"name":"Ram","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1265":{"name":"Ram","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1267":{"name":"Vulture","hitpoints":10,"combatLevel":31,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"1268":{"name":"Vulture","hitpoints":10,"combatLevel":31,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"1273":{"name":"Experiment","hitpoints":40,"combatLevel":51,"attackSpeed":4,"attackLevel":40,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"1274":{"name":"Experiment","hitpoints":100,"combatLevel":25,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"1275":{"name":"Experiment","hitpoints":100,"combatLevel":25,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"1276":{"name":"Loar Shadow","hitpoints":38,"combatLevel":40,"attackSpeed":4,"attackLevel":45,"strengthLevel":30,"defenceLevel":26},"1277":{"name":"Loar Shade","hitpoints":38,"combatLevel":40,"attackSpeed":4,"attackLevel":45,"strengthLevel":30,"defenceLevel":26},"1279":{"name":"Phrin Shadow","hitpoints":56,"combatLevel":60,"attackSpeed":4,"attackLevel":64,"strengthLevel":47,"defenceLevel":42},"1280":{"name":"Phrin Shade","hitpoints":56,"combatLevel":60,"attackSpeed":4,"attackLevel":64,"strengthLevel":47,"defenceLevel":42},"1281":{"name":"Riyl Shadow","hitpoints":76,"combatLevel":80,"attackSpeed":4,"attackLevel":88,"strengthLevel":55,"defenceLevel":60},"1282":{"name":"Riyl Shade","hitpoints":76,"combatLevel":80,"attackSpeed":4,"attackLevel":88,"strengthLevel":55,"defenceLevel":60},"1283":{"name":"Asyn Shadow","hitpoints":90,"combatLevel":100,"attackSpeed":4,"attackLevel":102,"strengthLevel":84,"defenceLevel":70},"1284":{"name":"Asyn Shade","hitpoints":90,"combatLevel":100,"attackSpeed":4,"attackLevel":102,"strengthLevel":84,"defenceLevel":70},"1285":{"name":"Fiyr Shadow","hitpoints":110,"combatLevel":120,"attackSpeed":6,"attackLevel":120,"strengthLevel":100,"defenceLevel":85},"1286":{"name":"Fiyr Shade","hitpoints":110,"combatLevel":120,"attackSpeed":6,"attackLevel":120,"strengthLevel":100,"defenceLevel":85},"1293":{"name":"Afflicted","hitpoints":30,"combatLevel":37,"attackSpeed":4,"attackLevel":30,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"1294":{"name":"Afflicted","hitpoints":28,"combatLevel":34,"attackSpeed":4,"attackLevel":28,"strengthLevel":28,"defenceLevel":38,"rangeLevel":1,"magicLevel":1},"1297":{"name":"Afflicted","hitpoints":26,"combatLevel":32,"attackSpeed":4,"attackLevel":26,"strengthLevel":26,"defenceLevel":36,"rangeLevel":1,"magicLevel":1},"1298":{"name":"Afflicted","hitpoints":24,"combatLevel":30,"attackSpeed":4,"attackLevel":24,"strengthLevel":24,"defenceLevel":34,"rangeLevel":1,"magicLevel":1},"1338":{"name":"Seagull","hitpoints":6,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1339":{"name":"Seagull","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1354":{"name":"Dwarf gang member","hitpoints":40,"combatLevel":44,"attackSpeed":5,"attackLevel":40,"strengthLevel":40,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"1355":{"name":"Dwarf gang member","hitpoints":25,"combatLevel":48,"attackSpeed":5,"attackLevel":80,"strengthLevel":30,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"1356":{"name":"Dwarf gang member","hitpoints":25,"combatLevel":49,"attackSpeed":5,"attackLevel":30,"strengthLevel":60,"defenceLevel":57,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"1362":{"name":"Slagilith","hitpoints":60,"combatLevel":92,"attackSpeed":4,"attackLevel":60,"strengthLevel":120,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":5,"rangeDef":50,"magicDef":5,"bonusAttack":10},"1364":{"name":"Slagilith","hitpoints":60,"combatLevel":92,"attackSpeed":4,"attackLevel":60,"strengthLevel":120,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":5,"rangeDef":50,"magicDef":5,"bonusAttack":10},"1365":{"name":"Fire elemental","hitpoints":30,"combatLevel":35,"attackSpeed":4,"attackLevel":35,"strengthLevel":35,"defenceLevel":20,"rangeLevel":20,"magicLevel":20},"1366":{"name":"Earth elemental","hitpoints":35,"combatLevel":35,"attackSpeed":6,"attackLevel":20,"strengthLevel":35,"defenceLevel":35,"rangeLevel":30,"magicLevel":10},"1367":{"name":"Earth elemental","hitpoints":35,"combatLevel":35,"attackSpeed":6,"attackLevel":20,"strengthLevel":35,"defenceLevel":35,"rangeLevel":30,"magicLevel":10,"poisonImmune":true,"venomImmune":true},"1369":{"name":"Air elemental","hitpoints":30,"combatLevel":34,"attackSpeed":4,"attackLevel":40,"strengthLevel":20,"defenceLevel":30,"rangeLevel":20,"magicLevel":40},"1370":{"name":"Water elemental","hitpoints":30,"combatLevel":34,"attackSpeed":4,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":30,"magicLevel":30},"1377":{"name":"The Kendal","hitpoints":50,"combatLevel":70,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":60,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":20,"bonusAttack":5,"bonusStrength":10},"1378":{"name":"The Kendal","hitpoints":50,"combatLevel":70,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":60,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":20,"bonusAttack":5,"bonusStrength":10},"1379":{"name":"Camp dweller","hitpoints":30,"combatLevel":31,"attackSpeed":4,"attackLevel":25,"strengthLevel":30,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"1380":{"name":"Camp dweller","hitpoints":30,"combatLevel":31,"attackSpeed":4,"attackLevel":25,"strengthLevel":30,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"1381":{"name":"Camp dweller","hitpoints":30,"combatLevel":31,"attackSpeed":4,"attackLevel":25,"strengthLevel":30,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"1382":{"name":"Camp dweller","hitpoints":30,"combatLevel":31,"attackSpeed":4,"attackLevel":25,"strengthLevel":30,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"1383":{"name":"Camp dweller","hitpoints":25,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":25,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"1401":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"1402":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"1403":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"1404":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"1405":{"name":"Dwarf","hitpoints":16,"combatLevel":10,"attackSpeed":5,"attackLevel":8,"strengthLevel":8,"defenceLevel":6,"rangeLevel":1,"magicLevel":1,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"1406":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"1407":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"1408":{"name":"Dwarf","hitpoints":10,"combatLevel":7,"attackSpeed":4,"attackLevel":6,"strengthLevel":6,"defenceLevel":6,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"1409":{"name":"Black Guard","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"1410":{"name":"Black Guard","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"1411":{"name":"Black Guard","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"1412":{"name":"Black Guard","hitpoints":30,"combatLevel":25,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"1429":{"name":"Foreman","hitpoints":20,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"slashDef":1,"crushDef":1},"1430":{"name":"Shipyard worker","hitpoints":10,"combatLevel":11,"attackSpeed":6,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"bonusAttack":6,"bonusStrength":6},"1432":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"1443":{"name":"Jungle Demon","hitpoints":170,"combatLevel":195,"attackSpeed":6,"attackLevel":170,"strengthLevel":170,"defenceLevel":170,"rangeLevel":1,"magicLevel":170,"slashDef":50,"magicDef":50,"bonusAttack":50,"bonusStrength":50},"1447":{"name":"Pirate","hitpoints":20,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"slashDef":1,"bonusAttack":9,"bonusStrength":10},"1448":{"name":"Thief","hitpoints":17,"combatLevel":16,"attackSpeed":4,"attackLevel":14,"strengthLevel":13,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"1461":{"name":"Mugger","hitpoints":8,"combatLevel":6,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":5,"rangeLevel":1,"magicLevel":1},"1475":{"name":"Chompy bird","hitpoints":10,"combatLevel":6,"attackLevel":5,"strengthLevel":5,"defenceLevel":3},"1537":{"name":"Skeleton hero","hitpoints":124,"combatLevel":149,"attackSpeed":4,"attackLevel":145,"strengthLevel":134,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"1538":{"name":"Skeleton brute","hitpoints":124,"combatLevel":132,"attackSpeed":4,"attackLevel":115,"strengthLevel":114,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"1539":{"name":"Skeleton warlord","hitpoints":124,"combatLevel":132,"attackSpeed":4,"attackLevel":115,"strengthLevel":114,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"1540":{"name":"Skeleton heavy","hitpoints":124,"combatLevel":132,"attackSpeed":4,"attackLevel":115,"strengthLevel":114,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"1541":{"name":"Skeleton thug","hitpoints":124,"combatLevel":132,"attackSpeed":4,"attackLevel":115,"strengthLevel":114,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"1543":{"name":"Gargoyle","hitpoints":105,"combatLevel":111,"slayerLevel":75,"attackSpeed":4,"attackLevel":75,"strengthLevel":105,"defenceLevel":107,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":20,"magicDef":20},"1545":{"name":"Black knight","hitpoints":42,"combatLevel":33,"attackSpeed":5,"attackLevel":25,"strengthLevel":25,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":73,"slashDef":76,"crushDef":70,"rangeDef":72,"bonusAttack":18,"bonusStrength":16},"1546":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"1547":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"1548":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"1549":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"1550":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"1556":{"name":"Fire wizard","hitpoints":25,"combatLevel":13,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":10,"magicLevel":10,"magicDef":15},"1557":{"name":"Water wizard","hitpoints":25,"combatLevel":13,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":10,"magicLevel":10,"magicDef":15},"1558":{"name":"Earth wizard","hitpoints":25,"combatLevel":13,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":10,"magicLevel":10,"magicDef":15},"1559":{"name":"Air wizard","hitpoints":25,"combatLevel":13,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":10,"magicLevel":10,"magicDef":15},"1603":{"name":"Kolodion","hitpoints":3,"attackSpeed":7},"1604":{"name":"Kolodion","hitpoints":3,"attackSpeed":7},"1605":{"name":"Kolodion","hitpoints":3,"attackSpeed":7},"1606":{"name":"Kolodion","hitpoints":65,"attackSpeed":7},"1607":{"name":"Kolodion","hitpoints":65,"attackSpeed":7},"1608":{"name":"Kolodion","hitpoints":78,"attackSpeed":7},"1609":{"name":"Kolodion","hitpoints":107,"combatLevel":112,"attackSpeed":4,"attackLevel":85,"strengthLevel":98,"defenceLevel":105,"rangeLevel":1,"magicLevel":80},"1610":{"name":"Battle mage","hitpoints":120,"combatLevel":54,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":50},"1611":{"name":"Battle mage","hitpoints":120,"combatLevel":54,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":50},"1612":{"name":"Battle mage","hitpoints":120,"combatLevel":54,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":50},"1667":{"name":"Penance Fighter","hitpoints":28,"combatLevel":30,"attackSpeed":4,"defenceLevel":25},"1668":{"name":"Penance Ranger","hitpoints":20,"combatLevel":21,"attackSpeed":4,"defenceLevel":21},"1672":{"name":"Ahrim the Blighted","hitpoints":100,"combatLevel":98,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":1,"magicLevel":100,"magic":73,"stabDef":103,"slashDef":85,"crushDef":117,"magicDef":73,"bonusStrength":68},"1673":{"name":"Dharok the Wretched","hitpoints":100,"combatLevel":115,"attackSpeed":7,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":252,"slashDef":250,"crushDef":244,"rangeDef":249,"bonusStrength":105},"1674":{"name":"Guthan the Infested","hitpoints":100,"combatLevel":115,"attackSpeed":5,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":259,"slashDef":257,"crushDef":241,"rangeDef":250,"bonusStrength":75},"1675":{"name":"Karil the Tainted","hitpoints":100,"combatLevel":98,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":100,"magicLevel":1,"range":134,"stabDef":79,"slashDef":71,"crushDef":90,"rangeDef":100,"magicDef":106,"bonusRangeStrength":55},"1676":{"name":"Torag the Corrupted","hitpoints":100,"combatLevel":115,"attackSpeed":5,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":221,"slashDef":235,"crushDef":222,"rangeDef":221,"bonusStrength":72},"1677":{"name":"Verac the Defiled","hitpoints":100,"combatLevel":115,"attackSpeed":5,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":227,"slashDef":230,"crushDef":221,"rangeDef":225,"bonusStrength":72},"1678":{"name":"Bloodworm","hitpoints":45,"combatLevel":52,"attackSpeed":4,"attackLevel":80,"strengthLevel":20,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"1679":{"name":"Crypt rat","hitpoints":35,"combatLevel":43,"attackSpeed":4,"attackLevel":70,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"1680":{"name":"Giant crypt rat","hitpoints":70,"combatLevel":76,"attackSpeed":4,"attackLevel":80,"strengthLevel":50,"defenceLevel":65,"rangeLevel":1,"magicLevel":1},"1681":{"name":"Giant crypt rat","hitpoints":70,"combatLevel":76,"attackSpeed":4,"attackLevel":80,"strengthLevel":50,"defenceLevel":65,"rangeLevel":1,"magicLevel":1},"1682":{"name":"Giant crypt rat","hitpoints":70,"combatLevel":76,"attackSpeed":4,"attackLevel":80,"strengthLevel":50,"defenceLevel":65,"rangeLevel":1,"magicLevel":1},"1683":{"name":"Crypt spider","hitpoints":45,"combatLevel":56,"attackSpeed":4,"attackLevel":45,"strengthLevel":47,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":10,"rangeDef":20,"magicDef":17},"1684":{"name":"Giant crypt spider","hitpoints":80,"combatLevel":79,"attackSpeed":4,"attackLevel":65,"strengthLevel":67,"defenceLevel":65,"rangeLevel":1,"magicLevel":1},"1685":{"name":"Skeleton","hitpoints":51,"combatLevel":77,"attackSpeed":4,"attackLevel":72,"strengthLevel":72,"defenceLevel":72,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"1686":{"name":"Skeleton","hitpoints":51,"combatLevel":77,"attackSpeed":4,"attackLevel":72,"strengthLevel":72,"defenceLevel":72,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"1687":{"name":"Skeleton","hitpoints":51,"combatLevel":77,"attackSpeed":4,"attackLevel":72,"strengthLevel":72,"defenceLevel":72,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"1688":{"name":"Skeleton","hitpoints":51,"combatLevel":77,"attackSpeed":4,"attackLevel":72,"strengthLevel":72,"defenceLevel":72,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"1689":{"name":"Splatter","hitpoints":13,"combatLevel":22,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":33,"magicLevel":17},"1690":{"name":"Splatter","hitpoints":23,"combatLevel":33,"attackSpeed":4,"attackLevel":22,"strengthLevel":22,"defenceLevel":53,"magicLevel":22},"1691":{"name":"Splatter","hitpoints":33,"combatLevel":44,"attackSpeed":4,"attackLevel":27,"strengthLevel":27,"defenceLevel":73,"magicLevel":27},"1692":{"name":"Splatter","hitpoints":43,"combatLevel":54,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":93,"magicLevel":32},"1693":{"name":"Splatter","hitpoints":53,"combatLevel":65,"attackSpeed":4,"attackLevel":37,"strengthLevel":37,"defenceLevel":113,"magicLevel":37},"1694":{"name":"Shifter","hitpoints":23,"combatLevel":38,"attackSpeed":4,"attackLevel":47,"strengthLevel":37,"defenceLevel":23,"magicLevel":27},"1695":{"name":"Shifter","hitpoints":23,"combatLevel":38,"attackSpeed":4,"attackLevel":47,"strengthLevel":37,"defenceLevel":23,"magicLevel":27},"1696":{"name":"Shifter","hitpoints":38,"combatLevel":57,"attackSpeed":4,"attackLevel":67,"strengthLevel":52,"defenceLevel":38,"magicLevel":37},"1697":{"name":"Shifter","hitpoints":38,"combatLevel":57,"attackSpeed":4,"attackLevel":67,"strengthLevel":52,"defenceLevel":38,"magicLevel":37},"1698":{"name":"Shifter","hitpoints":53,"combatLevel":76,"attackSpeed":4,"attackLevel":87,"strengthLevel":67,"defenceLevel":53,"magicLevel":47},"1699":{"name":"Shifter","hitpoints":53,"combatLevel":76,"attackSpeed":4,"attackLevel":87,"strengthLevel":67,"defenceLevel":53,"magicLevel":47},"1700":{"name":"Shifter","hitpoints":68,"combatLevel":90,"attackSpeed":4,"attackLevel":100,"strengthLevel":75,"defenceLevel":68,"magicLevel":50},"1701":{"name":"Shifter","hitpoints":68,"combatLevel":90,"attackSpeed":4,"attackLevel":100,"strengthLevel":75,"defenceLevel":68,"magicLevel":50},"1702":{"name":"Shifter","hitpoints":83,"combatLevel":104,"attackSpeed":4,"attackLevel":110,"strengthLevel":83,"defenceLevel":83,"magicLevel":60},"1703":{"name":"Shifter","hitpoints":83,"combatLevel":104,"attackSpeed":4,"attackLevel":110,"strengthLevel":83,"defenceLevel":83,"magicLevel":60},"1704":{"name":"Ravager","hitpoints":23,"combatLevel":36,"attackSpeed":4,"attackLevel":27,"strengthLevel":57,"defenceLevel":13},"1705":{"name":"Ravager","hitpoints":38,"combatLevel":53,"attackSpeed":4,"attackLevel":37,"strengthLevel":82,"defenceLevel":23},"1706":{"name":"Ravager","hitpoints":53,"combatLevel":71,"attackSpeed":4,"attackLevel":47,"strengthLevel":107,"defenceLevel":33},"1707":{"name":"Ravager","hitpoints":68,"combatLevel":89,"attackSpeed":4,"attackLevel":57,"strengthLevel":132,"defenceLevel":43},"1708":{"name":"Ravager","hitpoints":83,"combatLevel":106,"attackSpeed":4,"attackLevel":67,"strengthLevel":157,"defenceLevel":53},"1709":{"name":"Spinner","hitpoints":33,"combatLevel":36,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":43,"magicLevel":37},"1710":{"name":"Spinner","hitpoints":53,"combatLevel":55,"attackSpeed":4,"attackLevel":22,"strengthLevel":22,"defenceLevel":68,"magicLevel":52},"1711":{"name":"Spinner","hitpoints":73,"combatLevel":74,"attackSpeed":4,"attackLevel":27,"strengthLevel":27,"defenceLevel":93,"magicLevel":67},"1712":{"name":"Spinner","hitpoints":101,"combatLevel":92,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":118,"magicLevel":82},"1713":{"name":"Spinner","hitpoints":93,"combatLevel":88,"attackSpeed":4,"attackLevel":37,"strengthLevel":37,"defenceLevel":143,"magicLevel":37},"1714":{"name":"Torcher","hitpoints":18,"combatLevel":33,"attackSpeed":4,"defenceLevel":25,"magicLevel":46},"1715":{"name":"Torcher","hitpoints":18,"combatLevel":33,"attackSpeed":4,"defenceLevel":25,"magicLevel":46},"1716":{"name":"Torcher","hitpoints":30,"combatLevel":49,"attackSpeed":4,"defenceLevel":41,"magicLevel":66},"1717":{"name":"Torcher","hitpoints":30,"combatLevel":49,"attackSpeed":4,"defenceLevel":41,"magicLevel":66},"1718":{"name":"Torcher","hitpoints":45,"combatLevel":66,"attackSpeed":4,"defenceLevel":57,"magicLevel":85},"1719":{"name":"Torcher","hitpoints":45,"combatLevel":66,"attackSpeed":4,"defenceLevel":57,"magicLevel":85},"1720":{"name":"Torcher","hitpoints":57,"combatLevel":79,"attackSpeed":4,"defenceLevel":73,"magicLevel":97},"1721":{"name":"Torcher","hitpoints":57,"combatLevel":79,"attackSpeed":4,"defenceLevel":73,"magicLevel":97},"1722":{"name":"Torcher","hitpoints":71,"combatLevel":91,"attackSpeed":4,"defenceLevel":89,"magicLevel":107},"1723":{"name":"Torcher","hitpoints":71,"combatLevel":92,"attackSpeed":4,"defenceLevel":89,"magicLevel":107},"1724":{"name":"Defiler","hitpoints":27,"combatLevel":33,"attackSpeed":4,"defenceLevel":17,"rangeLevel":46},"1725":{"name":"Defiler","hitpoints":27,"combatLevel":33,"attackSpeed":4,"defenceLevel":17,"rangeLevel":46},"1726":{"name":"Defiler","hitpoints":45,"combatLevel":50,"attackSpeed":4,"defenceLevel":29,"rangeLevel":66},"1727":{"name":"Defiler","hitpoints":45,"combatLevel":50,"attackSpeed":4,"defenceLevel":29,"rangeLevel":66},"1728":{"name":"Defiler","hitpoints":62,"combatLevel":66,"attackSpeed":4,"defenceLevel":41,"rangeLevel":84},"1729":{"name":"Defiler","hitpoints":62,"combatLevel":67,"attackSpeed":4,"defenceLevel":41,"rangeLevel":85},"1730":{"name":"Defiler","hitpoints":78,"combatLevel":80,"attackSpeed":4,"defenceLevel":53,"rangeLevel":98},"1731":{"name":"Defiler","hitpoints":78,"combatLevel":80,"attackSpeed":4,"defenceLevel":53,"rangeLevel":98},"1732":{"name":"Defiler","hitpoints":97,"combatLevel":97,"attackSpeed":4,"defenceLevel":65,"rangeLevel":117},"1733":{"name":"Defiler","hitpoints":97,"combatLevel":97,"attackSpeed":4,"defenceLevel":65,"rangeLevel":117},"1734":{"name":"Brawler","hitpoints":53,"combatLevel":51,"attackSpeed":4,"attackLevel":27,"strengthLevel":57,"defenceLevel":43},"1735":{"name":"Brawler","hitpoints":83,"combatLevel":76,"attackSpeed":4,"attackLevel":37,"strengthLevel":82,"defenceLevel":67},"1736":{"name":"Brawler","hitpoints":113,"combatLevel":101,"attackSpeed":4,"attackLevel":47,"strengthLevel":107,"defenceLevel":93},"1737":{"name":"Brawler","hitpoints":143,"combatLevel":129,"attackSpeed":4,"attackLevel":67,"strengthLevel":132,"defenceLevel":118},"1738":{"name":"Brawler","hitpoints":173,"combatLevel":158,"attackSpeed":4,"attackLevel":87,"strengthLevel":157,"defenceLevel":143},"1739":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1740":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1741":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1742":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1743":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1744":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1745":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1746":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1747":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1748":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1749":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1750":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1751":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1752":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1753":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1754":{"name":"Portal","defenceLevel":85,"rangeDef":70,"magicDef":70},"1777":{"name":"Double agent","hitpoints":80,"combatLevel":65,"attackSpeed":4,"attackLevel":80,"strengthLevel":43,"defenceLevel":24,"rangeLevel":1,"magicLevel":24,"stabDef":3,"slashDef":3,"crushDef":3},"1778":{"name":"Double agent","hitpoints":120,"combatLevel":108,"attackSpeed":4,"attackLevel":100,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":80,"stabDef":20,"slashDef":20,"crushDef":20,"bonusAttack":40,"bonusStrength":40},"1782":{"name":"Scarab swarm","hitpoints":25,"combatLevel":98,"attackSpeed":1,"attackLevel":255,"strengthLevel":5,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":5,"rangeDef":90,"magicDef":90},"1792":{"name":"Goat","hitpoints":21,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"rangeDef":10,"bonusAttack":29,"bonusStrength":10},"1793":{"name":"Goat","hitpoints":21,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"rangeDef":10,"bonusAttack":29,"bonusStrength":10},"1794":{"name":"Billy Goat","hitpoints":28,"combatLevel":33,"attackSpeed":4,"attackLevel":31,"strengthLevel":29,"defenceLevel":29,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"rangeDef":10,"bonusAttack":29,"bonusStrength":10},"1795":{"name":"Goat","hitpoints":21,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"rangeDef":10,"bonusAttack":29,"bonusStrength":10},"1796":{"name":"Goat","hitpoints":21,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"rangeDef":10,"bonusAttack":29,"bonusStrength":10},"1797":{"name":"Billy Goat","hitpoints":28,"combatLevel":33,"attackSpeed":4,"attackLevel":31,"strengthLevel":29,"defenceLevel":29,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"rangeDef":10,"bonusAttack":29,"bonusStrength":10},"1798":{"name":"White Knight","hitpoints":52,"combatLevel":36,"attackSpeed":7,"attackLevel":27,"strengthLevel":29,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":83,"slashDef":76,"crushDef":70,"rangeDef":74,"bonusAttack":30,"bonusStrength":31},"1799":{"name":"White Knight","hitpoints":52,"combatLevel":38,"attackSpeed":7,"attackLevel":30,"strengthLevel":29,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":83,"slashDef":76,"crushDef":70,"rangeDef":74,"bonusAttack":30,"bonusStrength":31},"1800":{"name":"White Knight","hitpoints":52,"combatLevel":39,"attackSpeed":7,"attackLevel":32,"strengthLevel":29,"defenceLevel":27,"rangeLevel":1,"magicLevel":1,"stabDef":83,"slashDef":76,"crushDef":70,"rangeDef":74,"bonusAttack":30,"bonusStrength":31},"1829":{"name":"White Knight","hitpoints":55,"combatLevel":42,"attackSpeed":7,"attackLevel":32,"strengthLevel":35,"defenceLevel":27,"rangeLevel":1,"magicLevel":1,"stabDef":83,"slashDef":76,"crushDef":70,"rangeDef":74,"bonusAttack":30,"bonusStrength":31},"1834":{"name":"Gorak","hitpoints":112,"combatLevel":145,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":131,"rangeLevel":1,"magicLevel":1},"1838":{"name":"Duck","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1839":{"name":"Duck","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1845":{"name":"Stag","hitpoints":19,"combatLevel":15,"attackSpeed":4,"attackLevel":11,"strengthLevel":13,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"1852":{"name":"Rabbit","hitpoints":1,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1853":{"name":"Rabbit","hitpoints":1,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"1861":{"name":"Tree spirit","hitpoints":25,"combatLevel":14,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":8,"rangeLevel":1,"magicLevel":1},"1862":{"name":"Tree spirit","hitpoints":40,"combatLevel":29,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1},"1863":{"name":"Tree spirit","hitpoints":60,"combatLevel":49,"attackSpeed":4,"attackLevel":38,"strengthLevel":38,"defenceLevel":38,"rangeLevel":1,"magicLevel":1},"1864":{"name":"Tree spirit","hitpoints":86,"combatLevel":79,"attackSpeed":4,"attackLevel":64,"strengthLevel":64,"defenceLevel":64,"rangeLevel":1,"magicLevel":1},"1865":{"name":"Tree spirit","hitpoints":120,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"1866":{"name":"Tree spirit","hitpoints":170,"combatLevel":159,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1},"1870":{"name":"Evil Chicken","hitpoints":120,"combatLevel":159,"attackSpeed":4,"defenceLevel":126,"magicLevel":200},"1871":{"name":"Baby black dragon","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"1872":{"name":"Baby black dragon","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"1874":{"name":"Ice troll runt","hitpoints":60,"combatLevel":74,"attackSpeed":4,"attackLevel":60,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"1875":{"name":"Ice troll male","hitpoints":80,"combatLevel":82,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"1876":{"name":"Ice troll female","hitpoints":80,"combatLevel":82,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"1877":{"name":"Ice troll grunt","hitpoints":80,"combatLevel":102,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"2001":{"name":"Duckling","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2005":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"2006":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"2007":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"2008":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"2018":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"2025":{"name":"Greater demon","hitpoints":89,"combatLevel":92,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"2026":{"name":"Greater demon","hitpoints":89,"combatLevel":92,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"2027":{"name":"Greater demon","hitpoints":89,"combatLevel":92,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"2028":{"name":"Greater demon","hitpoints":89,"combatLevel":92,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"2029":{"name":"Greater demon","hitpoints":89,"combatLevel":92,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"2030":{"name":"Greater demon","hitpoints":89,"combatLevel":92,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"2031":{"name":"Greater demon","hitpoints":89,"combatLevel":92,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"2032":{"name":"Greater demon","hitpoints":89,"combatLevel":92,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"2042":{"name":"Zulrah","hitpoints":500,"combatLevel":725,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":300,"rangeLevel":300,"magicLevel":300,"range":50,"magic":50,"rangeDef":50,"bonusRangeStrength":20,"bonusMagicDamage":20},"2043":{"name":"Zulrah","hitpoints":500,"combatLevel":725,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":300,"rangeLevel":300,"magicLevel":300,"range":50,"magic":50,"rangeDef":300,"bonusRangeStrength":20,"bonusMagicDamage":20},"2044":{"name":"Zulrah","hitpoints":500,"combatLevel":725,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":300,"rangeLevel":300,"magicLevel":300,"range":50,"magic":50,"magicDef":300,"bonusRangeStrength":20,"bonusMagicDamage":20},"2045":{"name":"Snakeling","hitpoints":1,"combatLevel":90,"attackSpeed":3,"attackLevel":140,"strengthLevel":138,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"bonusAttack":120},"2046":{"name":"Snakeling","hitpoints":1,"combatLevel":90,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":185,"bonusAttack":120},"2048":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"2049":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"2050":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"2051":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"2052":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"2054":{"name":"Chaos Elemental","hitpoints":250,"combatLevel":305,"attackSpeed":5,"attackLevel":270,"strengthLevel":270,"defenceLevel":270,"rangeLevel":270,"magicLevel":270,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":70,"magicDef":70},"2056":{"name":"Dark wizard","hitpoints":25,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":10,"defenceLevel":20,"rangeLevel":1,"magicLevel":25,"magicDef":5},"2057":{"name":"Dark wizard","hitpoints":24,"combatLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":10,"defenceLevel":15,"rangeLevel":1,"magicLevel":25,"magicDef":5},"2058":{"name":"Dark wizard","hitpoints":15,"combatLevel":11,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":10,"rangeLevel":1,"magicLevel":10,"magicDef":5},"2059":{"name":"Dark wizard","hitpoints":15,"combatLevel":11,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":10,"rangeLevel":1,"magicLevel":10,"magicDef":5},"2062":{"name":"Oomlie bird","hitpoints":40,"combatLevel":46,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"2063":{"name":"Penguin","hitpoints":4,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":2,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2064":{"name":"Terrorbird","hitpoints":34,"combatLevel":28,"attackSpeed":6,"attackLevel":23,"strengthLevel":23,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"2065":{"name":"Terrorbird","hitpoints":34,"combatLevel":28,"attackSpeed":6,"attackLevel":23,"strengthLevel":23,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"2066":{"name":"Terrorbird","hitpoints":34,"combatLevel":28,"attackSpeed":6,"attackLevel":23,"strengthLevel":23,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"2067":{"name":"Mounted terrorbird gnome","hitpoints":36,"combatLevel":31,"attackSpeed":4,"attackLevel":25,"strengthLevel":25,"defenceLevel":25,"stabDef":16,"slashDef":16,"crushDef":18,"rangeDef":10,"magicDef":15},"2068":{"name":"Mounted terrorbird gnome","hitpoints":55,"combatLevel":49,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":16,"slashDef":16,"crushDef":18,"rangeDef":10,"magicDef":15,"bonusRangeStrength":2},"2075":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2076":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2077":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2078":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2079":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2080":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2081":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2082":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2083":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2084":{"name":"Fire giant","hitpoints":111,"combatLevel":86,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2085":{"name":"Ice giant","hitpoints":70,"combatLevel":53,"attackSpeed":5,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2086":{"name":"Ice giant","hitpoints":70,"combatLevel":53,"attackSpeed":5,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2087":{"name":"Ice giant","hitpoints":70,"combatLevel":53,"attackSpeed":5,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2088":{"name":"Ice giant","hitpoints":70,"combatLevel":53,"attackSpeed":5,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2089":{"name":"Ice giant","hitpoints":70,"combatLevel":53,"attackSpeed":5,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"2090":{"name":"Moss giant","hitpoints":60,"combatLevel":42,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":33,"bonusStrength":31},"2091":{"name":"Moss giant","hitpoints":60,"combatLevel":42,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":33,"bonusStrength":31},"2092":{"name":"Moss giant","hitpoints":60,"combatLevel":42,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":33,"bonusStrength":31},"2093":{"name":"Moss giant","hitpoints":60,"combatLevel":42,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":33,"bonusStrength":31},"2094":{"name":"Jogre","hitpoints":60,"combatLevel":53,"attackSpeed":6,"attackLevel":43,"strengthLevel":43,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":20},"2095":{"name":"Ogre","hitpoints":60,"combatLevel":53,"attackSpeed":6,"attackLevel":43,"strengthLevel":43,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":20},"2096":{"name":"Ogre","hitpoints":60,"combatLevel":53,"attackSpeed":6,"attackLevel":43,"strengthLevel":43,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":20},"2097":{"name":"Cyclops","hitpoints":75,"combatLevel":56,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"2098":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"2099":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"2100":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"2101":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"2102":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"2103":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"2115":{"name":"Thing under the bed","hitpoints":25,"attackSpeed":4},"2120":{"name":"Market Guard","hitpoints":22,"combatLevel":20,"attackSpeed":5,"attackLevel":17,"strengthLevel":18,"defenceLevel":13,"rangeLevel":1,"magicLevel":1,"stabDef":24,"slashDef":14,"crushDef":19,"rangeDef":16,"magicDef":4,"bonusAttack":9,"bonusStrength":7},"2137":{"name":"Cyclops","hitpoints":150,"combatLevel":106,"attackSpeed":4,"attackLevel":95,"strengthLevel":75,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"2138":{"name":"Cyclops","hitpoints":150,"combatLevel":106,"attackSpeed":4,"attackLevel":95,"strengthLevel":75,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"2139":{"name":"Cyclops","hitpoints":150,"combatLevel":106,"attackSpeed":4,"attackLevel":95,"strengthLevel":75,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"2140":{"name":"Cyclops","hitpoints":150,"combatLevel":106,"attackSpeed":4,"attackLevel":95,"strengthLevel":75,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"2141":{"name":"Cyclops","hitpoints":150,"combatLevel":106,"attackSpeed":4,"attackLevel":95,"strengthLevel":75,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"2142":{"name":"Cyclops","hitpoints":150,"combatLevel":106,"attackSpeed":4,"attackLevel":95,"strengthLevel":75,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"2145":{"name":"Undead Druid","hitpoints":140,"combatLevel":105,"attackSpeed":4,"attackLevel":60,"strengthLevel":50,"defenceLevel":60,"magicLevel":115,"magic":100,"stabDef":40,"slashDef":30,"crushDef":80,"rangeDef":40,"magicDef":140,"bonusAttack":50,"bonusStrength":50,"bonusMagicDamage":50},"2154":{"name":"TzHaar-Mej","hitpoints":100,"combatLevel":103,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":120},"2155":{"name":"TzHaar-Mej","hitpoints":100,"combatLevel":103,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":120},"2156":{"name":"TzHaar-Mej","hitpoints":100,"combatLevel":103,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":120},"2157":{"name":"TzHaar-Mej","hitpoints":100,"combatLevel":103,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":120},"2158":{"name":"TzHaar-Mej","hitpoints":100,"combatLevel":103,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":120},"2159":{"name":"TzHaar-Mej","hitpoints":100,"combatLevel":103,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":120},"2160":{"name":"TzHaar-Mej","hitpoints":100,"combatLevel":103,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":120},"2161":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"2162":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"2163":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"2164":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"2165":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"2166":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"2167":{"name":"TzHaar-Xil","hitpoints":120,"combatLevel":133,"attackSpeed":4,"attackLevel":140,"strengthLevel":100,"defenceLevel":100,"rangeLevel":120,"magicLevel":40},"2168":{"name":"TzHaar-Xil","hitpoints":120,"combatLevel":133,"attackSpeed":4,"attackLevel":140,"strengthLevel":100,"defenceLevel":100,"rangeLevel":120,"magicLevel":40},"2169":{"name":"TzHaar-Xil","hitpoints":120,"combatLevel":133,"attackSpeed":4,"attackLevel":140,"strengthLevel":100,"defenceLevel":100,"rangeLevel":120,"magicLevel":40},"2170":{"name":"TzHaar-Xil","hitpoints":120,"combatLevel":133,"attackSpeed":4,"attackLevel":140,"strengthLevel":100,"defenceLevel":100,"rangeLevel":120,"magicLevel":40},"2171":{"name":"TzHaar-Xil","hitpoints":120,"combatLevel":133,"attackSpeed":4,"attackLevel":140,"strengthLevel":100,"defenceLevel":100,"rangeLevel":120,"magicLevel":40},"2172":{"name":"TzHaar-Xil","hitpoints":120,"combatLevel":133,"attackSpeed":4,"attackLevel":140,"strengthLevel":100,"defenceLevel":100,"rangeLevel":120,"magicLevel":40},"2173":{"name":"TzHaar-Ket","hitpoints":140,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":140,"defenceLevel":120,"rangeLevel":1,"magicLevel":40},"2174":{"name":"TzHaar-Ket","hitpoints":140,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":140,"defenceLevel":120,"rangeLevel":1,"magicLevel":40},"2175":{"name":"TzHaar-Ket","hitpoints":140,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":140,"defenceLevel":120,"rangeLevel":1,"magicLevel":40},"2176":{"name":"TzHaar-Ket","hitpoints":140,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":140,"defenceLevel":120,"rangeLevel":1,"magicLevel":40},"2177":{"name":"TzHaar-Ket","hitpoints":140,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":140,"defenceLevel":120,"rangeLevel":1,"magicLevel":40},"2178":{"name":"TzHaar-Ket","hitpoints":140,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":140,"defenceLevel":120,"rangeLevel":1,"magicLevel":40},"2179":{"name":"TzHaar-Ket","hitpoints":140,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":140,"defenceLevel":120,"rangeLevel":1,"magicLevel":40},"2189":{"name":"Tz-Kih","hitpoints":10,"combatLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":30,"defenceLevel":15,"rangeLevel":30,"magicLevel":15},"2190":{"name":"Tz-Kih","hitpoints":10,"combatLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":30,"defenceLevel":15,"rangeLevel":30,"magicLevel":15},"2191":{"name":"Tz-Kek","hitpoints":20,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":60,"defenceLevel":30,"rangeLevel":60,"magicLevel":30},"2192":{"name":"Tz-Kek","hitpoints":20,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":60,"defenceLevel":30,"rangeLevel":60,"magicLevel":30},"2193":{"name":"Tok-Xil","hitpoints":40,"combatLevel":90,"attackSpeed":4,"attackLevel":80,"strengthLevel":120,"defenceLevel":60,"rangeLevel":120,"magicLevel":60},"2194":{"name":"Tok-Xil","hitpoints":40,"combatLevel":90,"attackSpeed":4,"attackLevel":80,"strengthLevel":120,"defenceLevel":60,"rangeLevel":120,"magicLevel":60},"2205":{"name":"Commander Zilyana","hitpoints":255,"combatLevel":596,"attackSpeed":2,"attackLevel":280,"strengthLevel":196,"defenceLevel":300,"rangeLevel":250,"magicLevel":300,"magic":200,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":100,"magicDef":100,"bonusAttack":195,"bonusStrength":20,"poisonImmune":true,"venomImmune":true},"2206":{"name":"Starlight","hitpoints":160,"combatLevel":149,"attackSpeed":5,"attackLevel":120,"strengthLevel":125,"defenceLevel":120,"rangeLevel":1,"magicLevel":125,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":60,"bonusStrength":10},"2207":{"name":"Growler","hitpoints":146,"combatLevel":139,"attackSpeed":5,"attackLevel":100,"strengthLevel":101,"defenceLevel":120,"rangeLevel":1,"magicLevel":150,"stabDef":12,"slashDef":14,"crushDef":14,"rangeDef":5,"magicDef":18,"bonusAttack":10,"bonusStrength":7},"2208":{"name":"Bree","hitpoints":162,"combatLevel":146,"attackSpeed":5,"attackLevel":110,"strengthLevel":80,"defenceLevel":130,"rangeLevel":150,"magicLevel":80,"stabDef":12,"slashDef":14,"crushDef":14,"rangeDef":5,"magicDef":18,"bonusAttack":10,"bonusStrength":7},"2209":{"name":"Saradomin priest","hitpoints":89,"combatLevel":113,"attackSpeed":5,"attackLevel":120,"strengthLevel":46,"defenceLevel":120,"rangeLevel":1,"magicLevel":125,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":9,"bonusStrength":5},"2210":{"name":"Spiritual warrior","hitpoints":110,"combatLevel":125,"slayerLevel":68,"attackSpeed":5,"attackLevel":110,"strengthLevel":106,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":9,"bonusStrength":6},"2211":{"name":"Spiritual ranger","hitpoints":106,"combatLevel":122,"slayerLevel":63,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":146,"magicLevel":1,"stabDef":3,"slashDef":5,"crushDef":13,"rangeDef":23,"magicDef":16},"2212":{"name":"Spiritual mage","hitpoints":85,"combatLevel":120,"slayerLevel":83,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":86,"rangeLevel":1,"magicLevel":160,"stabDef":8,"slashDef":7,"crushDef":3,"rangeDef":2,"magicDef":16},"2213":{"name":"Knight of Saradomin","hitpoints":135,"combatLevel":103,"attackSpeed":6,"attackLevel":70,"strengthLevel":85,"defenceLevel":70,"rangeLevel":1,"magicLevel":60,"stabDef":10,"slashDef":10,"crushDef":7,"rangeDef":13,"bonusAttack":8,"bonusStrength":8},"2214":{"name":"Knight of Saradomin","hitpoints":108,"combatLevel":101,"attackSpeed":6,"attackLevel":75,"strengthLevel":90,"defenceLevel":82,"rangeLevel":1,"magicLevel":60,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"bonusAttack":13,"bonusStrength":11},"2215":{"name":"General Graardor","hitpoints":255,"combatLevel":624,"attackSpeed":6,"attackLevel":280,"strengthLevel":350,"defenceLevel":250,"rangeLevel":350,"magicLevel":80,"range":100,"stabDef":90,"slashDef":90,"crushDef":90,"rangeDef":90,"magicDef":298,"bonusAttack":120,"bonusStrength":43,"bonusRangeStrength":40,"poisonImmune":true,"venomImmune":true},"2216":{"name":"Sergeant Strongstack","hitpoints":128,"combatLevel":141,"attackSpeed":5,"attackLevel":124,"strengthLevel":118,"defenceLevel":125,"rangeLevel":50,"magicLevel":50,"bonusStrength":14},"2217":{"name":"Sergeant Steelwill","hitpoints":127,"combatLevel":142,"attackSpeed":5,"attackLevel":80,"strengthLevel":50,"defenceLevel":150,"rangeLevel":1,"magicLevel":150,"bonusStrength":6},"2218":{"name":"Sergeant Grimspike","hitpoints":146,"combatLevel":142,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":132,"rangeLevel":150,"magicLevel":50,"range":20,"bonusRangeStrength":20},"2232":{"name":"Black Guard Berserker","hitpoints":50,"combatLevel":66,"attackSpeed":3,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"bonusAttack":55,"bonusStrength":55},"2233":{"name":"Ogre","hitpoints":70,"combatLevel":58,"attackSpeed":6,"attackLevel":46,"strengthLevel":48,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":21},"2234":{"name":"Jogre","hitpoints":70,"combatLevel":58,"attackSpeed":6,"attackLevel":46,"strengthLevel":48,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"bonusStrength":21},"2235":{"name":"Cyclops","hitpoints":110,"combatLevel":81,"attackSpeed":4,"attackLevel":60,"strengthLevel":70,"defenceLevel":48,"rangeLevel":1,"magicLevel":1,"bonusAttack":22},"2236":{"name":"Cyclops","hitpoints":110,"combatLevel":81,"attackSpeed":4,"attackLevel":60,"strengthLevel":70,"defenceLevel":48,"rangeLevel":1,"magicLevel":1,"bonusAttack":22},"2237":{"name":"Ork","hitpoints":110,"combatLevel":107,"attackSpeed":5,"attackLevel":100,"strengthLevel":100,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":19,"bonusStrength":4},"2238":{"name":"Ork","hitpoints":110,"combatLevel":107,"attackSpeed":5,"attackLevel":100,"strengthLevel":100,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":19,"bonusStrength":4},"2239":{"name":"Ork","hitpoints":110,"combatLevel":107,"attackSpeed":5,"attackLevel":100,"strengthLevel":100,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":19,"bonusStrength":4},"2240":{"name":"Ork","hitpoints":110,"combatLevel":107,"attackSpeed":5,"attackLevel":100,"strengthLevel":100,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":19,"bonusStrength":4},"2241":{"name":"Hobgoblin","hitpoints":52,"combatLevel":47,"attackSpeed":4,"attackLevel":39,"strengthLevel":39,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"2242":{"name":"Spiritual ranger","hitpoints":131,"combatLevel":115,"slayerLevel":63,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":96,"rangeLevel":121,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5},"2243":{"name":"Spiritual warrior","hitpoints":131,"combatLevel":134,"slayerLevel":68,"attackSpeed":5,"attackLevel":116,"strengthLevel":120,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":29,"bonusStrength":13},"2244":{"name":"Spiritual mage","hitpoints":106,"combatLevel":121,"slayerLevel":83,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":103,"rangeLevel":1,"magicLevel":142,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":35},"2245":{"name":"Goblin","hitpoints":18,"combatLevel":17,"attackSpeed":4,"attackLevel":14,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"2246":{"name":"Goblin","hitpoints":3,"combatLevel":12,"attackSpeed":4,"attackLevel":15,"strengthLevel":6,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"2247":{"name":"Goblin","hitpoints":15,"combatLevel":12,"attackSpeed":4,"attackLevel":12,"strengthLevel":6,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"2248":{"name":"Goblin","hitpoints":16,"combatLevel":15,"attackSpeed":4,"attackLevel":16,"strengthLevel":6,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"2249":{"name":"Goblin","hitpoints":13,"combatLevel":13,"attackSpeed":4,"attackLevel":11,"strengthLevel":6,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"2259":{"name":"Dagannoth","hitpoints":85,"combatLevel":88,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":75,"rangeLevel":100,"magicLevel":1,"slashDef":10,"crushDef":90,"rangeDef":150,"magicDef":200},"2261":{"name":"Giant Rock Crab","hitpoints":180,"combatLevel":137,"attackSpeed":4,"attackLevel":50,"strengthLevel":80,"defenceLevel":200,"rangeLevel":1,"magicLevel":1,"stabDef":225,"slashDef":200,"crushDef":175,"rangeDef":250},"2264":{"name":"Dagannoth fledgeling","hitpoints":100,"combatLevel":70,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"2265":{"name":"Dagannoth Supreme","hitpoints":255,"combatLevel":303,"attackSpeed":4,"attackLevel":255,"strengthLevel":255,"defenceLevel":128,"rangeLevel":255,"magicLevel":255,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":550,"magicDef":255},"2266":{"name":"Dagannoth Prime","hitpoints":255,"combatLevel":303,"attackSpeed":4,"attackLevel":255,"strengthLevel":255,"defenceLevel":255,"magicLevel":255,"stabDef":255,"slashDef":255,"crushDef":255,"rangeDef":10,"magicDef":255},"2267":{"name":"Dagannoth Rex","hitpoints":255,"combatLevel":303,"attackSpeed":4,"attackLevel":255,"strengthLevel":255,"defenceLevel":255,"rangeLevel":255,"stabDef":255,"slashDef":255,"crushDef":255,"rangeDef":255,"magicDef":10},"2316":{"name":"Guard","hitpoints":26,"combatLevel":26,"attackSpeed":6,"attackLevel":25,"strengthLevel":18,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":16,"crushDef":19,"rangeDef":12},"2317":{"name":"Guard","hitpoints":26,"combatLevel":24,"attackSpeed":6,"attackLevel":22,"strengthLevel":16,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"slashDef":15,"crushDef":19,"rangeDef":12},"2423":{"name":"Chaos dwarf","hitpoints":61,"combatLevel":48,"attackSpeed":4,"attackLevel":38,"strengthLevel":42,"defenceLevel":28,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":34,"crushDef":25,"rangeDef":35,"magicDef":10,"bonusAttack":13,"bonusStrength":9},"2450":{"name":"Animated Bronze Armour","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":25,"crushDef":19,"rangeDef":400,"magicDef":400,"bonusAttack":4,"bonusStrength":5},"2451":{"name":"Animated Iron Armour","hitpoints":20,"combatLevel":23,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":25,"crushDef":19,"rangeDef":400,"magicDef":400,"bonusAttack":4,"bonusStrength":5},"2452":{"name":"Animated Steel Armour","hitpoints":40,"combatLevel":46,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":25,"crushDef":19,"rangeDef":400,"magicDef":400,"bonusAttack":4,"bonusStrength":5},"2453":{"name":"Animated Black Armour","hitpoints":60,"combatLevel":69,"attackSpeed":4,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":25,"crushDef":19,"rangeDef":400,"magicDef":400,"bonusAttack":4,"bonusStrength":5},"2454":{"name":"Animated Mithril Armour","hitpoints":80,"combatLevel":92,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":25,"crushDef":19,"rangeDef":400,"magicDef":400,"bonusAttack":4,"bonusStrength":5},"2455":{"name":"Animated Adamant Armour","hitpoints":99,"combatLevel":113,"attackSpeed":4,"attackLevel":99,"strengthLevel":99,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":25,"crushDef":19,"rangeDef":400,"magicDef":400,"bonusAttack":4,"bonusStrength":5},"2456":{"name":"Animated Rune Armour","hitpoints":120,"combatLevel":138,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":25,"crushDef":19,"rangeDef":400,"magicDef":400,"bonusAttack":4,"bonusStrength":5},"2463":{"name":"Cyclops","hitpoints":75,"combatLevel":56,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"2464":{"name":"Cyclops","hitpoints":100,"combatLevel":76,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"2465":{"name":"Cyclops","hitpoints":75,"combatLevel":56,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"2466":{"name":"Cyclops","hitpoints":100,"combatLevel":76,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"2467":{"name":"Cyclops","hitpoints":75,"combatLevel":56,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"2468":{"name":"Cyclops","hitpoints":100,"combatLevel":76,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"2474":{"name":"Catablepon","hitpoints":40,"combatLevel":49,"attackSpeed":4,"attackLevel":45,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":60,"stabDef":40,"slashDef":30,"crushDef":20,"rangeDef":40,"magicDef":20},"2475":{"name":"Catablepon","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":45,"stabDef":50,"slashDef":40,"crushDef":30,"rangeDef":50,"magicDef":30},"2476":{"name":"Catablepon","hitpoints":50,"combatLevel":68,"attackSpeed":4,"attackLevel":65,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":60,"stabDef":50,"slashDef":40,"crushDef":30,"rangeDef":50,"magicDef":30},"2477":{"name":"Giant spider","hitpoints":50,"combatLevel":50,"attackSpeed":4,"attackLevel":41,"strengthLevel":51,"defenceLevel":31,"rangeLevel":1,"magicLevel":1},"2478":{"name":"Spider","hitpoints":22,"combatLevel":24,"attackSpeed":6,"attackLevel":21,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":53,"slashDef":53,"crushDef":53,"rangeDef":53,"magicDef":53,"bonusAttack":35,"bonusStrength":58},"2479":{"name":"Scorpion","hitpoints":55,"combatLevel":59,"attackSpeed":4,"attackLevel":50,"strengthLevel":52,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":55},"2480":{"name":"Scorpion","hitpoints":37,"combatLevel":37,"attackSpeed":4,"attackLevel":31,"strengthLevel":32,"defenceLevel":31,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":15,"crushDef":15,"rangeDef":35,"magicDef":30},"2481":{"name":"Minotaur","hitpoints":10,"combatLevel":12,"attackSpeed":4,"attackLevel":12,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2483":{"name":"Minotaur","hitpoints":22,"combatLevel":27,"attackSpeed":4,"attackLevel":23,"strengthLevel":25,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"2484":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"2485":{"name":"Goblin","hitpoints":16,"combatLevel":13,"attackSpeed":4,"attackLevel":12,"strengthLevel":13,"defenceLevel":7,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":6,"crushDef":8,"rangeDef":4,"magicDef":4},"2486":{"name":"Goblin","hitpoints":7,"combatLevel":11,"attackSpeed":6,"attackLevel":10,"strengthLevel":3,"defenceLevel":7,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"2487":{"name":"Goblin","hitpoints":22,"combatLevel":16,"attackSpeed":4,"attackLevel":13,"strengthLevel":11,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":22},"2488":{"name":"Goblin","hitpoints":26,"combatLevel":25,"attackSpeed":4,"attackLevel":22,"strengthLevel":23,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":6,"crushDef":8,"rangeDef":4,"magicDef":4},"2489":{"name":"Goblin","hitpoints":22,"combatLevel":16,"attackSpeed":4,"attackLevel":13,"strengthLevel":11,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":22},"2490":{"name":"Wolf","hitpoints":15,"combatLevel":14,"attackSpeed":4,"attackLevel":10,"strengthLevel":15,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2491":{"name":"Wolf","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2492":{"name":"Rat","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2498":{"name":"Flesh Crawler","hitpoints":25,"combatLevel":28,"attackSpeed":3,"attackLevel":60,"strengthLevel":2,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":15,"crushDef":15,"rangeDef":15,"magicDef":15},"2499":{"name":"Flesh Crawler","hitpoints":25,"combatLevel":35,"attackSpeed":3,"attackLevel":80,"strengthLevel":2,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":15,"crushDef":15,"rangeDef":15,"magicDef":15},"2500":{"name":"Flesh Crawler","hitpoints":25,"combatLevel":41,"attackSpeed":3,"attackLevel":100,"strengthLevel":2,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":15,"crushDef":15,"rangeDef":15,"magicDef":15},"2501":{"name":"Zombie","hitpoints":30,"combatLevel":30,"attackSpeed":4,"attackLevel":25,"strengthLevel":29,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"2502":{"name":"Zombie","hitpoints":30,"combatLevel":30,"attackSpeed":4,"attackLevel":25,"strengthLevel":29,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"2503":{"name":"Zombie","hitpoints":30,"combatLevel":30,"attackSpeed":4,"attackLevel":25,"strengthLevel":29,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"2504":{"name":"Zombie","hitpoints":40,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"2505":{"name":"Zombie","hitpoints":40,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"2506":{"name":"Zombie","hitpoints":40,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"2507":{"name":"Zombie","hitpoints":50,"combatLevel":53,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":48,"rangeLevel":1,"magicLevel":1},"2508":{"name":"Zombie","hitpoints":50,"combatLevel":53,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":48,"rangeLevel":1,"magicLevel":1},"2509":{"name":"Zombie","hitpoints":50,"combatLevel":53,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":48,"rangeLevel":1,"magicLevel":1},"2510":{"name":"Giant rat","hitpoints":25,"combatLevel":26,"attackSpeed":4,"attackLevel":22,"strengthLevel":23,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"2511":{"name":"Giant rat","hitpoints":25,"combatLevel":26,"attackSpeed":4,"attackLevel":22,"strengthLevel":23,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"2512":{"name":"Giant rat","hitpoints":25,"combatLevel":26,"attackSpeed":4,"attackLevel":22,"strengthLevel":23,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"2513":{"name":"Rat","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2514":{"name":"Ankou","hitpoints":60,"combatLevel":75,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":60,"rangeLevel":1,"magicLevel":1},"2515":{"name":"Ankou","hitpoints":65,"combatLevel":82,"attackSpeed":4,"attackLevel":80,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"2516":{"name":"Ankou","hitpoints":70,"combatLevel":86,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"2517":{"name":"Ankou","hitpoints":60,"combatLevel":75,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":60,"rangeLevel":1,"magicLevel":1},"2518":{"name":"Ankou","hitpoints":65,"combatLevel":82,"attackSpeed":4,"attackLevel":80,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"2519":{"name":"Ankou","hitpoints":70,"combatLevel":86,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"2520":{"name":"Skeleton","hitpoints":70,"combatLevel":68,"attackSpeed":4,"attackLevel":60,"strengthLevel":50,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":35,"rangeDef":35},"2521":{"name":"Skeleton","hitpoints":70,"combatLevel":60,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":35,"rangeDef":35},"2522":{"name":"Skeleton","hitpoints":70,"combatLevel":60,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":35,"rangeDef":35},"2523":{"name":"Skeleton","hitpoints":70,"combatLevel":60,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":35,"rangeDef":35},"2524":{"name":"Skeleton","hitpoints":77,"combatLevel":85,"attackSpeed":4,"attackLevel":74,"strengthLevel":74,"defenceLevel":74,"rangeLevel":1,"magicLevel":1,"stabDef":79,"slashDef":31,"crushDef":20,"rangeDef":70,"magicDef":5,"bonusAttack":15,"bonusStrength":14},"2525":{"name":"Skeleton","hitpoints":77,"combatLevel":85,"attackSpeed":4,"attackLevel":74,"strengthLevel":74,"defenceLevel":74,"rangeLevel":1,"magicLevel":1,"stabDef":79,"slashDef":31,"crushDef":20,"rangeDef":70,"magicDef":5,"bonusAttack":15,"bonusStrength":14},"2526":{"name":"Skeleton","hitpoints":77,"combatLevel":85,"attackSpeed":4,"attackLevel":74,"strengthLevel":74,"defenceLevel":74,"rangeLevel":1,"magicLevel":1,"stabDef":79,"slashDef":31,"crushDef":20,"rangeDef":70,"magicDef":5,"bonusAttack":15,"bonusStrength":14},"2527":{"name":"Ghost","hitpoints":80,"combatLevel":77,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":55,"slashDef":55,"crushDef":5,"rangeDef":55,"magicDef":55},"2528":{"name":"Ghost","hitpoints":80,"combatLevel":77,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":55,"slashDef":55,"crushDef":5,"rangeDef":55,"magicDef":55},"2529":{"name":"Ghost","hitpoints":80,"combatLevel":77,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":55,"slashDef":55,"crushDef":5,"rangeDef":55,"magicDef":55},"2530":{"name":"Ghost","hitpoints":80,"combatLevel":77,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":55,"slashDef":55,"crushDef":5,"rangeDef":55,"magicDef":55},"2531":{"name":"Ghost","hitpoints":75,"combatLevel":76,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":45,"crushDef":5,"rangeDef":45},"2532":{"name":"Ghost","hitpoints":75,"combatLevel":76,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":45,"crushDef":5,"rangeDef":45},"2533":{"name":"Ghost","hitpoints":75,"combatLevel":76,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":45,"crushDef":5,"rangeDef":45},"2534":{"name":"Ghost","hitpoints":75,"combatLevel":76,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":45,"crushDef":5,"rangeDef":45},"2536":{"name":"H.A.M. Guard","hitpoints":15,"combatLevel":12,"attackSpeed":7,"attackLevel":8,"strengthLevel":12,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"2537":{"name":"H.A.M. Guard","hitpoints":20,"combatLevel":18,"attackSpeed":6,"attackLevel":14,"strengthLevel":18,"defenceLevel":12,"rangeLevel":1,"magicLevel":1,"stabDef":6,"slashDef":6,"rangeDef":6},"2538":{"name":"H.A.M. Guard","hitpoints":30,"combatLevel":22,"attackSpeed":5,"attackLevel":16,"strengthLevel":20,"defenceLevel":12,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"2579":{"name":"Monk","hitpoints":15,"combatLevel":5,"attackSpeed":4,"attackLevel":2,"strengthLevel":2,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"2584":{"name":"Abyssal leech","hitpoints":10,"combatLevel":41,"attackSpeed":2,"attackLevel":95,"strengthLevel":5,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":100,"rangeDef":10,"magicDef":50,"bonusAttack":100,"bonusStrength":10},"2585":{"name":"Abyssal guardian","hitpoints":55,"combatLevel":59,"attackSpeed":4,"attackLevel":30,"strengthLevel":90,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":70,"magicDef":150,"bonusAttack":15,"bonusStrength":5},"2586":{"name":"Abyssal walker","hitpoints":95,"combatLevel":81,"attackSpeed":5,"attackLevel":5,"strengthLevel":100,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":75,"slashDef":75,"crushDef":10,"rangeDef":75,"magicDef":75,"bonusAttack":5,"bonusStrength":10},"2592":{"name":"Mogre","hitpoints":48,"combatLevel":60,"slayerLevel":32,"attackSpeed":6,"attackLevel":58,"strengthLevel":55,"defenceLevel":48,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":20,"poisonImmune":true,"venomImmune":true},"2593":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2594":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2595":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2596":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2597":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2598":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2599":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2600":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2601":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2602":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2603":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2604":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2605":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2606":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2607":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2608":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2609":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2610":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2611":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2612":{"name":"Werewolf","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"magicDef":60},"2613":{"name":"Boris","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2614":{"name":"Imre","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2615":{"name":"Yuri","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2616":{"name":"Joseph","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2617":{"name":"Nikolai","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2618":{"name":"Eduard","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2619":{"name":"Lev","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2620":{"name":"Georgy","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2621":{"name":"Svetlana","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2622":{"name":"Irina","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2623":{"name":"Alexis","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2624":{"name":"Milla","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2625":{"name":"Galina","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2626":{"name":"Sofiya","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2627":{"name":"Ksenia","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2628":{"name":"Yadviga","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2629":{"name":"Nikita","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2630":{"name":"Vera","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2631":{"name":"Zoja","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2632":{"name":"Liliya","hitpoints":60,"combatLevel":24,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2634":{"name":"Myre Blamish Snail","hitpoints":8,"combatLevel":9,"attackSpeed":4,"defenceLevel":22,"rangeLevel":5,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":50,"magicDef":5,"bonusAttack":10,"bonusStrength":10},"2645":{"name":"Blood Blamish Snail","hitpoints":13,"combatLevel":20,"attackSpeed":6,"defenceLevel":45,"rangeLevel":12,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":50,"bonusAttack":10,"bonusStrength":10},"2646":{"name":"Ochre Blamish Snail","hitpoints":10,"combatLevel":10,"attackSpeed":6,"defenceLevel":18,"rangeLevel":7,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":50,"magicDef":5,"bonusAttack":10,"bonusStrength":10},"2647":{"name":"Bruise Blamish Snail","hitpoints":12,"combatLevel":20,"attackSpeed":6,"defenceLevel":40,"rangeLevel":15,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":50,"magicDef":5,"bonusAttack":10,"bonusStrength":10},"2648":{"name":"Bark Blamish Snail","hitpoints":22,"combatLevel":15,"attackSpeed":6,"defenceLevel":20,"rangeLevel":10,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":30,"magicDef":5,"bonusAttack":10,"bonusStrength":10},"2649":{"name":"Myre Blamish Snail","hitpoints":13,"combatLevel":10,"attackSpeed":4,"defenceLevel":12,"rangeLevel":8,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":50,"magicDef":5,"bonusAttack":10,"bonusStrength":10},"2650":{"name":"Blood Blamish Snail","hitpoints":10,"combatLevel":20,"attackSpeed":6,"defenceLevel":30,"rangeLevel":21,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":50,"bonusAttack":10,"bonusStrength":10},"2651":{"name":"Ochre Blamish Snail","hitpoints":20,"combatLevel":15,"attackSpeed":6,"defenceLevel":25,"rangeLevel":8,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":50,"magicDef":5,"bonusAttack":10,"bonusStrength":10},"2652":{"name":"Bruise Blamish Snail","hitpoints":15,"combatLevel":20,"attackSpeed":6,"defenceLevel":27,"rangeLevel":20,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":50,"magicDef":5,"bonusAttack":10,"bonusStrength":10},"2790":{"name":"Cow","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"2791":{"name":"Cow","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"2792":{"name":"Cow calf","hitpoints":6,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2793":{"name":"Cow","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"2794":{"name":"Cow calf","hitpoints":6,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2795":{"name":"Cow","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"2801":{"name":"Cow calf","hitpoints":6,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2804":{"name":"Chicken","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2805":{"name":"Chicken","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2806":{"name":"Chicken","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2827":{"name":"Bat","hitpoints":8,"combatLevel":6,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":5,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":2,"crushDef":5,"magicDef":2},"2834":{"name":"Giant bat","hitpoints":32,"combatLevel":27,"attackSpeed":4,"attackLevel":22,"strengthLevel":22,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":12,"rangeDef":8,"magicDef":10},"2837":{"name":"Unicorn","hitpoints":19,"combatLevel":15,"attackSpeed":4,"attackLevel":11,"strengthLevel":13,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"2838":{"name":"Grizzly bear","hitpoints":27,"combatLevel":21,"attackSpeed":4,"attackLevel":17,"strengthLevel":18,"defenceLevel":15,"rangeLevel":1,"magicLevel":1},"2839":{"name":"Black bear","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":15,"strengthLevel":16,"defenceLevel":13},"2840":{"name":"Earth warrior","hitpoints":54,"combatLevel":51,"attackSpeed":4,"attackLevel":42,"strengthLevel":42,"defenceLevel":42,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":40,"crushDef":20,"rangeDef":30,"magicDef":10},"2841":{"name":"Ice warrior","hitpoints":59,"combatLevel":57,"attackSpeed":4,"attackLevel":47,"strengthLevel":47,"defenceLevel":47,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":40,"crushDef":20,"rangeDef":30,"magicDef":10},"2842":{"name":"Ice warrior","hitpoints":59,"combatLevel":57,"attackSpeed":4,"attackLevel":47,"strengthLevel":47,"defenceLevel":47,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":40,"crushDef":20,"rangeDef":30,"magicDef":10},"2843":{"name":"Otherworldly being","hitpoints":66,"combatLevel":64,"attackSpeed":4,"attackLevel":56,"strengthLevel":56,"defenceLevel":46,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":10,"crushDef":20,"rangeDef":15},"2844":{"name":"Magic axe","hitpoints":44,"combatLevel":42,"attackSpeed":4,"attackLevel":38,"strengthLevel":38,"defenceLevel":29,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":5,"crushDef":15,"rangeDef":10,"magicDef":5},"2845":{"name":"Snake","hitpoints":6,"combatLevel":5,"attackSpeed":4,"attackLevel":4,"strengthLevel":5,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"2848":{"name":"Monkey","hitpoints":6,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2849":{"name":"Black unicorn","hitpoints":29,"combatLevel":27,"attackSpeed":4,"attackLevel":21,"strengthLevel":23,"defenceLevel":23,"rangeLevel":1,"magicLevel":1},"2851":{"name":"Ice warrior","hitpoints":59,"combatLevel":57,"attackSpeed":4,"attackLevel":47,"strengthLevel":47,"defenceLevel":47,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":40,"crushDef":20,"rangeDef":30,"magicDef":10},"2853":{"name":"Shadow warrior","hitpoints":67,"combatLevel":48,"attackSpeed":4,"attackLevel":36,"strengthLevel":36,"defenceLevel":33,"stabDef":43,"slashDef":31,"crushDef":19,"rangeDef":38,"magicDef":15,"bonusAttack":20,"bonusStrength":26},"2854":{"name":"Rat","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2855":{"name":"Rat","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2856":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2857":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2858":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2859":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2860":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2861":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2862":{"name":"Giant rat","hitpoints":10,"combatLevel":6,"attackSpeed":4,"attackLevel":6,"strengthLevel":5,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2863":{"name":"Giant rat","hitpoints":10,"combatLevel":6,"attackSpeed":4,"attackLevel":6,"strengthLevel":5,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2864":{"name":"Giant rat","hitpoints":10,"combatLevel":6,"attackSpeed":4,"attackLevel":6,"strengthLevel":5,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2865":{"name":"Dungeon rat","hitpoints":12,"combatLevel":12,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2866":{"name":"Dungeon rat","hitpoints":12,"combatLevel":12,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2867":{"name":"Dungeon rat","hitpoints":12,"combatLevel":12,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"2916":{"name":"Waterfiend","hitpoints":128,"combatLevel":115,"attackSpeed":4,"defenceLevel":128,"rangeLevel":105,"magicLevel":105,"stabDef":100,"slashDef":100,"crushDef":10,"rangeDef":100,"magicDef":100,"poisonImmune":true,"venomImmune":true},"2917":{"name":"Waterfiend","hitpoints":128,"combatLevel":115,"attackSpeed":4,"defenceLevel":128,"rangeLevel":105,"magicLevel":105,"stabDef":100,"slashDef":100,"crushDef":10,"rangeDef":100,"magicDef":100,"poisonImmune":true,"venomImmune":true},"2918":{"name":"Brutal green dragon","hitpoints":175,"combatLevel":227,"attackSpeed":4,"attackLevel":268,"strengthLevel":168,"defenceLevel":168,"magicLevel":168,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60,"poisonImmune":true,"venomImmune":true},"2919":{"name":"Mithril dragon","hitpoints":254,"combatLevel":304,"attackSpeed":4,"attackLevel":268,"strengthLevel":268,"defenceLevel":268,"rangeLevel":168,"magicLevel":168,"stabDef":50,"slashDef":100,"crushDef":70,"rangeDef":90,"magicDef":30,"poisonImmune":true,"venomImmune":true},"2920":{"name":"Confused barbarian","hitpoints":124,"combatLevel":132,"attackSpeed":4,"attackLevel":115,"strengthLevel":114,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"2921":{"name":"Lost barbarian","hitpoints":124,"combatLevel":132,"attackSpeed":4,"attackLevel":115,"strengthLevel":114,"defenceLevel":110,"rangeLevel":1,"magicLevel":1},"2946":{"name":"Nail beast","hitpoints":55,"combatLevel":69,"attackSpeed":8,"attackLevel":150,"strengthLevel":6,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"2947":{"name":"Nail beast","hitpoints":65,"combatLevel":98,"attackSpeed":8,"attackLevel":215,"strengthLevel":16,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"2948":{"name":"Nail beast","hitpoints":75,"combatLevel":141,"attackSpeed":8,"attackLevel":320,"strengthLevel":26,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"2954":{"name":"Zamorak wizard","hitpoints":73,"combatLevel":65,"attackSpeed":4,"attackLevel":24,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":80,"magicDef":3},"2955":{"name":"Saradomin wizard","hitpoints":120,"combatLevel":108,"attackSpeed":4,"attackLevel":100,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":80,"magicDef":30,"bonusAttack":40,"bonusStrength":40},"2978":{"name":"Big Snake","hitpoints":120,"combatLevel":84,"attackSpeed":6,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"bonusAttack":66,"bonusStrength":62},"2992":{"name":"Undead cow","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2993":{"name":"Undead chicken","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"2994":{"name":"Giant lobster","hitpoints":32,"combatLevel":32,"attackSpeed":4,"attackLevel":18,"strengthLevel":26,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":5,"rangeDef":5},"2999":{"name":"Tortured soul","hitpoints":51,"combatLevel":59,"attackSpeed":4,"attackLevel":52,"strengthLevel":62,"defenceLevel":38,"rangeLevel":1,"magicLevel":1},"3010":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"3011":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"3014":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3015":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3016":{"name":"Shadow spider","hitpoints":55,"combatLevel":52,"attackSpeed":4,"attackLevel":44,"strengthLevel":42,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":15,"crushDef":10,"rangeDef":15,"magicDef":15},"3017":{"name":"Giant spider","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3018":{"name":"Giant spider","hitpoints":32,"combatLevel":27,"attackSpeed":4,"attackLevel":20,"strengthLevel":24,"defenceLevel":21,"rangeLevel":1,"magicLevel":1},"3019":{"name":"Spider","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3020":{"name":"Jungle spider","hitpoints":50,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":37,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":10,"rangeDef":20,"magicDef":17},"3021":{"name":"Deadly red spider","hitpoints":35,"combatLevel":34,"attackSpeed":4,"attackLevel":30,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":16,"crushDef":7,"rangeDef":16,"magicDef":12},"3022":{"name":"Ice spider","hitpoints":65,"combatLevel":61,"attackSpeed":4,"attackLevel":50,"strengthLevel":55,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":17,"crushDef":12,"rangeDef":13,"magicDef":13},"3023":{"name":"Poison spider","hitpoints":64,"combatLevel":64,"attackSpeed":6,"attackLevel":50,"strengthLevel":52,"defenceLevel":58,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":17,"crushDef":10,"rangeDef":14,"magicDef":14},"3024":{"name":"Scorpion","hitpoints":17,"combatLevel":14,"attackSpeed":4,"attackLevel":11,"strengthLevel":12,"defenceLevel":11,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"3025":{"name":"Poison Scorpion","hitpoints":23,"combatLevel":20,"attackSpeed":4,"attackLevel":16,"strengthLevel":17,"defenceLevel":15,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"3026":{"name":"Pit Scorpion","hitpoints":32,"combatLevel":28,"attackSpeed":4,"attackLevel":23,"strengthLevel":23,"defenceLevel":23,"rangeLevel":1,"magicLevel":1,"slashDef":5,"crushDef":5},"3027":{"name":"King Scorpion","hitpoints":30,"combatLevel":32,"attackSpeed":4,"attackLevel":30,"strengthLevel":29,"defenceLevel":23,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"3028":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3029":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3030":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3031":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3032":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3033":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3034":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3035":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3036":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3037":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3038":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3039":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3040":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3041":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3042":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3043":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3044":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3045":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"3046":{"name":"Goblin","hitpoints":16,"combatLevel":13,"attackSpeed":4,"attackLevel":12,"strengthLevel":13,"defenceLevel":7,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":6,"crushDef":8,"rangeDef":4,"magicDef":4},"3047":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"3048":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"3049":{"name":"Hobgoblin","hitpoints":29,"combatLevel":28,"attackSpeed":4,"attackLevel":22,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1},"3050":{"name":"Hobgoblin","hitpoints":49,"combatLevel":42,"attackSpeed":4,"attackLevel":33,"strengthLevel":31,"defenceLevel":36,"rangeLevel":1,"magicLevel":1,"bonusAttack":8,"bonusStrength":10},"3051":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3052":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3053":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3054":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3055":{"name":"Barbarian","hitpoints":24,"combatLevel":17,"attackSpeed":4,"attackLevel":15,"strengthLevel":14,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3056":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3057":{"name":"Barbarian","hitpoints":24,"combatLevel":17,"attackSpeed":4,"attackLevel":15,"strengthLevel":14,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3058":{"name":"Barbarian","hitpoints":24,"combatLevel":17,"attackSpeed":4,"attackLevel":15,"strengthLevel":14,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3059":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3060":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3061":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3062":{"name":"Barbarian","hitpoints":24,"combatLevel":17,"attackSpeed":4,"attackLevel":15,"strengthLevel":14,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3064":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3065":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3066":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3067":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3068":{"name":"Barbarian","hitpoints":24,"combatLevel":15,"attackSpeed":4,"attackLevel":15,"strengthLevel":3,"defenceLevel":10,"rangeLevel":15,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3069":{"name":"Barbarian","hitpoints":24,"combatLevel":17,"attackSpeed":4,"attackLevel":15,"strengthLevel":14,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3070":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3071":{"name":"Barbarian","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"3072":{"name":"Barbarian","hitpoints":20,"combatLevel":9,"attackSpeed":4,"attackLevel":6,"strengthLevel":7,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":10,"crushDef":10,"rangeDef":5,"bonusAttack":9,"bonusStrength":16},"3073":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"3074":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"3075":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"3076":{"name":"Goblin","hitpoints":12,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":12},"3106":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3107":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3108":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3109":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3110":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3111":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3112":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3113":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3114":{"name":"Farmer","hitpoints":12,"combatLevel":7,"attackSpeed":6,"attackLevel":3,"strengthLevel":4,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"bonusAttack":5,"bonusStrength":6},"3116":{"name":"Tz-Kih","hitpoints":10,"combatLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":30,"defenceLevel":15,"rangeLevel":30,"magicLevel":15},"3117":{"name":"Tz-Kih","hitpoints":10,"combatLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":30,"defenceLevel":15,"rangeLevel":30,"magicLevel":15},"3118":{"name":"Tz-Kek","hitpoints":20,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":60,"defenceLevel":30,"rangeLevel":60,"magicLevel":30},"3119":{"name":"Tz-Kek","hitpoints":20,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":60,"defenceLevel":30,"rangeLevel":60,"magicLevel":30},"3120":{"name":"Tz-Kek","hitpoints":10,"combatLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":30,"defenceLevel":15,"rangeLevel":30,"magicLevel":15},"3121":{"name":"Tok-Xil","hitpoints":40,"combatLevel":90,"attackSpeed":4,"attackLevel":80,"strengthLevel":120,"defenceLevel":60,"rangeLevel":120,"magicLevel":60},"3122":{"name":"Tok-Xil","hitpoints":40,"combatLevel":90,"attackSpeed":4,"attackLevel":80,"strengthLevel":120,"defenceLevel":60,"rangeLevel":120,"magicLevel":60},"3123":{"name":"Yt-MejKot","hitpoints":80,"combatLevel":180,"attackSpeed":4,"attackLevel":160,"strengthLevel":240,"defenceLevel":120,"rangeLevel":240,"magicLevel":120},"3124":{"name":"Yt-MejKot","hitpoints":80,"combatLevel":180,"attackSpeed":4,"attackLevel":160,"strengthLevel":240,"defenceLevel":120,"rangeLevel":240,"magicLevel":120},"3125":{"name":"Ket-Zek","hitpoints":160,"combatLevel":360,"attackSpeed":4,"attackLevel":320,"strengthLevel":480,"defenceLevel":240,"rangeLevel":480,"magicLevel":240,"magic":60},"3126":{"name":"Ket-Zek","hitpoints":160,"combatLevel":360,"attackSpeed":4,"attackLevel":320,"strengthLevel":480,"defenceLevel":240,"rangeLevel":480,"magicLevel":240,"magic":60},"3127":{"name":"TzTok-Jad","hitpoints":250,"combatLevel":702,"attackSpeed":8,"attackLevel":640,"strengthLevel":960,"defenceLevel":480,"rangeLevel":960,"magicLevel":480,"magic":60},"3128":{"name":"Yt-HurKot","hitpoints":60,"combatLevel":108,"attackSpeed":4,"attackLevel":140,"strengthLevel":100,"defenceLevel":60,"rangeLevel":120,"magicLevel":120,"rangeDef":100,"magicDef":100},"3129":{"name":"K'ril Tsutsaroth","hitpoints":255,"combatLevel":650,"attackSpeed":6,"attackLevel":340,"strengthLevel":300,"defenceLevel":270,"rangeLevel":1,"magicLevel":200,"stabDef":80,"slashDef":80,"crushDef":80,"rangeDef":80,"magicDef":130,"bonusAttack":160,"bonusStrength":31},"3130":{"name":"Tstanon Karlak","hitpoints":142,"combatLevel":145,"attackSpeed":5,"attackLevel":124,"strengthLevel":118,"defenceLevel":125,"rangeLevel":50,"magicLevel":50,"bonusStrength":14},"3131":{"name":"Zakl'n Gritch","hitpoints":150,"combatLevel":142,"attackSpeed":5,"attackLevel":83,"strengthLevel":76,"defenceLevel":127,"rangeLevel":150,"magicLevel":50,"range":20,"bonusRangeStrength":20},"3132":{"name":"Balfrug Kreeyath","hitpoints":161,"combatLevel":151,"attackSpeed":5,"attackLevel":115,"strengthLevel":60,"defenceLevel":153,"rangeLevel":1,"magicLevel":150,"magicDef":10},"3133":{"name":"Hellhound","hitpoints":116,"combatLevel":127,"attackSpeed":4,"attackLevel":107,"strengthLevel":116,"defenceLevel":106,"rangeLevel":1,"magicLevel":1},"3134":{"name":"Imp","hitpoints":10,"combatLevel":7,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":6,"rangeLevel":1,"magicLevel":1},"3135":{"name":"Werewolf","hitpoints":92,"combatLevel":93,"attackSpeed":4,"attackLevel":77,"strengthLevel":76,"defenceLevel":85,"rangeLevel":1,"magicLevel":1},"3136":{"name":"Werewolf","hitpoints":92,"combatLevel":93,"attackSpeed":4,"attackLevel":77,"strengthLevel":76,"defenceLevel":85,"rangeLevel":1,"magicLevel":1},"3137":{"name":"Feral Vampyre","hitpoints":60,"combatLevel":77,"attackSpeed":4,"attackLevel":65,"strengthLevel":66,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"3138":{"name":"Bloodveld","hitpoints":134,"combatLevel":81,"slayerLevel":50,"attackSpeed":4,"attackLevel":80,"strengthLevel":46,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"3139":{"name":"Pyrefiend","hitpoints":48,"combatLevel":48,"slayerLevel":30,"attackSpeed":4,"attackLevel":60,"strengthLevel":36,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10},"3140":{"name":"Icefiend","hitpoints":20,"combatLevel":18,"attackSpeed":4,"attackLevel":12,"strengthLevel":16,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"3141":{"name":"Gorak","hitpoints":128,"combatLevel":149,"attackSpeed":4,"attackLevel":133,"strengthLevel":126,"defenceLevel":135,"rangeLevel":1,"magicLevel":1},"3159":{"name":"Spiritual warrior","hitpoints":100,"combatLevel":115,"slayerLevel":68,"attackSpeed":5,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"3160":{"name":"Spiritual ranger","hitpoints":120,"combatLevel":118,"slayerLevel":63,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":80,"rangeLevel":140,"magicLevel":1},"3161":{"name":"Spiritual mage","hitpoints":75,"combatLevel":121,"slayerLevel":83,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":61,"rangeLevel":1,"magicLevel":180},"3162":{"name":"Kree'arra","hitpoints":255,"combatLevel":580,"attackSpeed":3,"attackLevel":300,"strengthLevel":200,"defenceLevel":260,"rangeLevel":380,"magicLevel":200,"range":120,"stabDef":180,"slashDef":180,"crushDef":180,"rangeDef":200,"magicDef":200,"bonusAttack":136,"bonusStrength":12,"bonusRangeStrength":50},"3163":{"name":"Wingman Skree","hitpoints":121,"combatLevel":143,"attackSpeed":5,"attackLevel":80,"strengthLevel":50,"defenceLevel":160,"rangeLevel":100,"magicLevel":150,"bonusAttack":45,"bonusStrength":25},"3164":{"name":"Flockleader Geerin","hitpoints":132,"combatLevel":149,"attackSpeed":5,"attackLevel":80,"strengthLevel":80,"defenceLevel":175,"rangeLevel":150,"magicLevel":50,"range":60,"bonusRangeStrength":35},"3165":{"name":"Flight Kilisa","hitpoints":133,"combatLevel":159,"attackSpeed":5,"attackLevel":124,"strengthLevel":118,"defenceLevel":175,"rangeLevel":169,"magicLevel":50,"bonusStrength":14},"3166":{"name":"Spiritual warrior","hitpoints":98,"combatLevel":123,"slayerLevel":68,"attackSpeed":5,"attackLevel":1,"strengthLevel":1,"defenceLevel":120,"rangeLevel":141,"magicLevel":1,"stabDef":23,"slashDef":25,"crushDef":13,"rangeDef":35,"magicDef":35},"3167":{"name":"Spiritual ranger","hitpoints":89,"combatLevel":127,"slayerLevel":63,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":130,"rangeLevel":150,"magicLevel":1,"stabDef":17,"slashDef":16,"crushDef":8,"rangeDef":41,"magicDef":35},"3168":{"name":"Spiritual mage","hitpoints":75,"combatLevel":123,"slayerLevel":83,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":111,"rangeLevel":1,"magicLevel":150,"stabDef":9,"slashDef":12,"crushDef":5,"rangeDef":28,"magicDef":45},"3169":{"name":"Aviansie","hitpoints":70,"combatLevel":69,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":70,"rangeLevel":71,"magicLevel":1},"3170":{"name":"Aviansie","hitpoints":83,"combatLevel":79,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":70,"rangeLevel":85,"magicLevel":1},"3171":{"name":"Aviansie","hitpoints":86,"combatLevel":84,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":70,"rangeLevel":93,"magicLevel":1},"3172":{"name":"Aviansie","hitpoints":86,"combatLevel":83,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":76,"magicLevel":1},"3173":{"name":"Aviansie","hitpoints":95,"combatLevel":92,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":90,"magicLevel":1},"3174":{"name":"Aviansie","hitpoints":98,"combatLevel":97,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":99,"magicLevel":1},"3175":{"name":"Aviansie","hitpoints":124,"combatLevel":137,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":160,"rangeLevel":136,"magicLevel":1},"3176":{"name":"Aviansie","hitpoints":139,"combatLevel":148,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":160,"rangeLevel":151,"magicLevel":1},"3177":{"name":"Aviansie","hitpoints":63,"combatLevel":71,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":55,"rangeLevel":86,"magicLevel":1},"3178":{"name":"Aviansie","hitpoints":67,"combatLevel":73,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":55,"rangeLevel":89,"magicLevel":1},"3179":{"name":"Aviansie","hitpoints":77,"combatLevel":79,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":55,"rangeLevel":96,"magicLevel":1},"3180":{"name":"Aviansie","hitpoints":69,"combatLevel":89,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":115,"rangeLevel":89,"magicLevel":1},"3181":{"name":"Aviansie","hitpoints":75,"combatLevel":94,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":115,"rangeLevel":96,"magicLevel":1},"3182":{"name":"Aviansie","hitpoints":79,"combatLevel":97,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":115,"rangeLevel":101,"magicLevel":1},"3183":{"name":"Aviansie","hitpoints":115,"combatLevel":131,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":175,"rangeLevel":143,"magicLevel":1},"3184":{"name":"Dagannoth spawn","hitpoints":35,"combatLevel":42,"attackSpeed":4,"attackLevel":50,"strengthLevel":35,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"3185":{"name":"Dagannoth","hitpoints":95,"combatLevel":90,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50,"bonusAttack":50,"bonusStrength":50},"3233":{"name":"Leech","hitpoints":45,"combatLevel":52,"attackSpeed":4,"attackLevel":80,"strengthLevel":20,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"3234":{"name":"Feral Vampyre","hitpoints":50,"combatLevel":72,"attackSpeed":4,"attackLevel":65,"strengthLevel":70,"defenceLevel":65,"rangeLevel":1,"magicLevel":50},"3237":{"name":"Feral Vampyre","hitpoints":40,"combatLevel":61,"attackSpeed":4,"attackLevel":55,"strengthLevel":60,"defenceLevel":55,"rangeLevel":1,"magicLevel":40},"3243":{"name":"Farmer","hitpoints":12,"combatLevel":7,"attackSpeed":6,"attackLevel":3,"strengthLevel":4,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"bonusAttack":5,"bonusStrength":6},"3244":{"name":"Farmer","hitpoints":12,"combatLevel":7,"attackSpeed":6,"attackLevel":3,"strengthLevel":4,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"bonusAttack":5,"bonusStrength":6},"3245":{"name":"Farmer","hitpoints":12,"combatLevel":7,"attackSpeed":6,"attackLevel":3,"strengthLevel":4,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"bonusAttack":5,"bonusStrength":6},"3250":{"name":"Farmer","hitpoints":12,"combatLevel":7,"attackSpeed":6,"attackLevel":3,"strengthLevel":4,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"bonusAttack":5,"bonusStrength":6},"3251":{"name":"Farmer","hitpoints":12,"combatLevel":7,"attackSpeed":6,"attackLevel":3,"strengthLevel":4,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"bonusAttack":5,"bonusStrength":6},"3252":{"name":"Thief","hitpoints":17,"combatLevel":16,"attackSpeed":4,"attackLevel":14,"strengthLevel":13,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"3253":{"name":"Thief","hitpoints":17,"combatLevel":16,"attackSpeed":4,"attackLevel":14,"strengthLevel":13,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"3254":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"3257":{"name":"Wizard","hitpoints":14,"combatLevel":9,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":5,"rangeLevel":1,"magicLevel":10,"magicDef":3},"3258":{"name":"Druid","hitpoints":30,"combatLevel":33,"attackSpeed":4,"attackLevel":28,"strengthLevel":28,"defenceLevel":32,"rangeLevel":1,"magicLevel":25},"3260":{"name":"Warrior woman","hitpoints":20,"combatLevel":24,"attackSpeed":4,"attackLevel":22,"strengthLevel":22,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"range":6,"magic":6,"stabDef":40,"slashDef":41,"crushDef":37,"rangeDef":38,"bonusAttack":6,"bonusStrength":7},"3261":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3262":{"name":"Barbarian","hitpoints":14,"combatLevel":8,"attackSpeed":6,"attackLevel":6,"strengthLevel":5,"defenceLevel":5,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":1,"bonusAttack":8,"bonusStrength":10},"3263":{"name":"Drunken man","hitpoints":7,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":3,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3264":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3265":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3268":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3269":{"name":"Guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"3270":{"name":"Guard","hitpoints":22,"combatLevel":22,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":26,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":6,"bonusStrength":10,"bonusRangeStrength":10},"3271":{"name":"Guard","hitpoints":22,"combatLevel":19,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":6,"bonusStrength":10},"3272":{"name":"Guard","hitpoints":22,"combatLevel":22,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":26,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":6,"bonusStrength":10},"3273":{"name":"Guard","hitpoints":22,"combatLevel":22,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":26,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":6,"bonusStrength":10},"3274":{"name":"Guard","hitpoints":22,"combatLevel":22,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":26,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":6,"bonusStrength":10},"3275":{"name":"Gardener","hitpoints":7,"combatLevel":4,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"3276":{"name":"Gardener","hitpoints":7,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":1,"defenceLevel":5,"rangeLevel":1,"magicLevel":1},"3279":{"name":"Cuffs","hitpoints":7,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":3,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5,"magicDef":5},"3280":{"name":"Narf","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3281":{"name":"Rusty","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3282":{"name":"Jeff","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3283":{"name":"Guard","hitpoints":22,"combatLevel":19,"attackSpeed":6,"attackLevel":15,"strengthLevel":15,"defenceLevel":16,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":6,"bonusStrength":10},"3284":{"name":"Hengel","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3285":{"name":"Anja","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3286":{"name":"Hobgoblin","hitpoints":29,"combatLevel":28,"attackSpeed":4,"attackLevel":22,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1},"3287":{"name":"Hobgoblin","hitpoints":29,"combatLevel":28,"attackSpeed":4,"attackLevel":22,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1},"3288":{"name":"Hobgoblin","hitpoints":29,"combatLevel":28,"attackSpeed":4,"attackLevel":22,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1},"3289":{"name":"Hobgoblin","hitpoints":29,"combatLevel":28,"attackSpeed":4,"attackLevel":22,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1},"3292":{"name":"Al-Kharid warrior","hitpoints":19,"combatLevel":9,"attackSpeed":4,"attackLevel":7,"strengthLevel":5,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":15,"crushDef":10,"rangeDef":12,"bonusAttack":10,"bonusStrength":9},"3293":{"name":"Paladin","hitpoints":66,"combatLevel":62,"attackSpeed":5,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":22},"3294":{"name":"Paladin","hitpoints":66,"combatLevel":62,"attackSpeed":5,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":22},"3295":{"name":"Hero","hitpoints":82,"combatLevel":69,"attackSpeed":5,"attackLevel":54,"strengthLevel":55,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":22},"3297":{"name":"Knight of Ardougne","hitpoints":52,"combatLevel":46,"attackSpeed":5,"attackLevel":38,"strengthLevel":40,"defenceLevel":31,"rangeLevel":1,"magicLevel":1,"stabDef":39,"slashDef":40,"crushDef":36,"rangeDef":36,"bonusAttack":8,"bonusStrength":10},"3298":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3299":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3300":{"name":"Knight of Ardougne","hitpoints":52,"combatLevel":46,"attackSpeed":5,"attackLevel":38,"strengthLevel":40,"defenceLevel":31,"rangeLevel":1,"magicLevel":1,"stabDef":39,"slashDef":40,"crushDef":36,"rangeDef":36,"bonusAttack":8,"bonusStrength":10},"3301":{"name":"Archer","hitpoints":50,"combatLevel":37,"attackSpeed":6,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":40,"magicLevel":1,"range":19,"stabDef":18,"slashDef":23,"crushDef":27,"rangeDef":19,"magicDef":10,"bonusRangeStrength":8},"3313":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"3314":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"3315":{"name":"Giant rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"3316":{"name":"Chicken","hitpoints":3,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3328":{"name":"Earth Warrior Champion","hitpoints":108,"combatLevel":102,"attackSpeed":4,"attackLevel":84,"strengthLevel":84,"defenceLevel":84,"rangeLevel":1,"magicLevel":1},"3329":{"name":"Giant Champion","hitpoints":70,"combatLevel":56,"attackSpeed":4,"attackLevel":36,"strengthLevel":44,"defenceLevel":52,"rangeLevel":1,"magicLevel":1},"3330":{"name":"Ghoul Champion","hitpoints":100,"combatLevel":85,"attackSpeed":4},"3353":{"name":"Goblin Champion","hitpoints":32,"combatLevel":24,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":14,"rangeLevel":1,"magicLevel":26},"3354":{"name":"Hobgoblin Champion","hitpoints":58,"combatLevel":56,"attackSpeed":4,"attackLevel":44,"strengthLevel":48,"defenceLevel":48,"rangeLevel":44,"magicLevel":1},"3355":{"name":"Imp Champion","hitpoints":40,"combatLevel":14,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":5,"rangeLevel":5,"magicLevel":1},"3356":{"name":"Jogre Champion","hitpoints":120,"combatLevel":107,"attackSpeed":4,"attackLevel":86,"strengthLevel":86,"defenceLevel":86,"rangeLevel":1,"magicLevel":1},"3357":{"name":"Lesser Demon Champion","hitpoints":148,"combatLevel":162,"attackSpeed":4,"attackLevel":136,"strengthLevel":140,"defenceLevel":142,"rangeLevel":1,"magicLevel":136},"3358":{"name":"Skeleton Champion","hitpoints":58,"combatLevel":40,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":34,"rangeLevel":36,"magicLevel":1},"3359":{"name":"Zombies Champion","hitpoints":60,"combatLevel":51,"attackSpeed":4,"attackLevel":38,"strengthLevel":42,"defenceLevel":42,"rangeLevel":1,"magicLevel":1},"3360":{"name":"Leon d'Cour","hitpoints":123,"combatLevel":141,"attackSpeed":4},"3361":{"name":"Kourend guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"3400":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"3420":{"name":"Rabbit","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3421":{"name":"Rabbit","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3422":{"name":"Rabbit","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3423":{"name":"Grizzly bear","hitpoints":35,"combatLevel":42,"attackSpeed":4,"attackLevel":40,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"3424":{"name":"Grizzly bear cub","hitpoints":35,"combatLevel":33,"attackSpeed":4,"attackLevel":30,"strengthLevel":26,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"3425":{"name":"Grizzly bear cub","hitpoints":35,"combatLevel":36,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":28,"rangeLevel":1,"magicLevel":1},"3426":{"name":"Dire Wolf","hitpoints":85,"combatLevel":88,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":1},"3428":{"name":"Iorwerth Archer","hitpoints":105,"combatLevel":90,"attackSpeed":5,"attackLevel":10,"strengthLevel":10,"defenceLevel":80,"rangeLevel":90,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"3429":{"name":"Iorwerth Warrior","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":80,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"3433":{"name":"Tyras guard","hitpoints":110,"combatLevel":110,"attackSpeed":5,"attackLevel":85,"strengthLevel":95,"defenceLevel":100,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":30},"3434":{"name":"Tyras guard","hitpoints":110,"combatLevel":110,"attackSpeed":5,"attackLevel":85,"strengthLevel":95,"defenceLevel":100,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":30},"3443":{"name":"Lucien","hitpoints":17,"combatLevel":14,"attackSpeed":4,"attackLevel":12,"strengthLevel":11,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":6,"slashDef":8,"crushDef":7,"rangeDef":4,"bonusAttack":5,"bonusStrength":4},"3444":{"name":"Lucien","hitpoints":17,"combatLevel":14,"attackSpeed":4,"attackLevel":12,"strengthLevel":11,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":6,"slashDef":8,"crushDef":7,"rangeDef":4,"bonusAttack":5,"bonusStrength":4},"3445":{"name":"Guardian of Armadyl","hitpoints":50,"combatLevel":45,"attackSpeed":5,"attackLevel":37,"strengthLevel":37,"defenceLevel":37,"rangeLevel":1,"magicLevel":1,"stabDef":43,"slashDef":55,"crushDef":50,"rangeDef":51,"bonusAttack":14,"bonusStrength":16},"3446":{"name":"Guardian of Armadyl","hitpoints":40,"combatLevel":43,"attackSpeed":5,"attackLevel":37,"strengthLevel":37,"defenceLevel":37,"rangeLevel":1,"magicLevel":1,"stabDef":43,"slashDef":55,"crushDef":50,"rangeDef":51,"bonusAttack":14,"bonusStrength":16},"3448":{"name":"Fire Warrior of Lesarkus","hitpoints":59,"combatLevel":84,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":78,"rangeLevel":1,"magicLevel":62,"stabDef":34,"slashDef":37,"crushDef":35,"rangeDef":35},"3449":{"name":"Shadow Hound","hitpoints":62,"combatLevel":63,"attackSpeed":4,"attackLevel":53,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1},"3456":{"name":"Fareed","hitpoints":130,"combatLevel":167,"attackSpeed":4,"attackLevel":190,"strengthLevel":120,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"bonusStrength":120},"3458":{"name":"Kamil","hitpoints":130,"combatLevel":154,"attackSpeed":4,"attackLevel":190,"strengthLevel":80,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":60,"crushDef":35,"bonusAttack":60,"bonusStrength":100},"3459":{"name":"Dessous","hitpoints":200,"combatLevel":139,"attackSpeed":3,"attackLevel":99,"strengthLevel":99,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":150,"crushDef":150,"bonusAttack":50,"bonusStrength":50},"3460":{"name":"Dessous","hitpoints":200,"combatLevel":139,"attackSpeed":3,"attackLevel":99,"strengthLevel":99,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":150,"crushDef":150,"bonusAttack":50,"bonusStrength":50},"3473":{"name":"The Inadequacy","hitpoints":180,"combatLevel":343,"attackSpeed":4,"attackLevel":564,"strengthLevel":170,"defenceLevel":240,"rangeLevel":170,"magicLevel":1},"3474":{"name":"The Everlasting","hitpoints":230,"combatLevel":223,"attackSpeed":6,"attackLevel":187,"strengthLevel":231,"defenceLevel":120,"rangeLevel":1,"magicLevel":1},"3475":{"name":"The Untouchable","hitpoints":90,"combatLevel":274,"attackSpeed":6,"attackLevel":187,"strengthLevel":255,"defenceLevel":434,"rangeLevel":1,"magicLevel":1},"3476":{"name":"The Illusive","hitpoints":140,"combatLevel":108,"attackSpeed":6},"3477":{"name":"A Doubt","hitpoints":50,"combatLevel":78,"attackSpeed":6},"3478":{"name":"The Illusive","hitpoints":140,"combatLevel":108,"attackSpeed":6},"3481":{"name":"Count Draynor","hitpoints":35,"combatLevel":34,"attackSpeed":4,"attackLevel":30,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":1,"crushDef":3},"3482":{"name":"Count Draynor","hitpoints":35,"combatLevel":34,"attackSpeed":4,"attackLevel":30,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":1,"crushDef":3},"3484":{"name":"Monk of Zamorak","hitpoints":20,"combatLevel":22,"attackSpeed":4,"attackLevel":18,"strengthLevel":18,"defenceLevel":22,"rangeLevel":1,"magicLevel":25},"3485":{"name":"Monk of Zamorak","hitpoints":10,"combatLevel":17,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":12,"rangeLevel":1,"magicLevel":25},"3486":{"name":"Monk of Zamorak","hitpoints":25,"combatLevel":30,"attackSpeed":4,"attackLevel":25,"strengthLevel":25,"defenceLevel":25,"rangeLevel":1,"magicLevel":40},"3508":{"name":"Bouncer","hitpoints":120,"combatLevel":160,"attackSpeed":4,"attackLevel":145,"strengthLevel":145,"defenceLevel":145,"rangeLevel":1,"magicLevel":1},"3509":{"name":"Bouncer","hitpoints":120,"combatLevel":160,"attackSpeed":4,"attackLevel":145,"strengthLevel":145,"defenceLevel":145,"rangeLevel":1,"magicLevel":1},"3517":{"name":"Renegade Knight","hitpoints":48,"combatLevel":37,"attackSpeed":4,"attackLevel":28,"strengthLevel":28,"defenceLevel":28,"rangeLevel":1,"magicLevel":1,"stabDef":99,"slashDef":82,"crushDef":62,"bonusAttack":20,"bonusStrength":16},"3518":{"name":"Thrantax the Mighty","hitpoints":80,"combatLevel":92,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"3527":{"name":"Sir Mordred","hitpoints":38,"combatLevel":39,"attackSpeed":4,"attackLevel":33,"strengthLevel":33,"defenceLevel":33,"rangeLevel":1,"magicLevel":1,"stabDef":99,"slashDef":82,"crushDef":45,"bonusAttack":20,"bonusStrength":16},"3544":{"name":"Desert snake","hitpoints":6,"combatLevel":5,"attackSpeed":4,"attackLevel":4,"strengthLevel":5,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"3549":{"name":"Menaphite Thug","hitpoints":60,"combatLevel":55,"attackSpeed":4,"attackLevel":60,"strengthLevel":50,"defenceLevel":20,"stabDef":9,"slashDef":8,"crushDef":10,"bonusAttack":4,"bonusStrength":9},"3550":{"name":"Menaphite Thug","hitpoints":60,"combatLevel":55,"attackSpeed":4,"attackLevel":60,"strengthLevel":50,"defenceLevel":20,"stabDef":9,"slashDef":8,"crushDef":10,"bonusAttack":4,"bonusStrength":9},"3551":{"name":"Tough Guy","hitpoints":75,"combatLevel":75,"attackSpeed":4,"attackLevel":85,"strengthLevel":50,"defenceLevel":50,"magicLevel":80,"stabDef":9,"slashDef":8,"crushDef":10,"bonusAttack":9,"bonusStrength":9,"poisonImmune":true},"3565":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4},"3600":{"name":"Frogeel","hitpoints":90,"combatLevel":103,"attackSpeed":4,"attackLevel":91,"strengthLevel":89,"defenceLevel":90,"rangeLevel":1,"magicLevel":1},"3601":{"name":"Unicow","hitpoints":24,"combatLevel":25,"attackSpeed":4,"attackLevel":18,"strengthLevel":26,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"3602":{"name":"Spidine","hitpoints":35,"combatLevel":42,"attackSpeed":4,"attackLevel":37,"strengthLevel":40,"defenceLevel":36,"rangeLevel":1,"magicLevel":1},"3603":{"name":"Swordchick","hitpoints":35,"combatLevel":46,"attackSpeed":4,"attackLevel":41,"strengthLevel":42,"defenceLevel":45,"rangeLevel":1,"magicLevel":1},"3604":{"name":"Jubster","hitpoints":60,"combatLevel":87,"attackSpeed":4,"attackLevel":82,"strengthLevel":78,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"3605":{"name":"Newtroost","hitpoints":18,"combatLevel":19,"attackSpeed":4,"attackLevel":18,"strengthLevel":16,"defenceLevel":17,"rangeLevel":1,"magicLevel":1},"3607":{"name":"Dungeon rat","hitpoints":12,"combatLevel":12,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"3608":{"name":"Dungeon rat","hitpoints":12,"combatLevel":12,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"3609":{"name":"Dungeon rat","hitpoints":12,"combatLevel":12,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"3612":{"name":"Possessed pickaxe","hitpoints":40,"combatLevel":50,"attackSpeed":4,"attackLevel":40,"strengthLevel":55,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"3615":{"name":"Skeletal miner","hitpoints":39,"combatLevel":42,"attackSpeed":4,"attackLevel":35,"strengthLevel":37,"defenceLevel":38,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"3616":{"name":"Treus Dayth","hitpoints":100,"combatLevel":95,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"3625":{"name":"Ghost","hitpoints":27,"combatLevel":29,"attackSpeed":4,"attackLevel":23,"strengthLevel":23,"defenceLevel":28,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"3651":{"name":"Gardener","hitpoints":7,"combatLevel":4,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"3652":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3661":{"name":"Chicken","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3662":{"name":"Chicken","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3663":{"name":"Rooster","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3664":{"name":"Rabbit","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3665":{"name":"Rabbit","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3674":{"name":"Ragnar","hitpoints":1,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3675":{"name":"Einar","hitpoints":1,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3676":{"name":"Alrik","hitpoints":1,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3677":{"name":"Thorhild","hitpoints":1,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3678":{"name":"Halla","hitpoints":1,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3681":{"name":"Rannveig","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"3682":{"name":"Thora","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"3683":{"name":"Valgerd","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"3684":{"name":"Skraeling","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"3685":{"name":"Broddi","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"3686":{"name":"Skraeling","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"3687":{"name":"Ragnvald","hitpoints":1,"combatLevel":2,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"3690":{"name":"Vampyre Juvinate","hitpoints":85,"combatLevel":54,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"3691":{"name":"Vampyre Juvinate","hitpoints":85,"combatLevel":54,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"3692":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"3693":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"3694":{"name":"Vampyre Juvinate","hitpoints":85,"combatLevel":54,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"3695":{"name":"Vampyre Juvinate","hitpoints":85,"combatLevel":54,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"3696":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"3697":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"3698":{"name":"Vampyre Juvinate","hitpoints":85,"combatLevel":54,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"3699":{"name":"Vampyre Juvinate","hitpoints":85,"combatLevel":54,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"3707":{"name":"Feral Vampyre","hitpoints":80,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":30,"defenceLevel":60,"rangeLevel":1,"magicLevel":1},"3708":{"name":"Feral Vampyre","hitpoints":80,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":30,"defenceLevel":60,"rangeLevel":1,"magicLevel":1},"3709":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3710":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3711":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3712":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3713":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3714":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3715":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3716":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3717":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3718":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3719":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3720":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3721":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3722":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3723":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3724":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3725":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3726":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3727":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3728":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3729":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3730":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3731":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3732":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3734":{"name":"Vanstrom Klause","hitpoints":155,"combatLevel":169,"attackSpeed":8,"attackLevel":150,"strengthLevel":136,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"poisonImmune":true,"venomImmune":true},"3735":{"name":"Vanstrom Klause","hitpoints":155,"combatLevel":169,"attackSpeed":8,"attackLevel":150,"strengthLevel":136,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"poisonImmune":true,"venomImmune":true},"3736":{"name":"Vanstrom Klause","hitpoints":155,"combatLevel":169,"attackSpeed":8,"attackLevel":150,"strengthLevel":136,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"poisonImmune":true,"venomImmune":true},"3737":{"name":"Vanstrom Klause","hitpoints":155,"combatLevel":169,"attackSpeed":8,"attackLevel":150,"strengthLevel":136,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"poisonImmune":true,"venomImmune":true},"3738":{"name":"Vanstrom Klause","hitpoints":155,"combatLevel":169,"attackSpeed":8,"attackLevel":150,"strengthLevel":136,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"poisonImmune":true,"venomImmune":true},"3739":{"name":"Vanstrom Klause","hitpoints":155,"combatLevel":169,"attackSpeed":8,"attackLevel":150,"strengthLevel":136,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"poisonImmune":true,"venomImmune":true},"3748":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3749":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3750":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3751":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3752":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3753":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3754":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3755":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3756":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3757":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3758":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3759":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3760":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"3761":{"name":"Vyrewatch","hitpoints":90,"combatLevel":110,"attackSpeed":8,"attackLevel":110,"strengthLevel":96,"defenceLevel":85,"rangeLevel":110,"magicLevel":110},"3762":{"name":"Vyrewatch","hitpoints":105,"combatLevel":120,"attackSpeed":8,"attackLevel":120,"strengthLevel":106,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3763":{"name":"Vyrewatch","hitpoints":110,"combatLevel":125,"attackSpeed":8,"attackLevel":120,"strengthLevel":116,"defenceLevel":85,"rangeLevel":120,"magicLevel":120},"3851":{"name":"Moss giant","hitpoints":85,"combatLevel":48,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":33,"bonusStrength":31},"3852":{"name":"Moss giant","hitpoints":85,"combatLevel":48,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":33,"bonusStrength":31},"3863":{"name":"Jake","hitpoints":50,"combatLevel":37,"attackSpeed":4,"attackLevel":27,"strengthLevel":31,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"3865":{"name":"Wilson","hitpoints":50,"combatLevel":37,"attackSpeed":4,"attackLevel":27,"strengthLevel":31,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"3869":{"name":"Palmer","hitpoints":50,"combatLevel":37,"attackSpeed":4,"attackLevel":27,"strengthLevel":31,"defenceLevel":25,"rangeLevel":1,"magicLevel":1},"3897":{"name":"Koschei the deathless","hitpoints":30,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20},"3898":{"name":"Koschei the deathless","hitpoints":50,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":40,"magicDef":40},"3899":{"name":"Koschei the deathless","hitpoints":70,"attackSpeed":4,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60},"3900":{"name":"Koschei the deathless","hitpoints":255,"attackSpeed":1,"attackLevel":255,"strengthLevel":5,"defenceLevel":255},"3901":{"name":"Fox","hitpoints":30,"combatLevel":19,"attackSpeed":4,"attackLevel":1,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":30,"crushDef":30,"rangeDef":30,"magicDef":30,"bonusAttack":30,"bonusStrength":30},"3902":{"name":"Bunny","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3903":{"name":"Bunny","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"3908":{"name":"Bear Cub","hitpoints":20,"combatLevel":15,"attackSpeed":4,"attackLevel":10,"strengthLevel":15,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"3909":{"name":"Bear Cub","hitpoints":20,"combatLevel":15,"attackSpeed":4,"attackLevel":10,"strengthLevel":15,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"3910":{"name":"Unicorn Foal","hitpoints":15,"combatLevel":12,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"3911":{"name":"Black unicorn Foal","hitpoints":25,"combatLevel":22,"attackSpeed":4,"attackLevel":18,"strengthLevel":18,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"3912":{"name":"Wolf","hitpoints":69,"combatLevel":64,"attackSpeed":4,"attackLevel":50,"strengthLevel":55,"defenceLevel":52,"rangeLevel":1,"magicLevel":1},"3922":{"name":"The Draugen","hitpoints":60,"combatLevel":69,"attackSpeed":4,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":500,"magicDef":500,"poisonImmune":true,"venomImmune":true},"3938":{"name":"Freidir","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"bonusStrength":20},"3939":{"name":"Borrokar","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"bonusStrength":20},"3940":{"name":"Lanzig","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"bonusStrength":20},"3943":{"name":"Lensa","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"bonusStrength":20},"3944":{"name":"Jennella","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"bonusStrength":20},"3949":{"name":"Market Guard","hitpoints":50,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50,"bonusAttack":40,"bonusStrength":40},"3950":{"name":"Warrior","hitpoints":50,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50,"bonusAttack":40,"bonusStrength":40},"3957":{"name":"Ungadulu","hitpoints":65,"combatLevel":70,"attackSpeed":4,"attackLevel":57,"strengthLevel":65,"defenceLevel":61,"magicLevel":65,"bonusStrength":3,"bonusRangeStrength":7},"3958":{"name":"Ungadulu","hitpoints":150,"combatLevel":169,"attackSpeed":4,"attackLevel":147,"strengthLevel":147,"defenceLevel":147,"bonusStrength":3,"bonusRangeStrength":7},"3959":{"name":"Jungle savage","hitpoints":90,"combatLevel":90,"attackSpeed":4,"attackLevel":76,"strengthLevel":76,"defenceLevel":76,"bonusAttack":8,"bonusStrength":10},"3962":{"name":"Nezikchened","hitpoints":150,"combatLevel":187,"attackSpeed":5,"attackLevel":165,"strengthLevel":168,"defenceLevel":167,"rangeLevel":160,"magicLevel":160},"3963":{"name":"Viyeldi","hitpoints":80,"combatLevel":79,"attackSpeed":4,"attackLevel":62,"strengthLevel":66,"defenceLevel":70,"bonusAttack":7,"bonusStrength":3},"3964":{"name":"San Tojalon","hitpoints":120,"combatLevel":106,"attackSpeed":4,"attackLevel":86,"strengthLevel":84,"defenceLevel":86,"stabDef":18,"slashDef":22,"crushDef":20,"rangeDef":20,"bonusAttack":16,"bonusStrength":17},"3965":{"name":"Irvig Senay","hitpoints":125,"combatLevel":100,"attackSpeed":4,"attackLevel":76,"strengthLevel":74,"defenceLevel":81,"stabDef":27,"slashDef":31,"crushDef":29,"rangeDef":29,"bonusAttack":29,"bonusStrength":31},"3966":{"name":"Ranalph Devere","hitpoints":130,"combatLevel":92,"attackSpeed":4,"attackLevel":66,"strengthLevel":67,"defenceLevel":66,"stabDef":38,"slashDef":40,"crushDef":36,"rangeDef":38,"bonusAttack":45,"bonusStrength":44},"3969":{"name":"Zombie rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"3970":{"name":"Zombie rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"3971":{"name":"Zombie rat","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":3,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"3972":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"3973":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"3974":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":11,"rangeDef":4,"magicDef":1,"bonusAttack":15,"bonusStrength":14},"3975":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"3976":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"3977":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"3978":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"3979":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"3980":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":5,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":8,"crushDef":12,"rangeDef":11,"magicDef":10,"bonusAttack":5,"bonusStrength":7},"3981":{"name":"Zombie","hitpoints":30,"combatLevel":24,"attackSpeed":5,"attackLevel":19,"strengthLevel":21,"defenceLevel":16,"rangeLevel":1,"magicLevel":1,"stabDef":9,"slashDef":8,"crushDef":12,"rangeDef":11,"magicDef":10,"bonusAttack":5,"bonusStrength":7},"3982":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"3996":{"name":"Witch's experiment","hitpoints":21,"combatLevel":19,"attackSpeed":4,"attackLevel":18,"strengthLevel":10,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"3997":{"name":"Witch's experiment (second form)","hitpoints":31,"combatLevel":30,"attackSpeed":4,"attackLevel":28,"strengthLevel":20,"defenceLevel":29,"rangeLevel":1,"magicLevel":1},"3998":{"name":"Witch's experiment (third form)","hitpoints":41,"combatLevel":42,"attackSpeed":4,"attackLevel":38,"strengthLevel":30,"defenceLevel":39,"rangeLevel":1,"magicLevel":1},"3999":{"name":"Witch's experiment (fourth form)","hitpoints":51,"combatLevel":53,"attackSpeed":4,"attackLevel":48,"strengthLevel":40,"defenceLevel":49,"rangeLevel":1,"magicLevel":1},"4004":{"name":"Shadow","hitpoints":15,"combatLevel":73,"attackSpeed":4,"attackLevel":90,"strengthLevel":120,"defenceLevel":5,"rangeLevel":1,"magicLevel":90,"stabDef":30,"slashDef":30,"crushDef":30,"rangeDef":30,"magicDef":30},"4005":{"name":"Dark beast","hitpoints":220,"combatLevel":182,"slayerLevel":90,"attackSpeed":4,"attackLevel":140,"strengthLevel":160,"defenceLevel":120,"rangeLevel":1,"magicLevel":160,"stabDef":30,"slashDef":40,"crushDef":100,"rangeDef":100,"magicDef":90},"4043":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4044":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4045":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4046":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4047":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4048":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4049":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4050":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4051":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4052":{"name":"Pirate","hitpoints":20,"combatLevel":57},"4067":{"name":"Black Knight Titan","hitpoints":142,"combatLevel":120,"attackSpeed":7,"attackLevel":91,"strengthLevel":100,"defenceLevel":91,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":27,"crushDef":18,"rangeDef":1000,"magicDef":1000,"bonusAttack":27,"bonusStrength":22},"4068":{"name":"Monk","hitpoints":15,"combatLevel":5,"attackSpeed":4,"attackLevel":2,"strengthLevel":2,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4095":{"name":"Eadburg","hitpoints":10,"combatLevel":4,"attackSpeed":4,"attackLevel":3,"strengthLevel":2,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4096":{"name":"Archer","hitpoints":50,"combatLevel":42,"attackSpeed":6,"defenceLevel":40,"rangeLevel":40,"magicLevel":1,"stabDef":39,"slashDef":50,"crushDef":49,"rangeDef":45,"magicDef":18,"bonusAttack":34},"4097":{"name":"Archer","hitpoints":50,"combatLevel":42,"attackSpeed":6,"defenceLevel":40,"rangeLevel":40,"magicLevel":1,"stabDef":39,"slashDef":50,"crushDef":49,"rangeDef":45,"magicDef":18,"bonusAttack":34},"4098":{"name":"Archer","hitpoints":50,"combatLevel":42,"attackSpeed":6,"defenceLevel":40,"rangeLevel":40,"magicLevel":1,"stabDef":39,"slashDef":50,"crushDef":49,"rangeDef":45,"magicDef":18,"bonusAttack":34},"4099":{"name":"Guard","hitpoints":40,"combatLevel":37,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"magicLevel":1,"stabDef":36,"slashDef":48,"crushDef":50,"rangeDef":38,"bonusAttack":18,"bonusStrength":16},"4100":{"name":"Guard","hitpoints":40,"combatLevel":37,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"magicLevel":1,"stabDef":36,"slashDef":48,"crushDef":50,"rangeDef":38,"bonusAttack":18,"bonusStrength":16},"4104":{"name":"Servant","hitpoints":10,"combatLevel":5,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4107":{"name":"Breoca","hitpoints":10,"combatLevel":5,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4108":{"name":"Ocga","hitpoints":10,"combatLevel":5,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4109":{"name":"Penda","hitpoints":10,"combatLevel":5,"attackSpeed":4,"attackLevel":3,"strengthLevel":3,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4110":{"name":"Hygd","hitpoints":10,"combatLevel":4,"attackSpeed":4,"attackLevel":3,"strengthLevel":2,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4111":{"name":"Ceolburg","hitpoints":10,"combatLevel":4,"attackSpeed":4,"attackLevel":3,"strengthLevel":2,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4114":{"name":"White Knight","hitpoints":55,"combatLevel":42,"attackSpeed":7,"attackLevel":32,"strengthLevel":35,"defenceLevel":27,"rangeLevel":1,"magicLevel":1,"stabDef":83,"slashDef":76,"crushDef":70,"rangeDef":74,"bonusAttack":30,"bonusStrength":31},"4115":{"name":"Fareed (hard)","hitpoints":255,"combatLevel":299,"attackSpeed":4,"attackLevel":380,"strengthLevel":240,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"bonusStrength":120},"4120":{"name":"Troll general","hitpoints":140,"combatLevel":113,"attackSpeed":4,"attackLevel":70,"strengthLevel":140,"defenceLevel":40,"stabDef":35,"slashDef":60,"crushDef":35,"rangeDef":200,"magicDef":200,"bonusAttack":60,"bonusStrength":100},"4121":{"name":"Troll general","hitpoints":140,"combatLevel":113,"attackSpeed":4,"attackLevel":70,"strengthLevel":140,"defenceLevel":40,"stabDef":35,"slashDef":60,"crushDef":35,"rangeDef":200,"magicDef":200,"bonusAttack":60,"bonusStrength":100},"4122":{"name":"Troll general","hitpoints":140,"combatLevel":113,"attackSpeed":4,"attackLevel":70,"strengthLevel":140,"defenceLevel":40,"stabDef":35,"slashDef":60,"crushDef":35,"rangeDef":200,"magicDef":200,"bonusAttack":60,"bonusStrength":100},"4123":{"name":"Troll spectator","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4124":{"name":"Troll spectator","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4125":{"name":"Troll spectator","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4126":{"name":"Troll spectator","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4127":{"name":"Troll spectator","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4128":{"name":"Troll spectator","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4129":{"name":"Troll spectator","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4130":{"name":"Dad","hitpoints":120,"combatLevel":101,"attackSpeed":8,"attackLevel":60,"strengthLevel":120,"defenceLevel":50,"stabDef":25,"slashDef":25,"crushDef":40,"rangeDef":200,"magicDef":200,"bonusAttack":40,"bonusStrength":70},"4131":{"name":"Twig","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4132":{"name":"Berry","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4133":{"name":"Twig","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4134":{"name":"Berry","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4135":{"name":"Thrower troll","hitpoints":95,"combatLevel":68,"attackSpeed":7,"attackLevel":30,"strengthLevel":95,"defenceLevel":15,"rangeLevel":60,"rangeDef":200,"magicDef":200},"4136":{"name":"Thrower troll","hitpoints":95,"combatLevel":68,"attackSpeed":7,"attackLevel":30,"strengthLevel":95,"defenceLevel":15,"rangeLevel":60,"rangeDef":200,"magicDef":200},"4137":{"name":"Thrower troll","hitpoints":95,"combatLevel":68,"attackSpeed":7,"attackLevel":30,"strengthLevel":95,"defenceLevel":15,"rangeLevel":60,"rangeDef":200,"magicDef":200},"4138":{"name":"Thrower troll","hitpoints":95,"combatLevel":68,"attackSpeed":7,"attackLevel":30,"strengthLevel":95,"defenceLevel":15,"rangeLevel":60,"rangeDef":200,"magicDef":200},"4139":{"name":"Thrower troll","hitpoints":95,"combatLevel":68,"attackSpeed":7,"attackLevel":30,"strengthLevel":95,"defenceLevel":15,"rangeLevel":60,"rangeDef":200,"magicDef":200},"4143":{"name":"Mountain troll","hitpoints":90,"combatLevel":71,"attackSpeed":6,"attackLevel":40,"strengthLevel":90,"defenceLevel":25,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"4167":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4168":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4169":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4170":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4171":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4172":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4173":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4174":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4175":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4176":{"name":"Outlaw","hitpoints":20,"combatLevel":32,"attackSpeed":6,"attackLevel":35,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4184":{"name":"Crocodile","hitpoints":62,"combatLevel":63,"attackSpeed":4,"attackLevel":53,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1},"4185":{"name":"Jackal","hitpoints":27,"combatLevel":21,"attackSpeed":4,"attackLevel":17,"strengthLevel":18,"defenceLevel":15,"rangeLevel":1,"magicLevel":1},"4186":{"name":"Locust","hitpoints":27,"combatLevel":18,"attackSpeed":4,"attackLevel":17,"strengthLevel":18,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4188":{"name":"Plague frog","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":18,"strengthLevel":10,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4192":{"name":"Scarab swarm","hitpoints":25,"combatLevel":98,"attackSpeed":1,"attackLevel":255,"strengthLevel":5,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":5,"rangeDef":90,"magicDef":90},"4195":{"name":"Het","hitpoints":75,"combatLevel":81,"attackSpeed":4,"attackLevel":80,"strengthLevel":83,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"4196":{"name":"Apmeken","hitpoints":70,"combatLevel":75,"attackSpeed":4,"attackLevel":30,"strengthLevel":40,"defenceLevel":60,"rangeLevel":1,"magicLevel":85,"stabDef":65,"slashDef":65,"crushDef":65,"magicDef":50},"4197":{"name":"Scabaras","hitpoints":50,"combatLevel":75,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":66,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":30,"crushDef":30,"rangeDef":70},"4198":{"name":"Crondis","hitpoints":60,"combatLevel":75,"attackSpeed":4,"attackLevel":80,"strengthLevel":70,"defenceLevel":35,"rangeLevel":1,"magicLevel":40,"stabDef":15,"slashDef":15,"crushDef":15,"rangeDef":40,"magicDef":70},"4210":{"name":"Possessed Priest","hitpoints":90,"combatLevel":91,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":40,"rangeLevel":1,"magicLevel":60,"stabDef":90,"crushDef":90,"bonusStrength":90},"4246":{"name":"Monk","hitpoints":5,"combatLevel":3,"attackSpeed":4,"attackLevel":2,"strengthLevel":2,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"4247":{"name":"Thief","hitpoints":17,"combatLevel":14,"attackSpeed":4,"attackLevel":14,"strengthLevel":10,"defenceLevel":9,"rangeLevel":2,"stabDef":8,"slashDef":13,"crushDef":14,"rangeDef":9,"magicDef":4,"bonusAttack":6,"bonusStrength":7},"4248":{"name":"Head Thief","hitpoints":37,"combatLevel":26,"attackSpeed":5,"attackLevel":24,"strengthLevel":18,"defenceLevel":16,"rangeLevel":2,"stabDef":8,"slashDef":14,"crushDef":15,"rangeDef":9,"magicDef":4,"bonusStrength":10},"4276":{"name":"Jail guard","hitpoints":32,"combatLevel":26,"attackSpeed":5,"attackLevel":19,"strengthLevel":23,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":10,"rangeDef":9,"magicDef":4,"bonusAttack":9,"bonusStrength":5},"4277":{"name":"Jail guard","hitpoints":32,"combatLevel":26,"attackSpeed":5,"attackLevel":19,"strengthLevel":23,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":10,"rangeDef":9,"magicDef":4,"bonusAttack":9,"bonusStrength":5},"4278":{"name":"Jail guard","hitpoints":32,"combatLevel":26,"attackSpeed":5,"attackLevel":19,"strengthLevel":23,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":10,"rangeDef":9,"magicDef":4,"bonusAttack":9,"bonusStrength":5},"4279":{"name":"Jail guard","hitpoints":32,"combatLevel":26,"attackSpeed":5,"attackLevel":19,"strengthLevel":23,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":10,"rangeDef":9,"magicDef":4,"bonusAttack":9,"bonusStrength":5},"4303":{"name":"Kalphite Queen","hitpoints":255,"combatLevel":333,"attackSpeed":4,"attackLevel":300,"strengthLevel":300,"defenceLevel":300,"rangeLevel":1,"magicLevel":150,"stabDef":50,"slashDef":50,"crushDef":10,"rangeDef":100,"magicDef":100},"4304":{"name":"Kalphite Queen","hitpoints":255,"combatLevel":333,"attackSpeed":4,"attackLevel":300,"strengthLevel":300,"defenceLevel":300,"rangeLevel":1,"magicLevel":150,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":10,"magicDef":10},"4308":{"name":"Sea troll","hitpoints":100,"combatLevel":79,"attackSpeed":4,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1},"4309":{"name":"Sea troll","hitpoints":80,"combatLevel":65,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"4310":{"name":"Sea troll","hitpoints":80,"combatLevel":87,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":1},"4311":{"name":"Sea troll","hitpoints":80,"combatLevel":101,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1},"4315":{"name":"Sea Troll Queen","hitpoints":200,"combatLevel":170,"attackSpeed":4,"attackLevel":100,"strengthLevel":70,"defenceLevel":100,"rangeLevel":1,"magicLevel":150,"stabDef":20,"slashDef":40,"crushDef":40,"magicDef":40,"bonusAttack":100,"bonusStrength":40},"4319":{"name":"Skeleton Mage","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":60,"rangeLevel":1,"magicLevel":100,"magicDef":15},"4321":{"name":"Renegade Knight","hitpoints":48,"combatLevel":37,"attackSpeed":4,"attackLevel":28,"strengthLevel":28,"defenceLevel":28,"rangeLevel":1,"magicLevel":1,"stabDef":99,"slashDef":82,"crushDef":62,"bonusAttack":20,"bonusStrength":16},"4328":{"name":"Sigmund","hitpoints":60,"combatLevel":50,"attackSpeed":4,"attackLevel":45,"strengthLevel":45,"defenceLevel":24,"rangeLevel":1,"magicLevel":1,"magic":8,"venomImmune":true},"4331":{"name":"Black Knight","hitpoints":42,"combatLevel":33,"attackSpeed":5,"attackLevel":25,"strengthLevel":25,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":73,"slashDef":76,"crushDef":70,"rangeDef":72,"bonusAttack":18,"bonusStrength":16},"4342":{"name":"Sir Lucan","hitpoints":105,"combatLevel":120,"attackSpeed":5,"attackLevel":105,"strengthLevel":110,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":275,"slashDef":65,"crushDef":45,"bonusAttack":40,"bonusStrength":40},"4343":{"name":"Sir Palomedes","hitpoints":100,"combatLevel":118,"attackSpeed":5,"attackLevel":105,"strengthLevel":105,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":250,"slashDef":250,"crushDef":50,"bonusAttack":35,"bonusStrength":35},"4344":{"name":"Sir Lancelot","hitpoints":115,"combatLevel":127,"attackSpeed":4,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":300,"slashDef":85,"crushDef":300,"bonusAttack":60,"bonusStrength":60},"4345":{"name":"Sir Bedivere","hitpoints":90,"combatLevel":110,"attackSpeed":5,"attackLevel":99,"strengthLevel":95,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":15,"crushDef":190,"bonusAttack":20,"bonusStrength":20},"4346":{"name":"Sir Tristram","hitpoints":105,"combatLevel":115,"attackSpeed":5,"attackLevel":99,"strengthLevel":99,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"crushDef":225,"bonusAttack":25,"bonusStrength":25},"4347":{"name":"Sir Pelleas","hitpoints":99,"combatLevel":112,"attackSpeed":5,"attackLevel":95,"strengthLevel":99,"defenceLevel":98,"rangeLevel":1,"magicLevel":1,"stabDef":200,"crushDef":5,"bonusAttack":22,"bonusStrength":22},"4348":{"name":"Sir Gawain","hitpoints":110,"combatLevel":122,"attackSpeed":5,"attackLevel":110,"strengthLevel":105,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":65,"slashDef":90,"crushDef":300,"bonusAttack":40,"bonusStrength":45},"4349":{"name":"Sir Kay","hitpoints":110,"combatLevel":124,"attackSpeed":5,"attackLevel":110,"strengthLevel":110,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":300,"crushDef":65,"bonusAttack":55,"bonusStrength":55},"4354":{"name":"Sir Lancelot","hitpoints":115,"combatLevel":127,"attackSpeed":4,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":300,"slashDef":85,"crushDef":300,"bonusAttack":60,"bonusStrength":60},"4355":{"name":"Sir Kay","hitpoints":110,"combatLevel":124,"attackSpeed":5,"attackLevel":110,"strengthLevel":110,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":300,"crushDef":65,"bonusAttack":55,"bonusStrength":55},"4356":{"name":"Sir Gawain","hitpoints":110,"combatLevel":122,"attackSpeed":5,"attackLevel":110,"strengthLevel":105,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":65,"slashDef":90,"crushDef":300,"bonusAttack":40,"bonusStrength":45},"4357":{"name":"Sir Lucan","hitpoints":105,"combatLevel":120,"attackSpeed":5,"attackLevel":105,"strengthLevel":110,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":275,"slashDef":65,"crushDef":45,"bonusAttack":40,"bonusStrength":40},"4358":{"name":"Sir Palomedes","hitpoints":100,"combatLevel":118,"attackSpeed":5,"attackLevel":105,"strengthLevel":105,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":250,"slashDef":250,"crushDef":50,"bonusAttack":35,"bonusStrength":35},"4359":{"name":"Sir Tristram","hitpoints":105,"combatLevel":115,"attackSpeed":5,"attackLevel":99,"strengthLevel":99,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"crushDef":225,"bonusAttack":25,"bonusStrength":25},"4360":{"name":"Sir Pelleas","hitpoints":99,"combatLevel":112,"attackSpeed":5,"attackLevel":95,"strengthLevel":99,"defenceLevel":98,"rangeLevel":1,"magicLevel":1,"stabDef":200,"crushDef":5,"bonusAttack":22,"bonusStrength":22},"4361":{"name":"Sir Bedivere","hitpoints":90,"combatLevel":110,"attackSpeed":5,"attackLevel":99,"strengthLevel":95,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":15,"crushDef":190,"bonusAttack":20,"bonusStrength":20},"4362":{"name":"Ogre chieftain","hitpoints":60,"combatLevel":81,"attackSpeed":4,"attackLevel":75,"strengthLevel":71,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":21,"crushDef":16,"bonusAttack":5,"bonusStrength":7},"4363":{"name":"Ogre chieftain","hitpoints":60,"combatLevel":81,"attackSpeed":4,"attackLevel":75,"strengthLevel":71,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":21,"crushDef":16,"bonusAttack":5,"bonusStrength":7},"4367":{"name":"Gorad","hitpoints":80,"combatLevel":68,"attackSpeed":4,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":27,"crushDef":21,"bonusAttack":8,"bonusStrength":8},"4373":{"name":"City guard","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":13,"slashDef":24,"crushDef":19,"bonusAttack":9,"bonusStrength":13},"4381":{"name":"Enclave guard","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":13,"slashDef":24,"crushDef":19,"bonusAttack":9,"bonusStrength":13},"4382":{"name":"Ogre shaman","hitpoints":1,"combatLevel":113,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4383":{"name":"Ogre shaman","hitpoints":1,"combatLevel":113,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4384":{"name":"Ogre shaman","hitpoints":1,"combatLevel":113,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4385":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"4387":{"name":"Ogre shaman","hitpoints":1,"combatLevel":113,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4389":{"name":"Ogre shaman","hitpoints":1,"combatLevel":113,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4391":{"name":"Ogre shaman","hitpoints":1,"combatLevel":113,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4393":{"name":"Ogre shaman","hitpoints":1,"combatLevel":113,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4395":{"name":"Ogre shaman","hitpoints":1,"combatLevel":113,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4405":{"name":"Tower guard","hitpoints":22,"combatLevel":28,"attackSpeed":4,"attackLevel":26,"strengthLevel":26,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"stabDef":23,"slashDef":35,"crushDef":28,"bonusAttack":8,"bonusStrength":8},"4406":{"name":"Colonel Radick","hitpoints":65,"combatLevel":38,"attackSpeed":4,"attackLevel":25,"strengthLevel":25,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":39,"slashDef":31,"crushDef":34,"bonusAttack":9,"bonusStrength":13},"4421":{"name":"Undead cow","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4427":{"name":"Vampyre Juvinate","hitpoints":65,"combatLevel":54,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4428":{"name":"Vampyre Juvinate","hitpoints":65,"combatLevel":54,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4429":{"name":"Vampyre Juvinate","hitpoints":65,"combatLevel":54,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4430":{"name":"Vampyre Juvinate","hitpoints":65,"combatLevel":54,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4431":{"name":"Feral Vampyre","hitpoints":80,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":30,"defenceLevel":60,"rangeLevel":1,"magicLevel":1},"4432":{"name":"Vampyre Juvinate","hitpoints":65,"combatLevel":54,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4436":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4437":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4438":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4439":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4442":{"name":"Vampyre Juvinate","hitpoints":110,"combatLevel":75,"attackSpeed":4,"attackLevel":70,"strengthLevel":50,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"4443":{"name":"Vampyre Juvinate","hitpoints":60,"combatLevel":50,"attackSpeed":4,"attackLevel":50,"strengthLevel":37,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4483":{"name":"Gadderanks","hitpoints":20,"combatLevel":35,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10,"magicDef":10,"poisonImmune":true,"venomImmune":true},"4484":{"name":"Gadderanks","hitpoints":20,"combatLevel":35,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10,"magicDef":10,"poisonImmune":true,"venomImmune":true},"4485":{"name":"Gadderanks","hitpoints":20,"combatLevel":35,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10,"magicDef":10,"poisonImmune":true,"venomImmune":true},"4486":{"name":"Vampyre Juvinate","hitpoints":60,"combatLevel":50,"attackSpeed":4,"attackLevel":50,"strengthLevel":37,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4487":{"name":"Vampyre Juvinate","hitpoints":65,"combatLevel":54,"attackSpeed":4,"attackLevel":55,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"4491":{"name":"Skeleton fremennik","hitpoints":25,"combatLevel":40,"attackSpeed":6,"attackLevel":53,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4492":{"name":"Skeleton fremennik","hitpoints":25,"combatLevel":40,"attackSpeed":6,"attackLevel":53,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4493":{"name":"Skeleton fremennik","hitpoints":25,"combatLevel":40,"attackSpeed":6,"attackLevel":53,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4494":{"name":"Skeleton fremennik","hitpoints":35,"combatLevel":50,"attackSpeed":6,"attackLevel":60,"strengthLevel":43,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4495":{"name":"Skeleton fremennik","hitpoints":35,"combatLevel":50,"attackSpeed":6,"attackLevel":60,"strengthLevel":43,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4496":{"name":"Skeleton fremennik","hitpoints":35,"combatLevel":50,"attackSpeed":6,"attackLevel":60,"strengthLevel":43,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4497":{"name":"Skeleton fremennik","hitpoints":40,"combatLevel":60,"attackSpeed":6,"attackLevel":72,"strengthLevel":54,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4498":{"name":"Skeleton fremennik","hitpoints":40,"combatLevel":60,"attackSpeed":6,"attackLevel":72,"strengthLevel":54,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4499":{"name":"Skeleton fremennik","hitpoints":40,"combatLevel":60,"attackSpeed":6,"attackLevel":72,"strengthLevel":54,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":15,"rangeDef":15,"bonusAttack":8,"bonusStrength":13},"4500":{"name":"Ulfric","hitpoints":60,"combatLevel":100,"attackSpeed":6,"attackLevel":100,"strengthLevel":82,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"4501":{"name":"Brine rat","hitpoints":50,"combatLevel":70,"slayerLevel":47,"attackSpeed":4,"attackLevel":70,"strengthLevel":79,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"4504":{"name":"Giant bat","hitpoints":32,"combatLevel":27,"attackSpeed":4,"attackLevel":22,"strengthLevel":22,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":12,"rangeDef":8,"magicDef":10},"4505":{"name":"Ulfric","hitpoints":60,"combatLevel":100,"attackSpeed":6,"attackLevel":100,"strengthLevel":82,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"4522":{"name":"Guard","hitpoints":30,"combatLevel":22,"attackSpeed":5,"attackLevel":16,"strengthLevel":20,"defenceLevel":12,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"4523":{"name":"Guard","hitpoints":30,"combatLevel":22,"attackSpeed":5,"attackLevel":16,"strengthLevel":20,"defenceLevel":12,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"4524":{"name":"Guard","hitpoints":30,"combatLevel":22,"attackSpeed":5,"attackLevel":16,"strengthLevel":20,"defenceLevel":12,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"4525":{"name":"Guard","hitpoints":30,"combatLevel":22,"attackSpeed":5,"attackLevel":16,"strengthLevel":20,"defenceLevel":12,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"4526":{"name":"Guard","hitpoints":30,"combatLevel":22,"attackSpeed":5,"attackLevel":16,"strengthLevel":20,"defenceLevel":12,"rangeLevel":1,"magicLevel":1,"stabDef":7,"slashDef":7,"rangeDef":7},"4533":{"name":"Blessed spider","hitpoints":32,"combatLevel":39,"attackSpeed":4,"attackLevel":35,"strengthLevel":35,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":16,"crushDef":7,"rangeDef":16,"magicDef":12},"4534":{"name":"Blessed giant rat","hitpoints":30,"combatLevel":9,"attackSpeed":4,"attackLevel":5,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4535":{"name":"Blessed giant rat","hitpoints":30,"combatLevel":9,"attackSpeed":4,"attackLevel":5,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4561":{"name":"Spider","hitpoints":2,"combatLevel":1,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4634":{"name":"Rowdy slave","hitpoints":16,"combatLevel":10,"attackSpeed":4,"attackLevel":7,"strengthLevel":7,"defenceLevel":7},"4635":{"name":"Mercenary Captain","hitpoints":80,"combatLevel":47,"attackSpeed":4,"attackLevel":32,"strengthLevel":29,"defenceLevel":32,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4643":{"name":"Shantay Guard","hitpoints":32,"combatLevel":22,"attackSpeed":4,"attackLevel":17,"strengthLevel":16,"defenceLevel":16,"stabDef":12,"slashDef":15,"crushDef":18,"rangeDef":15,"magicDef":6,"bonusAttack":11,"bonusStrength":12},"4648":{"name":"Shantay Guard","hitpoints":32,"combatLevel":22,"attackSpeed":4,"attackLevel":17,"strengthLevel":16,"defenceLevel":16,"stabDef":12,"slashDef":15,"crushDef":18,"rangeDef":15,"magicDef":6,"bonusAttack":11,"bonusStrength":12},"4649":{"name":"Desert Wolf","hitpoints":34,"combatLevel":27,"attackSpeed":4,"attackLevel":20,"strengthLevel":21,"defenceLevel":22},"4650":{"name":"Desert Wolf","hitpoints":34,"combatLevel":27,"attackSpeed":4,"attackLevel":20,"strengthLevel":21,"defenceLevel":22},"4651":{"name":"Desert Wolf","hitpoints":34,"combatLevel":27,"attackSpeed":4,"attackLevel":20,"strengthLevel":21,"defenceLevel":22},"4652":{"name":"Ugthanki","hitpoints":45,"combatLevel":42,"attackSpeed":4,"attackLevel":35,"strengthLevel":35,"defenceLevel":35},"4655":{"name":"Bedabin Nomad Fighter","hitpoints":50,"combatLevel":56,"attackSpeed":4,"attackLevel":40,"strengthLevel":50,"defenceLevel":60,"bonusAttack":9,"bonusStrength":14},"4656":{"name":"Mercenary","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4657":{"name":"Mercenary","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4658":{"name":"Mercenary","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4659":{"name":"Mercenary","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4660":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4661":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4662":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4663":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4664":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4665":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4666":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4667":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4668":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4669":{"name":"Guard","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":39,"stabDef":17,"slashDef":15,"crushDef":19,"rangeDef":19,"bonusAttack":9,"bonusStrength":14},"4682":{"name":"Sir Leye","hitpoints":20,"combatLevel":20,"attackSpeed":5},"4688":{"name":"Angry unicorn","hitpoints":50,"combatLevel":47,"attackSpeed":4,"attackLevel":38,"strengthLevel":41,"defenceLevel":38,"rangeLevel":1,"magicLevel":1},"4689":{"name":"Angry giant rat","hitpoints":50,"combatLevel":47,"attackSpeed":4,"attackLevel":38,"strengthLevel":41,"defenceLevel":38,"rangeLevel":1,"magicLevel":1},"4690":{"name":"Angry giant rat","hitpoints":50,"combatLevel":47,"attackSpeed":4,"attackLevel":38,"strengthLevel":41,"defenceLevel":38,"rangeLevel":1,"magicLevel":1},"4691":{"name":"Angry goblin","hitpoints":50,"combatLevel":47,"attackSpeed":4,"attackLevel":38,"strengthLevel":41,"defenceLevel":38,"rangeLevel":1,"magicLevel":1},"4692":{"name":"Angry bear","hitpoints":50,"combatLevel":47,"attackSpeed":4,"attackLevel":38,"strengthLevel":41,"defenceLevel":38,"rangeLevel":1,"magicLevel":1},"4693":{"name":"Fear reaper","hitpoints":57,"combatLevel":55,"attackSpeed":4,"attackLevel":45,"strengthLevel":48,"defenceLevel":45,"rangeLevel":1,"magicLevel":1},"4694":{"name":"Confusion beast","hitpoints":64,"combatLevel":63,"attackSpeed":4,"attackLevel":52,"strengthLevel":55,"defenceLevel":52,"rangeLevel":1,"magicLevel":1},"4695":{"name":"Hopeless creature","hitpoints":71,"combatLevel":71,"attackSpeed":4,"attackLevel":59,"strengthLevel":62,"defenceLevel":59,"rangeLevel":1,"magicLevel":1},"4708":{"name":"The Shaikahan","hitpoints":100,"combatLevel":83,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"rangeDef":10000,"magicDef":10000},"4709":{"name":"The Shaikahan","hitpoints":100,"combatLevel":83,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"rangeDef":10000,"magicDef":10000},"4742":{"name":"Black golem","hitpoints":80,"combatLevel":75,"attackSpeed":4,"attackLevel":80,"strengthLevel":30,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":300,"slashDef":300,"crushDef":1,"rangeDef":300,"magicDef":300},"4743":{"name":"White golem","hitpoints":80,"combatLevel":75,"attackSpeed":4,"attackLevel":80,"strengthLevel":30,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":300,"crushDef":300,"rangeDef":300,"magicDef":300},"4744":{"name":"Grey golem","hitpoints":80,"combatLevel":75,"attackSpeed":4,"attackLevel":80,"strengthLevel":30,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":300,"slashDef":1,"crushDef":300,"rangeDef":300,"magicDef":300},"4757":{"name":"Tarik","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"4758":{"name":"Poltenip","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"4759":{"name":"Radat","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":4,"bonusStrength":5},"4772":{"name":"Fortress Guard","hitpoints":22,"combatLevel":20,"attackSpeed":6,"attackLevel":18,"strengthLevel":18,"defenceLevel":15,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":14,"rangeDef":15,"bonusAttack":5,"bonusStrength":6},"4773":{"name":"Fortress Guard","hitpoints":22,"combatLevel":20,"attackSpeed":6,"attackLevel":18,"strengthLevel":18,"defenceLevel":15,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":14,"rangeDef":15,"bonusAttack":5,"bonusStrength":6},"4774":{"name":"Fortress Guard","hitpoints":22,"combatLevel":20,"attackSpeed":6,"attackLevel":18,"strengthLevel":18,"defenceLevel":15,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":14,"rangeDef":15,"bonusAttack":5,"bonusStrength":6},"4775":{"name":"Fortress Guard","hitpoints":22,"combatLevel":20,"attackSpeed":6,"attackLevel":18,"strengthLevel":18,"defenceLevel":15,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":14,"rangeDef":15,"bonusAttack":5,"bonusStrength":6},"4776":{"name":"Fortress Guard","hitpoints":22,"combatLevel":20,"attackSpeed":6,"attackLevel":18,"strengthLevel":18,"defenceLevel":15,"rangeLevel":1,"magicLevel":1,"stabDef":14,"slashDef":18,"crushDef":14,"rangeDef":15,"bonusAttack":5,"bonusStrength":6},"4797":{"name":"Slug Prince","hitpoints":70,"combatLevel":62,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":30,"crushDef":30,"rangeDef":99,"magicDef":99},"4798":{"name":"Slug Prince","hitpoints":70,"combatLevel":62,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":30,"crushDef":30,"rangeDef":99,"magicDef":99},"4799":{"name":"Giant lobster","hitpoints":50,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":30,"crushDef":15,"rangeDef":70,"magicDef":99},"4800":{"name":"Giant lobster","hitpoints":50,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":30,"crushDef":15,"rangeDef":70,"magicDef":99},"4805":{"name":"Hobgoblin","hitpoints":29,"combatLevel":28,"attackSpeed":4,"attackLevel":22,"strengthLevel":24,"defenceLevel":24,"rangeLevel":1,"magicLevel":1},"4813":{"name":"Icefiend","hitpoints":15,"combatLevel":13,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"4819":{"name":"Crab","hitpoints":19,"combatLevel":23,"attackSpeed":4,"attackLevel":19,"strengthLevel":20,"defenceLevel":26,"rangeLevel":1,"magicLevel":1},"4820":{"name":"Mudskipper","hitpoints":20,"combatLevel":30,"attackSpeed":4,"attackLevel":29,"strengthLevel":29,"defenceLevel":26,"rangeLevel":1,"magicLevel":1},"4821":{"name":"Mudskipper","hitpoints":20,"combatLevel":31,"attackSpeed":4,"attackLevel":30,"strengthLevel":30,"defenceLevel":29,"rangeLevel":1,"magicLevel":1},"4822":{"name":"Crab","hitpoints":18,"combatLevel":21,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":22,"rangeLevel":1,"magicLevel":1},"4849":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4863":{"name":"Jubbly bird","hitpoints":21,"combatLevel":9},"4864":{"name":"Jubbly bird","hitpoints":21,"combatLevel":9},"4872":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4873":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4874":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4875":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4876":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4877":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4878":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4879":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"4880":{"name":"Agrith-Na-Na","hitpoints":200,"combatLevel":146,"attackSpeed":4,"attackLevel":83,"strengthLevel":150,"defenceLevel":82,"rangeLevel":100,"magicLevel":100,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":100,"magicDef":100,"bonusAttack":100},"4881":{"name":"Flambeed","hitpoints":210,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":5,"rangeDef":50,"magicDef":5,"bonusAttack":100},"4882":{"name":"Karamel","hitpoints":250,"combatLevel":136,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":100,"magicLevel":1,"range":134,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":150,"magicDef":150,"bonusRangeStrength":55},"4883":{"name":"Dessourt","hitpoints":130,"combatLevel":121,"attackSpeed":3,"attackLevel":99,"strengthLevel":99,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":150,"crushDef":150,"bonusAttack":50,"bonusStrength":50},"4884":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"4885":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"4886":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"4887":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"4888":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"4889":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"4919":{"name":"Grip","hitpoints":25,"combatLevel":22,"attackSpeed":4,"attackLevel":18,"strengthLevel":17,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":16,"slashDef":27,"crushDef":22,"bonusAttack":5,"bonusStrength":7},"4922":{"name":"Ice Queen","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":94,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":40,"crushDef":20,"rangeDef":30,"magicDef":10},"4926":{"name":"Pirate Guard","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":18,"strengthLevel":16,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":39,"crushDef":30,"bonusAttack":20,"bonusStrength":16},"4927":{"name":"Entrana firebird","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"4930":{"name":"Solus Dellagar","hitpoints":40,"attackSpeed":3},"4933":{"name":"Solus Dellagar","hitpoints":40,"attackSpeed":3},"4934":{"name":"Black Knight","hitpoints":42,"combatLevel":32,"attackSpeed":5},"4959":{"name":"Black Knight","hitpoints":42,"combatLevel":32,"attackSpeed":5},"4960":{"name":"Black Knight","hitpoints":42,"combatLevel":32,"attackSpeed":5},"4962":{"name":"Solus Dellagar","hitpoints":40,"attackSpeed":3},"4969":{"name":"Khazard trooper","hitpoints":22,"combatLevel":19,"attackSpeed":5,"attackLevel":17,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":22,"bonusAttack":9,"bonusStrength":5},"4970":{"name":"Khazard trooper","hitpoints":22,"combatLevel":19,"attackSpeed":5,"attackLevel":17,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":22,"bonusAttack":9,"bonusStrength":5},"4972":{"name":"Khazard commander","hitpoints":22,"combatLevel":48,"attackSpeed":4,"attackLevel":50,"strengthLevel":45,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"4973":{"name":"Gnome troop","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":5,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":5,"rangeDef":4,"magicDef":2,"bonusAttack":2,"bonusStrength":3,"bonusRangeStrength":2},"4974":{"name":"Gnome troop","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":5,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":5,"rangeDef":4,"magicDef":2,"bonusAttack":2,"bonusStrength":3,"bonusRangeStrength":2},"4987":{"name":"Chronozon","hitpoints":60,"combatLevel":170,"attackSpeed":4,"attackLevel":173,"strengthLevel":172,"defenceLevel":173,"rangeLevel":1,"magicLevel":1},"5007":{"name":"Imp","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5043":{"name":"Suit of armour","hitpoints":29,"combatLevel":19,"attackSpeed":5,"attackLevel":16,"strengthLevel":14,"defenceLevel":9,"rangeLevel":1,"magicLevel":1,"stabDef":46,"slashDef":50,"crushDef":45,"rangeDef":45,"bonusAttack":8,"bonusStrength":10},"5054":{"name":"Skeleton Hellhound","hitpoints":55,"combatLevel":97,"attackSpeed":4,"attackLevel":70,"strengthLevel":110,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"5079":{"name":"Delrith","hitpoints":10,"combatLevel":27,"attackSpeed":6,"attackLevel":11,"strengthLevel":10,"defenceLevel":5,"rangeLevel":1,"magicLevel":1,"slashDef":2},"5086":{"name":"Dark wizard","hitpoints":12,"combatLevel":7,"attackSpeed":4,"attackLevel":5,"strengthLevel":2,"defenceLevel":5,"rangeLevel":1,"magicLevel":6,"magicDef":3},"5087":{"name":"Dark wizard","hitpoints":12,"combatLevel":7,"attackSpeed":4,"attackLevel":5,"strengthLevel":2,"defenceLevel":5,"rangeLevel":1,"magicLevel":6,"magicDef":3},"5088":{"name":"Dark wizard","hitpoints":24,"combatLevel":20,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":14,"rangeLevel":1,"magicLevel":22,"magicDef":3},"5089":{"name":"Dark wizard","hitpoints":24,"combatLevel":20,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":14,"rangeLevel":1,"magicLevel":22,"magicDef":3},"5126":{"name":"Experiment No.2","hitpoints":95,"combatLevel":109,"attackSpeed":4,"attackLevel":104,"strengthLevel":92,"defenceLevel":90,"rangeLevel":100,"magicLevel":1,"stabDef":45,"slashDef":70,"crushDef":60,"rangeDef":55,"magicDef":60},"5127":{"name":"Mouse","hitpoints":70,"combatLevel":95,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":86,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":55,"crushDef":65,"rangeDef":60,"magicDef":50},"5128":{"name":"Mouse","hitpoints":70,"combatLevel":95,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":86,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":55,"crushDef":65,"rangeDef":60,"magicDef":50},"5129":{"name":"Glod","hitpoints":160,"combatLevel":138,"attackSpeed":4,"attackLevel":115,"strengthLevel":120,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":105,"slashDef":110,"crushDef":130,"rangeDef":100,"magicDef":125},"5139":{"name":"Sigmund","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"magic":8,"poisonImmune":true,"venomImmune":true},"5142":{"name":"Sigmund","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"magic":8,"poisonImmune":true,"venomImmune":true},"5143":{"name":"Sigmund","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"magic":8,"poisonImmune":true,"venomImmune":true},"5144":{"name":"Sigmund","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"magic":8,"poisonImmune":true,"venomImmune":true},"5145":{"name":"Sigmund","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"magic":8,"poisonImmune":true,"venomImmune":true},"5146":{"name":"Sigmund","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"magic":8,"poisonImmune":true,"venomImmune":true},"5157":{"name":"H.A.M. Archer","hitpoints":35,"combatLevel":30,"attackSpeed":10,"defenceLevel":30,"rangeLevel":30,"stabDef":5,"slashDef":5,"rangeDef":5},"5158":{"name":"H.A.M. Mage","hitpoints":35,"combatLevel":30,"attackSpeed":6,"defenceLevel":30,"rangeLevel":1,"magicLevel":30,"magic":8,"stabDef":5,"slashDef":5,"rangeDef":5},"5185":{"name":"Guard","hitpoints":16,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":9,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"5186":{"name":"Guard","hitpoints":16,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":9,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"5187":{"name":"Guard","hitpoints":16,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":9,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"5188":{"name":"Guard","hitpoints":16,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":9,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"5189":{"name":"Guard","hitpoints":16,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":9,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":4,"rangeDef":3,"magicDef":2,"bonusAttack":5,"bonusStrength":7},"5192":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5193":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5194":{"name":"Baby green dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"5195":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5196":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5197":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5198":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5199":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5200":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5201":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5202":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5203":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5204":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5205":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5206":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5207":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5208":{"name":"Goblin","hitpoints":5,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5211":{"name":"Weaponsmaster","hitpoints":20,"combatLevel":23,"attackSpeed":5,"attackLevel":21,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":21,"slashDef":23,"crushDef":21,"rangeDef":20,"bonusAttack":8,"bonusStrength":10},"5213":{"name":"Jonny the beard","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5217":{"name":"Thief","hitpoints":17,"combatLevel":16,"attackSpeed":4,"attackLevel":14,"strengthLevel":13,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5218":{"name":"Thief","hitpoints":17,"combatLevel":16,"attackSpeed":4,"attackLevel":14,"strengthLevel":13,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5219":{"name":"Thief","hitpoints":17,"combatLevel":16,"attackSpeed":4,"attackLevel":14,"strengthLevel":13,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5220":{"name":"Thief","hitpoints":17,"combatLevel":16,"attackSpeed":4,"attackLevel":14,"strengthLevel":13,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5223":{"name":"Sigmund","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"magic":8,"poisonImmune":true,"venomImmune":true},"5237":{"name":"Skeleton","hitpoints":110,"combatLevel":142,"attackSpeed":4,"attackLevel":180,"strengthLevel":90,"defenceLevel":110,"rangeLevel":1,"magicLevel":110,"bonusAttack":50},"5238":{"name":"Spider","hitpoints":2,"combatLevel":1,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"slashDef":1,"magicDef":1,"bonusAttack":1,"bonusStrength":1},"5239":{"name":"Spider","hitpoints":2,"combatLevel":1,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"slashDef":1,"magicDef":1,"bonusAttack":1,"bonusStrength":1},"5240":{"name":"Bird","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":10},"5241":{"name":"Bird","hitpoints":5,"combatLevel":5,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":5,"rangeLevel":1,"magicLevel":5},"5242":{"name":"Scorpion","hitpoints":15,"combatLevel":38,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":10,"rangeLevel":1,"magicLevel":10,"bonusAttack":100,"bonusStrength":100},"5243":{"name":"Jungle spider","hitpoints":35,"combatLevel":37,"attackSpeed":4,"attackLevel":50,"strengthLevel":30,"defenceLevel":10,"rangeLevel":1,"magicLevel":10,"bonusAttack":100,"bonusStrength":100},"5244":{"name":"Snake","hitpoints":36,"combatLevel":24,"attackSpeed":4,"attackLevel":15,"strengthLevel":25,"defenceLevel":10,"rangeLevel":1,"magicLevel":10,"bonusAttack":100,"bonusStrength":100},"5259":{"name":"Oipuis","combatLevel":149},"5260":{"name":"Uyoro","combatLevel":149},"5261":{"name":"Ouhai","combatLevel":149},"5262":{"name":"Uodai","combatLevel":149},"5263":{"name":"Padulah","hitpoints":130,"combatLevel":149,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":130,"bonusAttack":50},"5271":{"name":"Monkey Guard","hitpoints":130,"combatLevel":149,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":130},"5272":{"name":"Monkey Archer","hitpoints":50,"combatLevel":86,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":110,"magicLevel":1},"5273":{"name":"Monkey Archer","hitpoints":50,"combatLevel":86,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":110,"magicLevel":1},"5274":{"name":"Monkey Archer","hitpoints":50,"combatLevel":86,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":110,"magicLevel":1},"5275":{"name":"Monkey Guard","hitpoints":130,"combatLevel":167,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":200,"rangeLevel":1,"magicLevel":130,"bonusAttack":50},"5276":{"name":"Monkey Guard","hitpoints":130,"combatLevel":167,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":200,"rangeLevel":1,"magicLevel":130,"bonusAttack":50},"5281":{"name":"Monkey Zombie","hitpoints":60,"combatLevel":98,"attackSpeed":4,"attackLevel":150,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":60,"bonusAttack":35},"5282":{"name":"Monkey Zombie","hitpoints":90,"combatLevel":129,"attackSpeed":4,"attackLevel":150,"strengthLevel":110,"defenceLevel":90,"rangeLevel":1,"magicLevel":90,"bonusAttack":35},"5283":{"name":"Monkey Zombie","hitpoints":60,"combatLevel":82,"attackSpeed":4,"attackLevel":100,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":60,"bonusAttack":15},"5293":{"name":"Elf Warrior","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":80,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5294":{"name":"Elf Warrior","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":80,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5295":{"name":"Elf Archer","hitpoints":105,"combatLevel":90,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":80,"rangeLevel":90,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5296":{"name":"Elf Archer","hitpoints":105,"combatLevel":90,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":80,"rangeLevel":90,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5329":{"name":"Sigmund","hitpoints":70,"combatLevel":64,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"magic":8,"poisonImmune":true,"venomImmune":true},"5330":{"name":"Cave goblin miner","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":12,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5331":{"name":"Cave goblin miner","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":12,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5332":{"name":"Cave goblin miner","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":12,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5333":{"name":"Cave goblin miner","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":12,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5334":{"name":"Cave goblin guard","hitpoints":26,"combatLevel":26,"attackSpeed":6,"attackLevel":25,"strengthLevel":18,"defenceLevel":25,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":16,"crushDef":19,"rangeDef":12},"5335":{"name":"Cave goblin guard","hitpoints":26,"combatLevel":24,"attackSpeed":6,"attackLevel":22,"strengthLevel":16,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"slashDef":15,"crushDef":19,"rangeDef":12},"5336":{"name":"Cave goblin miner","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":12,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5337":{"name":"Cave goblin miner","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":12,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5338":{"name":"Cave goblin miner","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":12,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5339":{"name":"Cave goblin miner","hitpoints":10,"combatLevel":11,"attackSpeed":4,"attackLevel":10,"strengthLevel":12,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5342":{"name":"Undead one","hitpoints":47,"combatLevel":68,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":63},"5343":{"name":"Undead one","hitpoints":47,"combatLevel":68,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":63},"5344":{"name":"Undead one","hitpoints":47,"combatLevel":61,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":55,"stabDef":5,"slashDef":5,"crushDef":4,"rangeDef":4,"magicDef":1,"bonusAttack":7,"bonusStrength":5},"5345":{"name":"Undead one","hitpoints":47,"combatLevel":61,"attackSpeed":4,"attackLevel":55,"strengthLevel":55,"defenceLevel":55,"stabDef":5,"slashDef":5,"crushDef":4,"rangeDef":4,"magicDef":1,"bonusAttack":7,"bonusStrength":5},"5346":{"name":"Undead one","hitpoints":47,"combatLevel":68,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":63},"5347":{"name":"Undead one","hitpoints":47,"combatLevel":68,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":63},"5348":{"name":"Undead one","hitpoints":47,"combatLevel":68,"attackSpeed":4,"attackLevel":63,"strengthLevel":63,"defenceLevel":63},"5349":{"name":"Undead one","hitpoints":59,"combatLevel":73,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"bonusAttack":4,"bonusStrength":5},"5350":{"name":"Undead one","hitpoints":59,"combatLevel":73,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"bonusAttack":4,"bonusStrength":5},"5351":{"name":"Undead one","hitpoints":59,"combatLevel":73,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"bonusAttack":4,"bonusStrength":5},"5353":{"name":"Nazastarool","hitpoints":70,"combatLevel":91,"attackSpeed":4,"attackLevel":85,"strengthLevel":80,"defenceLevel":80},"5354":{"name":"Nazastarool","hitpoints":70,"combatLevel":68,"attackSpeed":4,"attackLevel":58,"strengthLevel":54,"defenceLevel":58,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5,"magicDef":5,"bonusAttack":15,"bonusStrength":15},"5355":{"name":"Nazastarool","hitpoints":80,"combatLevel":93,"attackSpeed":4,"attackLevel":85,"strengthLevel":80,"defenceLevel":80},"5369":{"name":"Goblin guard","hitpoints":43,"combatLevel":42,"attackSpeed":4,"attackLevel":32,"strengthLevel":37,"defenceLevel":37,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":6,"crushDef":6,"bonusAttack":8,"bonusStrength":5},"5370":{"name":"Ghost","hitpoints":20,"combatLevel":24,"attackSpeed":4,"attackLevel":23,"strengthLevel":23,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"5372":{"name":"Grave scorpion","hitpoints":7,"combatLevel":12,"attackSpeed":4,"attackLevel":11,"strengthLevel":12,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":3},"5373":{"name":"Poison spider","hitpoints":25,"combatLevel":31,"attackSpeed":4,"attackLevel":28,"strengthLevel":28,"defenceLevel":28,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":17,"crushDef":10,"rangeDef":14,"magicDef":14},"5418":{"name":"Guard","hitpoints":22,"combatLevel":20,"attackSpeed":5,"attackLevel":17,"strengthLevel":18,"defenceLevel":13,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":9,"bonusStrength":7},"5420":{"name":"Watchman","hitpoints":22,"combatLevel":33,"attackSpeed":6,"attackLevel":31,"strengthLevel":31,"defenceLevel":31,"rangeLevel":1,"magicLevel":1,"stabDef":24,"slashDef":14,"crushDef":19,"rangeDef":16},"5421":{"name":"Soldier","hitpoints":22,"combatLevel":28,"attackSpeed":5,"attackLevel":26,"strengthLevel":25,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"stabDef":24,"slashDef":14,"crushDef":19,"rangeDef":16,"bonusAttack":8,"bonusStrength":10},"5457":{"name":"Shipyard worker","hitpoints":10,"combatLevel":11,"attackSpeed":6,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"bonusAttack":6,"bonusStrength":6},"5512":{"name":"Gardener","hitpoints":7,"combatLevel":4,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":7,"rangeLevel":1,"magicLevel":1},"5534":{"name":"Whirlpool","slayerLevel":87,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"rangeDef":300,"magicDef":130},"5535":{"name":"Enormous Tentacle","hitpoints":120,"combatLevel":112,"slayerLevel":87,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":150,"magicLevel":1,"rangeDef":270},"5563":{"name":"Angry barbarian spirit","hitpoints":190,"combatLevel":166,"attackSpeed":4,"attackLevel":150,"strengthLevel":140,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"5564":{"name":"Enraged barbarian spirit","hitpoints":190,"combatLevel":166,"attackSpeed":4,"attackLevel":150,"strengthLevel":140,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"5565":{"name":"Berserk barbarian spirit","hitpoints":190,"combatLevel":166,"attackSpeed":4,"attackLevel":150,"strengthLevel":140,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"rangeDef":2,"bonusAttack":9,"bonusStrength":15},"5566":{"name":"Ferocious barbarian spirit","hitpoints":190,"combatLevel":166,"attackSpeed":4,"attackLevel":150,"strengthLevel":140,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"slashDef":73,"crushDef":72,"rangeDef":72,"bonusAttack":9,"bonusStrength":15},"5615":{"name":"Swamp snake","hitpoints":120,"combatLevel":80,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":70,"rangeLevel":30,"magicLevel":30},"5616":{"name":"Swamp snake","hitpoints":125,"combatLevel":109,"attackSpeed":4,"attackLevel":70,"strengthLevel":110,"defenceLevel":80,"rangeLevel":30,"magicLevel":30},"5617":{"name":"Swamp snake","hitpoints":130,"combatLevel":139,"attackSpeed":4,"attackLevel":90,"strengthLevel":170,"defenceLevel":90,"rangeLevel":30,"magicLevel":30},"5625":{"name":"Ghast","hitpoints":115,"combatLevel":79,"attackSpeed":8,"attackLevel":80,"strengthLevel":30,"defenceLevel":60,"rangeLevel":30,"magicLevel":30},"5626":{"name":"Ghast","hitpoints":135,"combatLevel":109,"attackSpeed":8,"attackLevel":110,"strengthLevel":70,"defenceLevel":70,"rangeLevel":30,"magicLevel":30},"5627":{"name":"Ghast","hitpoints":160,"combatLevel":139,"attackSpeed":8,"attackLevel":135,"strengthLevel":110,"defenceLevel":80,"rangeLevel":30,"magicLevel":30},"5628":{"name":"Giant snail","hitpoints":125,"combatLevel":80,"attackSpeed":4,"attackLevel":30,"strengthLevel":30,"defenceLevel":60,"rangeLevel":70,"magicLevel":30},"5629":{"name":"Giant snail","hitpoints":150,"combatLevel":109,"attackSpeed":4,"attackLevel":30,"strengthLevel":30,"defenceLevel":75,"rangeLevel":110,"magicLevel":30},"5630":{"name":"Giant snail","hitpoints":160,"combatLevel":139,"attackSpeed":4,"attackLevel":30,"strengthLevel":30,"defenceLevel":86,"rangeLevel":160,"magicLevel":30},"5631":{"name":"Riyl shadow","hitpoints":76,"combatLevel":80,"attackSpeed":4,"attackLevel":87,"strengthLevel":56,"defenceLevel":60,"rangeLevel":50,"magicLevel":50},"5632":{"name":"Asyn shadow","hitpoints":94,"combatLevel":110,"attackSpeed":4,"attackLevel":110,"strengthLevel":96,"defenceLevel":80,"rangeLevel":50,"magicLevel":50},"5633":{"name":"Shade","hitpoints":115,"combatLevel":140,"attackSpeed":4,"attackLevel":130,"strengthLevel":136,"defenceLevel":100,"rangeLevel":50,"magicLevel":50},"5634":{"name":"Vampyre Juvinate","hitpoints":50,"combatLevel":59,"attackSpeed":4,"attackLevel":95,"strengthLevel":16,"defenceLevel":45,"rangeLevel":50,"magicLevel":50},"5635":{"name":"Vampyre Juvinate","hitpoints":100,"combatLevel":90,"attackSpeed":4,"attackLevel":113,"strengthLevel":46,"defenceLevel":55,"rangeLevel":50,"magicLevel":50},"5636":{"name":"Vampyre Juvinate","hitpoints":150,"combatLevel":119,"attackSpeed":4,"attackLevel":127,"strengthLevel":76,"defenceLevel":65,"rangeLevel":50,"magicLevel":50},"5637":{"name":"Vampyre Juvinate","hitpoints":50,"combatLevel":59,"attackSpeed":4,"attackLevel":95,"strengthLevel":16,"defenceLevel":45,"rangeLevel":50,"magicLevel":50},"5638":{"name":"Vampyre Juvinate","hitpoints":100,"combatLevel":90,"attackSpeed":4,"attackLevel":113,"strengthLevel":46,"defenceLevel":55,"rangeLevel":50,"magicLevel":50},"5639":{"name":"Vampyre Juvinate","hitpoints":150,"combatLevel":119,"attackSpeed":4,"attackLevel":127,"strengthLevel":76,"defenceLevel":65,"rangeLevel":50,"magicLevel":50},"5640":{"name":"Feral Vampyre","hitpoints":75,"combatLevel":70,"attackSpeed":4,"attackLevel":90,"strengthLevel":46,"defenceLevel":30},"5641":{"name":"Feral Vampyre","hitpoints":135,"combatLevel":100,"attackSpeed":4,"attackLevel":105,"strengthLevel":76,"defenceLevel":30},"5642":{"name":"Feral Vampyre","hitpoints":185,"combatLevel":130,"attackSpeed":4,"attackLevel":120,"strengthLevel":116,"defenceLevel":30},"5643":{"name":"Tentacle","hitpoints":75,"combatLevel":99,"attackSpeed":6,"attackLevel":115,"strengthLevel":76,"defenceLevel":75,"rangeLevel":115,"magicLevel":50},"5644":{"name":"Head","hitpoints":150,"combatLevel":140,"attackSpeed":10,"attackLevel":73,"strengthLevel":186,"defenceLevel":75,"rangeLevel":50,"magicLevel":50},"5645":{"name":"Head","hitpoints":150,"combatLevel":140,"attackSpeed":10,"attackLevel":73,"strengthLevel":186,"defenceLevel":75,"rangeLevel":50,"magicLevel":50},"5646":{"name":"Tentacle","hitpoints":75,"combatLevel":99,"attackSpeed":6,"attackLevel":115,"strengthLevel":76,"defenceLevel":75,"rangeLevel":115,"magicLevel":50},"5647":{"name":"Zombie","hitpoints":20,"combatLevel":23,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"5648":{"name":"Undead Lumberjack","hitpoints":10,"combatLevel":30,"attackSpeed":4,"attackLevel":73,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"5649":{"name":"Undead Lumberjack","hitpoints":10,"combatLevel":30,"attackSpeed":4,"attackLevel":73,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"5650":{"name":"Undead Lumberjack","hitpoints":10,"combatLevel":30,"attackSpeed":4,"attackLevel":73,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"5651":{"name":"Undead Lumberjack","hitpoints":10,"combatLevel":30,"attackSpeed":4,"attackLevel":73,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"5652":{"name":"Undead Lumberjack","hitpoints":10,"combatLevel":30,"attackSpeed":4,"attackLevel":73,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"5653":{"name":"Undead Lumberjack","hitpoints":10,"combatLevel":30,"attackSpeed":4,"attackLevel":73,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"5654":{"name":"Undead Lumberjack","hitpoints":10,"combatLevel":30,"attackSpeed":4,"attackLevel":73,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"5655":{"name":"Undead Lumberjack","hitpoints":10,"combatLevel":30,"attackSpeed":4,"attackLevel":73,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"5656":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":35,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5657":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":35,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5658":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":35,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5659":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":35,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5660":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":35,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5661":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":35,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5662":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":35,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5663":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":35,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5665":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":40,"attackSpeed":4,"attackLevel":101,"strengthLevel":6,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5666":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":40,"attackSpeed":4,"attackLevel":101,"strengthLevel":6,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5667":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":40,"attackSpeed":4,"attackLevel":101,"strengthLevel":6,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5668":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":40,"attackSpeed":4,"attackLevel":101,"strengthLevel":6,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5669":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":40,"attackSpeed":4,"attackLevel":101,"strengthLevel":6,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5670":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":40,"attackSpeed":4,"attackLevel":101,"strengthLevel":6,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5671":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":40,"attackSpeed":4,"attackLevel":101,"strengthLevel":6,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5672":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":40,"attackSpeed":4,"attackLevel":101,"strengthLevel":6,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"5673":{"name":"Undead Lumberjack","hitpoints":13,"combatLevel":45,"attackSpeed":4,"attackLevel":105,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"5674":{"name":"Undead Lumberjack","hitpoints":13,"combatLevel":45,"attackSpeed":4,"attackLevel":105,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"5675":{"name":"Undead Lumberjack","hitpoints":13,"combatLevel":45,"attackSpeed":4,"attackLevel":105,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"5676":{"name":"Undead Lumberjack","hitpoints":13,"combatLevel":45,"attackSpeed":4,"attackLevel":105,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"5677":{"name":"Undead Lumberjack","hitpoints":13,"combatLevel":45,"attackSpeed":4,"attackLevel":105,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"5678":{"name":"Undead Lumberjack","hitpoints":13,"combatLevel":45,"attackSpeed":4,"attackLevel":105,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"5679":{"name":"Undead Lumberjack","hitpoints":13,"combatLevel":45,"attackSpeed":4,"attackLevel":105,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"5680":{"name":"Undead Lumberjack","hitpoints":13,"combatLevel":45,"attackSpeed":4,"attackLevel":105,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"5681":{"name":"Undead Lumberjack","hitpoints":14,"combatLevel":50,"attackSpeed":4,"attackLevel":117,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"5682":{"name":"Undead Lumberjack","hitpoints":14,"combatLevel":50,"attackSpeed":4,"attackLevel":117,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"5683":{"name":"Undead Lumberjack","hitpoints":14,"combatLevel":50,"attackSpeed":4,"attackLevel":117,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"5684":{"name":"Undead Lumberjack","hitpoints":14,"combatLevel":50,"attackSpeed":4,"attackLevel":117,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"5685":{"name":"Undead Lumberjack","hitpoints":14,"combatLevel":50,"attackSpeed":4,"attackLevel":117,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"5686":{"name":"Undead Lumberjack","hitpoints":14,"combatLevel":50,"attackSpeed":4,"attackLevel":117,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"5687":{"name":"Undead Lumberjack","hitpoints":14,"combatLevel":50,"attackSpeed":4,"attackLevel":117,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"5688":{"name":"Undead Lumberjack","hitpoints":14,"combatLevel":50,"attackSpeed":4,"attackLevel":117,"strengthLevel":16,"defenceLevel":14,"rangeLevel":1,"magicLevel":1},"5689":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":55,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5690":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":55,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5691":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":55,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5692":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":55,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5693":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":55,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5694":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":55,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5695":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":55,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5696":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":55,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5697":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":60,"attackSpeed":4,"attackLevel":137,"strengthLevel":26,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"5698":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":60,"attackSpeed":4,"attackLevel":137,"strengthLevel":26,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"5699":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":60,"attackSpeed":4,"attackLevel":137,"strengthLevel":26,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"5700":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":60,"attackSpeed":4,"attackLevel":137,"strengthLevel":26,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"5701":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":60,"attackSpeed":4,"attackLevel":137,"strengthLevel":26,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"5702":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":60,"attackSpeed":4,"attackLevel":137,"strengthLevel":26,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"5703":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":60,"attackSpeed":4,"attackLevel":137,"strengthLevel":26,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"5704":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":60,"attackSpeed":4,"attackLevel":137,"strengthLevel":26,"defenceLevel":16,"rangeLevel":1,"magicLevel":1},"5705":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":64,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5706":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":64,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5707":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":64,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5708":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":64,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5709":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":64,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5710":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":64,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5711":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":64,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5712":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":64,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5713":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":70,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5714":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":70,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5715":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":70,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5716":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":70,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5717":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":70,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5718":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":70,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5719":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":70,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5720":{"name":"Undead Lumberjack","hitpoints":12,"combatLevel":70,"attackSpeed":4,"rangeLevel":1,"magicLevel":1},"5729":{"name":"Shipyard worker","hitpoints":10,"combatLevel":11,"attackSpeed":6,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"bonusAttack":6,"bonusStrength":6},"5732":{"name":"Market Guard","hitpoints":22,"combatLevel":20,"attackSpeed":5,"attackLevel":17,"strengthLevel":18,"defenceLevel":13,"rangeLevel":1,"magicLevel":1,"stabDef":24,"slashDef":14,"crushDef":19,"rangeDef":16,"magicDef":4,"bonusAttack":9,"bonusStrength":7},"5739":{"name":"Penance Fighter","hitpoints":28,"combatLevel":30,"attackSpeed":4,"defenceLevel":25},"5740":{"name":"Penance Fighter","hitpoints":29,"combatLevel":32,"attackSpeed":4,"defenceLevel":27},"5741":{"name":"Penance Fighter","hitpoints":32,"combatLevel":37,"attackSpeed":4,"defenceLevel":34},"5742":{"name":"Penance Fighter","hitpoints":37,"combatLevel":42,"attackSpeed":4,"defenceLevel":37},"5743":{"name":"Penance Fighter","hitpoints":38,"combatLevel":47,"attackSpeed":4,"defenceLevel":44},"5744":{"name":"Penance Fighter","hitpoints":49,"combatLevel":56,"attackSpeed":4,"defenceLevel":48},"5745":{"name":"Penance Fighter","hitpoints":50,"combatLevel":61,"attackSpeed":4,"defenceLevel":52},"5746":{"name":"Penance Fighter","hitpoints":55,"combatLevel":68,"attackSpeed":4,"defenceLevel":62},"5747":{"name":"Penance Fighter","hitpoints":56,"combatLevel":77,"attackSpeed":4,"defenceLevel":66},"5757":{"name":"Penance Ranger","hitpoints":20,"combatLevel":21,"attackSpeed":4,"defenceLevel":21},"5758":{"name":"Penance Ranger","hitpoints":29,"combatLevel":25,"attackSpeed":4,"defenceLevel":29},"5759":{"name":"Penance Ranger","hitpoints":32,"combatLevel":32,"attackSpeed":4,"defenceLevel":33},"5760":{"name":"Penance Ranger","hitpoints":34,"combatLevel":38,"attackSpeed":4,"defenceLevel":42},"5761":{"name":"Penance Ranger","hitpoints":41,"combatLevel":43,"attackSpeed":4,"defenceLevel":46},"5762":{"name":"Penance Ranger","hitpoints":50,"combatLevel":51,"attackSpeed":4,"defenceLevel":54},"5763":{"name":"Penance Ranger","hitpoints":50,"combatLevel":57,"attackSpeed":4,"defenceLevel":61},"5764":{"name":"Penance Ranger","hitpoints":55,"combatLevel":64,"attackSpeed":4,"defenceLevel":68},"5765":{"name":"Penance Ranger","hitpoints":58,"combatLevel":72,"attackSpeed":4,"defenceLevel":80},"5775":{"name":"Penance Queen","hitpoints":250,"combatLevel":209,"attackSpeed":4,"attackLevel":260,"strengthLevel":92,"defenceLevel":132,"rangeLevel":116,"magicLevel":1},"5776":{"name":"Queen spawn","hitpoints":45,"combatLevel":63,"attackSpeed":4,"attackLevel":60,"strengthLevel":62,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"venomImmune":true},"5779":{"name":"Giant Mole","hitpoints":200,"combatLevel":230,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":200,"stabDef":60,"slashDef":80,"crushDef":100,"rangeDef":60,"magicDef":80},"5816":{"name":"Yak","hitpoints":50,"combatLevel":22,"attackSpeed":4,"attackLevel":20,"strengthLevel":10,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"5822":{"name":"Ice Troll King","hitpoints":150,"combatLevel":122,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":45,"crushDef":45,"rangeDef":2000,"magicDef":2000,"bonusAttack":60,"bonusStrength":60},"5823":{"name":"Ice troll runt","hitpoints":60,"combatLevel":74,"attackSpeed":4,"attackLevel":60,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"5824":{"name":"Ice troll male","hitpoints":80,"combatLevel":82,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"5825":{"name":"Ice troll female","hitpoints":80,"combatLevel":82,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"5826":{"name":"Ice troll grunt","hitpoints":80,"combatLevel":102,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"5828":{"name":"Ice troll runt","hitpoints":60,"combatLevel":74,"attackSpeed":4,"attackLevel":60,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"5829":{"name":"Ice troll male","hitpoints":80,"combatLevel":82,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"5830":{"name":"Ice troll female","hitpoints":80,"combatLevel":82,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"5831":{"name":"Ice troll grunt","hitpoints":80,"combatLevel":102,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":60,"crushDef":30,"bonusAttack":60,"bonusStrength":60},"5842":{"name":"Cow","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"5848":{"name":"Tanglefoot","hitpoints":102,"combatLevel":111,"attackSpeed":4,"attackLevel":97,"strengthLevel":97,"defenceLevel":91,"rangeLevel":1,"magicLevel":1},"5853":{"name":"Baby tanglefoot","hitpoints":40,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":38,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"5854":{"name":"Baby tanglefoot","hitpoints":40,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":38,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"5862":{"name":"Cerberus","hitpoints":600,"combatLevel":318,"slayerLevel":91,"attackSpeed":6,"attackLevel":220,"strengthLevel":220,"defenceLevel":100,"rangeLevel":220,"magicLevel":220,"range":50,"magic":50,"stabDef":50,"slashDef":100,"crushDef":25,"rangeDef":100,"magicDef":100,"bonusAttack":50,"poisonImmune":true,"venomImmune":true},"5863":{"name":"Cerberus","hitpoints":600,"combatLevel":318,"slayerLevel":91,"attackSpeed":6,"attackLevel":220,"strengthLevel":220,"defenceLevel":100,"rangeLevel":220,"magicLevel":220,"range":50,"magic":50,"stabDef":50,"slashDef":100,"crushDef":25,"rangeDef":100,"magicDef":100,"bonusAttack":50,"poisonImmune":true,"venomImmune":true},"5866":{"name":"Cerberus","hitpoints":600,"combatLevel":318,"slayerLevel":91,"attackSpeed":6,"attackLevel":220,"strengthLevel":220,"defenceLevel":100,"rangeLevel":220,"magicLevel":220,"range":50,"magic":50,"stabDef":50,"slashDef":100,"crushDef":25,"rangeDef":100,"magicDef":100,"bonusAttack":50,"poisonImmune":true,"venomImmune":true},"5872":{"name":"Baby green dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"5873":{"name":"Baby green dragon","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"5874":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"5875":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"5876":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"5877":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"5878":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5879":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5880":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5881":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5882":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"5886":{"name":"Abyssal Sire","hitpoints":400,"combatLevel":350,"slayerLevel":85,"attackSpeed":7,"attackLevel":180,"strengthLevel":136,"defenceLevel":250,"rangeLevel":1,"magicLevel":200,"stabDef":40,"slashDef":60,"crushDef":50,"rangeDef":60,"magicDef":20,"bonusAttack":65},"5887":{"name":"Abyssal Sire","hitpoints":400,"combatLevel":350,"slayerLevel":85,"attackSpeed":7,"attackLevel":180,"strengthLevel":136,"defenceLevel":250,"rangeLevel":1,"magicLevel":200,"stabDef":40,"slashDef":60,"crushDef":50,"rangeDef":60,"magicDef":20,"bonusAttack":65},"5888":{"name":"Abyssal Sire","hitpoints":400,"combatLevel":350,"slayerLevel":85,"attackSpeed":7,"attackLevel":180,"strengthLevel":136,"defenceLevel":250,"rangeLevel":1,"magicLevel":200,"stabDef":40,"slashDef":60,"crushDef":50,"rangeDef":60,"magicDef":20,"bonusAttack":65},"5889":{"name":"Abyssal Sire","hitpoints":400,"combatLevel":350,"slayerLevel":85,"attackSpeed":7,"attackLevel":180,"strengthLevel":136,"defenceLevel":250,"rangeLevel":1,"magicLevel":200,"stabDef":40,"slashDef":60,"crushDef":50,"rangeDef":60,"magicDef":20,"bonusAttack":65},"5890":{"name":"Abyssal Sire","hitpoints":400,"combatLevel":350,"slayerLevel":85,"attackSpeed":7,"attackLevel":180,"strengthLevel":136,"defenceLevel":250,"rangeLevel":1,"magicLevel":200,"stabDef":40,"slashDef":60,"crushDef":50,"rangeDef":60,"magicDef":20,"bonusAttack":65},"5891":{"name":"Abyssal Sire","hitpoints":400,"combatLevel":350,"slayerLevel":85,"attackSpeed":7,"attackLevel":180,"strengthLevel":136,"defenceLevel":250,"rangeLevel":1,"magicLevel":200,"stabDef":40,"slashDef":60,"crushDef":50,"rangeDef":60,"magicDef":20,"bonusAttack":65},"5908":{"name":"Abyssal Sire","hitpoints":400,"combatLevel":350,"slayerLevel":85,"attackSpeed":7,"attackLevel":180,"strengthLevel":136,"defenceLevel":250,"rangeLevel":1,"magicLevel":200,"stabDef":40,"slashDef":60,"crushDef":50,"rangeDef":60,"magicDef":20,"bonusAttack":65},"5914":{"name":"Respiratory system","hitpoints":50,"defenceLevel":80},"5916":{"name":"Spawn","hitpoints":15,"combatLevel":60,"attackSpeed":4,"attackLevel":120,"strengthLevel":30,"defenceLevel":30,"rangeLevel":50,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"5917":{"name":"Spawn","hitpoints":15,"combatLevel":60,"attackSpeed":4,"attackLevel":120,"strengthLevel":30,"defenceLevel":30,"rangeLevel":50,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"5918":{"name":"Scion","hitpoints":50,"combatLevel":100,"attackSpeed":4,"attackLevel":120,"strengthLevel":90,"defenceLevel":80,"rangeLevel":100,"magicLevel":1,"range":30,"bonusAttack":30,"bonusRangeStrength":30,"poisonImmune":true,"venomImmune":true},"5935":{"name":"Sand Crab","hitpoints":60,"combatLevel":15,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5936":{"name":"Sandy rocks","hitpoints":60,"combatLevel":15,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"5938":{"name":"Wallasalki","hitpoints":120,"combatLevel":98,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":80,"rangeLevel":1,"magicLevel":100,"stabDef":100,"slashDef":150,"crushDef":175,"magicDef":250},"5939":{"name":"Wallasalki","hitpoints":120,"combatLevel":98,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":80,"rangeLevel":1,"magicLevel":100,"stabDef":100,"slashDef":150,"crushDef":175,"magicDef":250},"5940":{"name":"Giant Rock Crab","hitpoints":180,"combatLevel":137,"attackSpeed":4,"attackLevel":50,"strengthLevel":80,"defenceLevel":200,"rangeLevel":1,"magicLevel":1,"stabDef":225,"slashDef":200,"crushDef":175,"rangeDef":250},"5942":{"name":"Dagannoth","hitpoints":85,"combatLevel":88,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":75,"rangeLevel":100,"magicLevel":1,"slashDef":10,"crushDef":90,"rangeDef":150,"magicDef":200},"5943":{"name":"Dagannoth","hitpoints":95,"combatLevel":90,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50,"bonusAttack":50,"bonusStrength":50},"5944":{"name":"Rock lobster","hitpoints":150,"combatLevel":127,"attackSpeed":2,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":50,"rangeDef":150,"magicDef":50},"5946":{"name":"Suspicious water","hitpoints":100,"combatLevel":34},"5947":{"name":"Spinolyp","hitpoints":100,"combatLevel":76,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":100,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":50,"magicDef":50},"5948":{"name":"Suspicious water","hitpoints":100,"combatLevel":34},"5961":{"name":"Spinolyp","hitpoints":100,"combatLevel":76,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":100,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":50,"magicDef":50},"5962":{"name":"Suspicious water","hitpoints":100,"combatLevel":34},"5963":{"name":"Spinolyp","hitpoints":100,"combatLevel":76,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":100,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":50,"magicDef":50},"5964":{"name":"Khazard trooper","hitpoints":22,"combatLevel":19,"attackSpeed":5,"attackLevel":17,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":22,"bonusAttack":9,"bonusStrength":5},"5965":{"name":"Khazard trooper","hitpoints":22,"combatLevel":19,"attackSpeed":5,"attackLevel":17,"strengthLevel":16,"defenceLevel":13,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":22,"bonusAttack":9,"bonusStrength":5},"5966":{"name":"Gnome troop","hitpoints":3,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":5,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":5,"rangeDef":4,"magicDef":2,"bonusAttack":2,"bonusStrength":3,"bonusRangeStrength":2},"5967":{"name":"Gnome troop","hitpoints":3,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":5,"magicLevel":1,"stabDef":3,"slashDef":4,"crushDef":5,"rangeDef":4,"magicDef":2,"bonusAttack":2,"bonusStrength":3,"bonusRangeStrength":2},"5968":{"name":"Gnome","hitpoints":3,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":5,"magicLevel":1,"bonusRangeStrength":2},"5969":{"name":"Gnome","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"bonusRangeStrength":2},"5970":{"name":"Gnome","hitpoints":3,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":5,"magicLevel":1,"bonusRangeStrength":2},"5971":{"name":"Mounted terrorbird gnome","hitpoints":55,"combatLevel":49,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":16,"slashDef":16,"crushDef":18,"rangeDef":10,"magicDef":15,"bonusRangeStrength":2},"5972":{"name":"Mounted terrorbird gnome","hitpoints":55,"combatLevel":49,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":16,"slashDef":16,"crushDef":18,"rangeDef":10,"magicDef":15,"bonusRangeStrength":2},"5973":{"name":"Mounted terrorbird gnome","hitpoints":55,"combatLevel":49,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":16,"slashDef":16,"crushDef":18,"rangeDef":10,"magicDef":15,"bonusRangeStrength":2},"5977":{"name":"Justiciar Zachariah","hitpoints":320,"combatLevel":348,"attackSpeed":6,"attackLevel":500,"strengthLevel":250,"defenceLevel":100,"rangeLevel":1,"magicLevel":180,"magic":80,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":200,"bonusAttack":200,"bonusMagicDamage":80,"poisonImmune":true,"venomImmune":true},"6046":{"name":"Black Guard","hitpoints":40,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":45,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":40,"bonusAttack":30,"bonusStrength":30},"6047":{"name":"Black Guard","hitpoints":40,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":45,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":40,"bonusAttack":30,"bonusStrength":30},"6048":{"name":"Black Guard","hitpoints":40,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":45,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":40,"bonusAttack":30,"bonusStrength":30},"6049":{"name":"Black Guard","hitpoints":40,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":45,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":40,"bonusAttack":30,"bonusStrength":30},"6050":{"name":"Black Guard Berserker","hitpoints":50,"combatLevel":66,"attackSpeed":3,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"bonusAttack":55,"bonusStrength":55},"6051":{"name":"Black Guard Berserker","hitpoints":50,"combatLevel":66,"attackSpeed":3,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"bonusAttack":55,"bonusStrength":55},"6052":{"name":"Black Guard Berserker","hitpoints":50,"combatLevel":66,"attackSpeed":3,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"bonusAttack":55,"bonusStrength":55},"6056":{"name":"Guard","hitpoints":50,"combatLevel":37,"attackSpeed":6,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":40,"magicLevel":1,"stabDef":18,"slashDef":23,"crushDef":27,"rangeDef":19,"magicDef":10,"bonusAttack":19,"bonusStrength":8,"bonusRangeStrength":8},"6065":{"name":"Tower Archer","hitpoints":30,"combatLevel":19,"attackSpeed":6,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":20,"magicLevel":1,"rangeDef":12,"magicDef":6,"bonusRangeStrength":16},"6066":{"name":"Tower Archer","hitpoints":50,"combatLevel":34,"attackSpeed":6,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":40,"magicLevel":1,"rangeDef":40,"magicDef":24,"bonusRangeStrength":22},"6067":{"name":"Tower Archer","hitpoints":70,"combatLevel":49,"attackSpeed":6,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":60,"magicLevel":1,"rangeDef":45,"magicDef":20,"bonusRangeStrength":31},"6068":{"name":"Tower Archer","hitpoints":90,"combatLevel":64,"attackSpeed":6,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":80,"magicLevel":1,"rangeDef":66,"magicDef":34,"bonusRangeStrength":49},"6075":{"name":"Tortoise","hitpoints":100,"combatLevel":79,"attackSpeed":4,"attackLevel":15,"strengthLevel":90,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"6076":{"name":"Tortoise","hitpoints":120,"combatLevel":92,"attackSpeed":4,"attackLevel":30,"strengthLevel":100,"defenceLevel":80,"rangeLevel":50,"magicLevel":50},"6077":{"name":"Gnome child","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6078":{"name":"Gnome child","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6079":{"name":"Gnome child","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6081":{"name":"Gnome guard","hitpoints":31,"combatLevel":23,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"bonusAttack":8,"bonusStrength":13},"6082":{"name":"Gnome guard","hitpoints":31,"combatLevel":23,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"bonusAttack":8,"bonusStrength":13},"6086":{"name":"Gnome woman","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6087":{"name":"Gnome woman","hitpoints":2,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6094":{"name":"Gnome","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"bonusRangeStrength":2},"6095":{"name":"Gnome","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"bonusRangeStrength":2},"6096":{"name":"Gnome","hitpoints":3,"combatLevel":1,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"bonusRangeStrength":2},"6097":{"name":"Gnome Archer","hitpoints":10,"combatLevel":5,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":6,"magicLevel":1},"6098":{"name":"Gnome Driver","hitpoints":10,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":5,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"6099":{"name":"Gnome Mage","hitpoints":10,"combatLevel":5,"attackSpeed":6,"attackLevel":3,"strengthLevel":1,"defenceLevel":1,"rangeLevel":5,"magicLevel":1},"6102":{"name":"Bush snake","hitpoints":25,"combatLevel":35,"attackSpeed":3,"attackLevel":25,"strengthLevel":25,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"6103":{"name":"Bush snake","hitpoints":25,"combatLevel":35,"attackSpeed":3,"attackLevel":25,"strengthLevel":25,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"6118":{"name":"Elvarg (hard)","hitpoints":240,"combatLevel":214,"attackSpeed":4,"attackLevel":210,"strengthLevel":210,"defenceLevel":70,"rangeLevel":1,"magicLevel":210,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"6119":{"name":"The Inadequacy (hard)","hitpoints":255,"combatLevel":600,"attackSpeed":4,"attackLevel":1128,"strengthLevel":340,"defenceLevel":240,"rangeLevel":340,"magicLevel":1},"6120":{"name":"The Everlasting (hard)","hitpoints":255,"combatLevel":365,"attackSpeed":6,"attackLevel":374,"strengthLevel":462,"defenceLevel":120,"rangeLevel":1,"magicLevel":1},"6121":{"name":"The Untouchable (hard)","hitpoints":180,"combatLevel":440,"attackSpeed":6,"attackLevel":374,"strengthLevel":510,"defenceLevel":434,"rangeLevel":1,"magicLevel":1},"6177":{"name":"Scion","hitpoints":50,"combatLevel":100,"attackSpeed":4,"attackLevel":120,"strengthLevel":90,"defenceLevel":80,"rangeLevel":100,"magicLevel":1,"range":30,"bonusAttack":30,"bonusRangeStrength":30,"poisonImmune":true,"venomImmune":true},"6267":{"name":"Jungle spider","hitpoints":50,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":37,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":10,"rangeDef":20,"magicDef":17},"6271":{"name":"Jungle spider","hitpoints":50,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":37,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":10,"rangeDef":20,"magicDef":17},"6272":{"name":"Large mosquito","hitpoints":3,"combatLevel":13,"attackSpeed":4,"attackLevel":5,"strengthLevel":1,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":2,"crushDef":5,"magicDef":2},"6273":{"name":"Mosquito swarm","hitpoints":9,"combatLevel":17,"attackSpeed":2,"attackLevel":10,"strengthLevel":1,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":2,"crushDef":5,"magicDef":2},"6291":{"name":"Tanglefoot (hard)","hitpoints":204,"combatLevel":199,"attackSpeed":4,"attackLevel":194,"strengthLevel":194,"defenceLevel":91,"rangeLevel":1,"magicLevel":1},"6292":{"name":"Chronozon (hard)","hitpoints":120,"combatLevel":297,"attackSpeed":4,"attackLevel":346,"strengthLevel":344,"defenceLevel":173,"rangeLevel":1,"magicLevel":1},"6293":{"name":"Bouncer (hard)","hitpoints":232,"combatLevel":244,"attackSpeed":4,"attackLevel":240,"strengthLevel":240,"defenceLevel":120,"rangeLevel":1,"magicLevel":1},"6294":{"name":"Ice Troll King (hard)","hitpoints":255,"combatLevel":213,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":45,"crushDef":45,"rangeDef":2000,"magicDef":2000,"bonusAttack":60,"bonusStrength":60},"6295":{"name":"Black demon (hard)","hitpoints":255,"combatLevel":292,"attackSpeed":4,"attackLevel":290,"strengthLevel":296,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"6297":{"name":"Glod (hard)","hitpoints":255,"combatLevel":276,"attackSpeed":4,"attackLevel":230,"strengthLevel":240,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":105,"slashDef":110,"crushDef":130,"rangeDef":100,"magicDef":125},"6298":{"name":"Treus Dayth (hard)","hitpoints":240,"combatLevel":194,"attackSpeed":4,"attackLevel":168,"strengthLevel":168,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"6299":{"name":"Black Knight Titan (hard)","hitpoints":255,"combatLevel":210,"attackSpeed":7,"attackLevel":182,"strengthLevel":200,"defenceLevel":91,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":27,"crushDef":18,"rangeDef":1000,"magicDef":1000,"bonusAttack":27,"bonusStrength":22},"6300":{"name":"Dagannoth mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6301":{"name":"Dagannoth mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6302":{"name":"Dagannoth mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6303":{"name":"Dagannoth mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6304":{"name":"Dagannoth mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6305":{"name":"Dagannoth mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6306":{"name":"Evil Chicken (hard)","hitpoints":240,"combatLevel":286,"attackSpeed":4,"defenceLevel":126,"magicLevel":400},"6307":{"name":"Culinaromancer (hard)","hitpoints":255,"combatLevel":209,"attackSpeed":4,"attackLevel":40,"strengthLevel":400,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"6308":{"name":"Agrith-Na-Na (hard)","hitpoints":255,"combatLevel":235,"attackSpeed":4,"attackLevel":166,"strengthLevel":300,"defenceLevel":82,"rangeLevel":200,"magicLevel":200,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":100,"magicDef":100,"bonusAttack":100},"6309":{"name":"Flambeed (hard)","hitpoints":255,"combatLevel":238,"attackSpeed":4,"attackLevel":240,"strengthLevel":240,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":5,"rangeDef":50,"magicDef":5,"bonusAttack":100},"6310":{"name":"Karamel (hard)","hitpoints":255,"combatLevel":186,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":200,"magicLevel":1,"range":268,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":150,"magicDef":150,"bonusRangeStrength":110},"6311":{"name":"Dessourt (hard)","hitpoints":255,"combatLevel":217,"attackSpeed":3,"attackLevel":198,"strengthLevel":198,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":150,"crushDef":150,"bonusAttack":50,"bonusStrength":50},"6312":{"name":"Gelatinnoth Mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6313":{"name":"Gelatinnoth Mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6314":{"name":"Gelatinnoth Mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6315":{"name":"Gelatinnoth Mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6316":{"name":"Gelatinnoth Mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6317":{"name":"Gelatinnoth Mother (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":81,"rangeLevel":120,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6318":{"name":"Nezikchened (hard)","hitpoints":150,"combatLevel":295,"attackSpeed":5,"attackLevel":330,"strengthLevel":336,"defenceLevel":167,"rangeLevel":320,"magicLevel":320},"6319":{"name":"Tree spirit (hard)","hitpoints":187,"combatLevel":199,"attackSpeed":4,"attackLevel":200,"strengthLevel":209,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"6320":{"name":"Me (hard)","hitpoints":135,"combatLevel":201,"attackSpeed":4,"attackLevel":225,"strengthLevel":234,"defenceLevel":74,"rangeLevel":1,"magicLevel":1},"6321":{"name":"Jungle Demon (hard)","hitpoints":255,"combatLevel":327,"attackSpeed":6,"attackLevel":340,"strengthLevel":340,"defenceLevel":170,"rangeLevel":1,"magicLevel":340,"slashDef":50,"magicDef":50,"bonusAttack":50,"bonusStrength":50},"6322":{"name":"The Kendal (hard)","hitpoints":150,"combatLevel":210,"attackSpeed":4,"attackLevel":195,"strengthLevel":195,"defenceLevel":60,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":20,"bonusAttack":5,"bonusStrength":10},"6323":{"name":"Giant Roc (hard)","hitpoints":255,"combatLevel":257,"attackSpeed":6,"attackLevel":260,"strengthLevel":260,"defenceLevel":100,"rangeLevel":260,"magicLevel":1,"magicDef":150},"6324":{"name":"Slagilith (hard)","hitpoints":150,"combatLevel":202,"attackSpeed":4,"attackLevel":150,"strengthLevel":300,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":5,"rangeDef":50,"magicDef":5,"bonusAttack":10},"6325":{"name":"Moss Guardian (hard)","hitpoints":240,"combatLevel":182,"attackSpeed":6,"attackLevel":165,"strengthLevel":165,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"bonusAttack":66,"bonusStrength":62},"6326":{"name":"Skeleton Hellhound (hard)","hitpoints":132,"combatLevel":198,"attackSpeed":4,"attackLevel":168,"strengthLevel":264,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"6327":{"name":"Agrith Naar (hard)","hitpoints":209,"combatLevel":196,"attackSpeed":4,"attackLevel":182,"strengthLevel":198,"defenceLevel":82,"rangeLevel":220,"magicLevel":220},"6329":{"name":"Khazard warlord (hard)","hitpoints":255,"combatLevel":192,"attackSpeed":4,"attackLevel":165,"strengthLevel":170,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"6330":{"name":"Dad (hard)","hitpoints":240,"combatLevel":201,"attackSpeed":8,"attackLevel":132,"strengthLevel":264,"defenceLevel":50,"stabDef":25,"slashDef":25,"crushDef":40,"rangeDef":200,"magicDef":200,"bonusAttack":40,"bonusStrength":70},"6331":{"name":"Arrg (hard)","hitpoints":255,"combatLevel":210,"attackSpeed":4,"attackLevel":140,"strengthLevel":280,"defenceLevel":40,"rangeLevel":140,"stabDef":35,"slashDef":60,"crushDef":35,"rangeDef":200,"magicDef":200,"bonusAttack":60,"bonusStrength":100},"6332":{"name":"Count Draynor (hard)","hitpoints":210,"combatLevel":177,"attackSpeed":4,"attackLevel":198,"strengthLevel":165,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":1,"crushDef":3},"6333":{"name":"Witch's experiment (hard)","hitpoints":63,"combatLevel":47,"attackSpeed":4,"attackLevel":54,"strengthLevel":30,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"6334":{"name":"Witch's experiment (second form) (hard)","hitpoints":93,"combatLevel":77,"attackSpeed":4,"attackLevel":84,"strengthLevel":60,"defenceLevel":29,"rangeLevel":1,"magicLevel":1},"6335":{"name":"Witch's experiment (third form) (hard)","hitpoints":103,"combatLevel":90,"attackSpeed":4,"attackLevel":95,"strengthLevel":75,"defenceLevel":39,"rangeLevel":1,"magicLevel":1},"6336":{"name":"Witch's experiment (fourth form) (hard)","hitpoints":113,"combatLevel":103,"attackSpeed":4,"attackLevel":105,"strengthLevel":88,"defenceLevel":49,"rangeLevel":1,"magicLevel":1},"6337":{"name":"Nazastarool (hard)","hitpoints":154,"combatLevel":176,"attackSpeed":4,"attackLevel":187,"strengthLevel":176,"defenceLevel":80},"6338":{"name":"Nazastarool (hard)","hitpoints":180,"combatLevel":153,"attackSpeed":4,"attackLevel":150,"strengthLevel":140,"defenceLevel":58,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5,"magicDef":5,"bonusAttack":15,"bonusStrength":15},"6339":{"name":"Nazastarool (hard)","hitpoints":176,"combatLevel":181,"attackSpeed":4,"attackLevel":187,"strengthLevel":176,"defenceLevel":80},"6340":{"name":"Cow (hard)","hitpoints":160,"combatLevel":170,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6342":{"name":"Barrelchest","hitpoints":134,"combatLevel":190,"attackSpeed":5,"attackLevel":170,"strengthLevel":145,"defenceLevel":140,"rangeLevel":1,"magicLevel":90,"bonusAttack":80,"bonusStrength":80},"6343":{"name":"Giant scarab","hitpoints":130,"combatLevel":191,"attackSpeed":4,"attackLevel":169,"strengthLevel":190,"defenceLevel":169,"rangeLevel":190,"magicLevel":1,"stabDef":70,"slashDef":99,"crushDef":99,"rangeDef":149,"magicDef":159},"6344":{"name":"Dessous","hitpoints":200,"combatLevel":139,"attackSpeed":3,"attackLevel":99,"strengthLevel":99,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":150,"crushDef":150,"bonusAttack":50,"bonusStrength":50},"6345":{"name":"Kamil","hitpoints":130,"combatLevel":154,"attackSpeed":4,"attackLevel":190,"strengthLevel":80,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":60,"crushDef":35,"bonusAttack":60,"bonusStrength":100},"6346":{"name":"Damis","hitpoints":90,"combatLevel":103,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":80},"6347":{"name":"Damis","hitpoints":200,"combatLevel":174,"attackSpeed":3,"attackLevel":160,"strengthLevel":100,"defenceLevel":160,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":120,"magicDef":80,"bonusStrength":100},"6348":{"name":"Fareed","hitpoints":130,"combatLevel":167,"attackSpeed":4,"attackLevel":190,"strengthLevel":120,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":100,"slashDef":100,"crushDef":100,"bonusStrength":120},"6349":{"name":"Elvarg","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":70,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"6350":{"name":"The Inadequacy","hitpoints":180,"combatLevel":343,"attackSpeed":4,"attackLevel":564,"strengthLevel":170,"defenceLevel":240,"rangeLevel":170,"magicLevel":1},"6351":{"name":"The Everlasting","hitpoints":230,"combatLevel":223,"attackSpeed":6,"attackLevel":187,"strengthLevel":231,"defenceLevel":120,"rangeLevel":1,"magicLevel":1},"6352":{"name":"The Untouchable","hitpoints":90,"combatLevel":274,"attackSpeed":6,"attackLevel":187,"strengthLevel":255,"defenceLevel":434,"rangeLevel":1,"magicLevel":1},"6353":{"name":"Tanglefoot","hitpoints":102,"combatLevel":111,"attackSpeed":4,"attackLevel":97,"strengthLevel":97,"defenceLevel":91,"rangeLevel":1,"magicLevel":1},"6354":{"name":"Chronozon","hitpoints":60,"combatLevel":170,"attackSpeed":4,"attackLevel":173,"strengthLevel":172,"defenceLevel":173,"rangeLevel":1,"magicLevel":1},"6355":{"name":"Bouncer","hitpoints":116,"combatLevel":137,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":1},"6356":{"name":"Ice Troll King","hitpoints":150,"combatLevel":122,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":45,"slashDef":45,"crushDef":45,"rangeDef":2000,"magicDef":2000,"bonusAttack":60,"bonusStrength":60},"6357":{"name":"Black demon","hitpoints":157,"combatLevel":172,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"6358":{"name":"Glod","hitpoints":160,"combatLevel":138,"attackSpeed":4,"attackLevel":115,"strengthLevel":120,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":105,"slashDef":110,"crushDef":130,"rangeDef":100,"magicDef":125},"6359":{"name":"Treus Dayth","hitpoints":100,"combatLevel":95,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"6360":{"name":"Black Knight Titan","hitpoints":142,"combatLevel":120,"attackSpeed":7,"attackLevel":91,"strengthLevel":100,"defenceLevel":91,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":27,"crushDef":18,"rangeDef":1000,"magicDef":1000,"bonusAttack":27,"bonusStrength":22},"6361":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6362":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6363":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6364":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6365":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6366":{"name":"Dagannoth mother","hitpoints":120,"combatLevel":100,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6367":{"name":"Evil Chicken","hitpoints":120,"combatLevel":159,"attackSpeed":4,"defenceLevel":126,"magicLevel":200},"6368":{"name":"Culinaromancer","hitpoints":150,"combatLevel":75,"attackSpeed":4,"attackLevel":10,"strengthLevel":100,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"6369":{"name":"Agrith-Na-Na","hitpoints":200,"combatLevel":146,"attackSpeed":4,"attackLevel":83,"strengthLevel":150,"defenceLevel":82,"rangeLevel":100,"magicLevel":100,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":100,"magicDef":100,"bonusAttack":100},"6370":{"name":"Flambeed","hitpoints":210,"combatLevel":149,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":5,"rangeDef":50,"magicDef":5,"bonusAttack":100},"6371":{"name":"Karamel","hitpoints":250,"combatLevel":136,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":100,"magicLevel":1,"range":134,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":150,"magicDef":150,"bonusRangeStrength":55},"6372":{"name":"Dessourt","hitpoints":130,"combatLevel":121,"attackSpeed":3,"attackLevel":99,"strengthLevel":99,"defenceLevel":99,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":150,"crushDef":150,"bonusAttack":50,"bonusStrength":50},"6373":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6374":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6375":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6376":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6377":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6378":{"name":"Gelatinnoth Mother","hitpoints":240,"combatLevel":130,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":50,"magicDef":50},"6379":{"name":"Nezikchened","hitpoints":150,"combatLevel":187,"attackSpeed":5,"attackLevel":165,"strengthLevel":168,"defenceLevel":167,"rangeLevel":160,"magicLevel":160},"6380":{"name":"Tree spirit","hitpoints":85,"combatLevel":101,"attackSpeed":4,"attackLevel":90,"strengthLevel":95,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"6381":{"name":"Me","hitpoints":45,"combatLevel":79,"attackSpeed":4,"attackLevel":75,"strengthLevel":78,"defenceLevel":74,"rangeLevel":1,"magicLevel":1},"6382":{"name":"Jungle Demon","hitpoints":170,"combatLevel":195,"attackSpeed":6,"attackLevel":170,"strengthLevel":170,"defenceLevel":170,"rangeLevel":1,"magicLevel":170,"slashDef":50,"magicDef":50,"bonusAttack":50,"bonusStrength":50},"6383":{"name":"The Kendal","hitpoints":50,"combatLevel":70,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":60,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":20,"bonusAttack":5,"bonusStrength":10},"6384":{"name":"Giant Roc","hitpoints":250,"combatLevel":172,"attackSpeed":6,"attackLevel":130,"strengthLevel":130,"defenceLevel":100,"rangeLevel":130,"magicLevel":1,"magicDef":150},"6385":{"name":"Slagilith","hitpoints":60,"combatLevel":92,"attackSpeed":4,"attackLevel":60,"strengthLevel":120,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":5,"rangeDef":50,"magicDef":5,"bonusAttack":10},"6386":{"name":"Moss Guardian","hitpoints":120,"combatLevel":84,"attackSpeed":6,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"bonusAttack":66,"bonusStrength":62},"6387":{"name":"Skeleton Hellhound","hitpoints":55,"combatLevel":97,"attackSpeed":4,"attackLevel":70,"strengthLevel":110,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"6388":{"name":"Agrith Naar","hitpoints":95,"combatLevel":100,"attackSpeed":4,"attackLevel":83,"strengthLevel":90,"defenceLevel":82,"rangeLevel":100,"magicLevel":100},"6390":{"name":"Khazard warlord","hitpoints":170,"combatLevel":112,"attackSpeed":4,"attackLevel":75,"strengthLevel":78,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"6391":{"name":"Dad","hitpoints":120,"combatLevel":101,"attackSpeed":8,"attackLevel":60,"strengthLevel":120,"defenceLevel":50,"stabDef":25,"slashDef":25,"crushDef":40,"rangeDef":200,"magicDef":200,"bonusAttack":40,"bonusStrength":70},"6392":{"name":"Arrg","hitpoints":140,"combatLevel":113,"attackSpeed":4,"attackLevel":70,"strengthLevel":140,"defenceLevel":40,"rangeLevel":70,"stabDef":35,"slashDef":60,"crushDef":35,"rangeDef":200,"magicDef":200,"bonusAttack":60,"bonusStrength":100},"6393":{"name":"Count Draynor","hitpoints":35,"combatLevel":34,"attackSpeed":4,"attackLevel":30,"strengthLevel":25,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":1,"crushDef":3},"6394":{"name":"Witch's experiment","hitpoints":21,"combatLevel":19,"attackSpeed":4,"attackLevel":18,"strengthLevel":10,"defenceLevel":19,"rangeLevel":1,"magicLevel":1},"6395":{"name":"Witch's experiment (second form)","hitpoints":31,"combatLevel":30,"attackSpeed":4,"attackLevel":28,"strengthLevel":20,"defenceLevel":29,"rangeLevel":1,"magicLevel":1},"6396":{"name":"Witch's experiment (third form)","hitpoints":41,"combatLevel":42,"attackSpeed":4,"attackLevel":38,"strengthLevel":30,"defenceLevel":39,"rangeLevel":1,"magicLevel":1},"6397":{"name":"Witch's experiment (fourth form)","hitpoints":51,"combatLevel":53,"attackSpeed":4,"attackLevel":48,"strengthLevel":40,"defenceLevel":49,"rangeLevel":1,"magicLevel":1},"6398":{"name":"Nazastarool","hitpoints":70,"combatLevel":91,"attackSpeed":4,"attackLevel":85,"strengthLevel":80,"defenceLevel":80},"6399":{"name":"Nazastarool","hitpoints":70,"combatLevel":68,"attackSpeed":4,"attackLevel":58,"strengthLevel":54,"defenceLevel":58,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5,"magicDef":5,"bonusAttack":15,"bonusStrength":15},"6400":{"name":"Nazastarool","hitpoints":80,"combatLevel":93,"attackSpeed":4,"attackLevel":85,"strengthLevel":80,"defenceLevel":80},"6401":{"name":"Cow","hitpoints":8,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"6402":{"name":"Mosquito swarm","hitpoints":15,"combatLevel":20,"attackSpeed":2,"attackLevel":15,"strengthLevel":1,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":2,"slashDef":2,"crushDef":5,"magicDef":2},"6406":{"name":"Tribesman","hitpoints":39,"combatLevel":32,"attackSpeed":4,"attackLevel":23,"strengthLevel":27,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":6,"crushDef":6,"bonusAttack":8,"bonusStrength":5},"6407":{"name":"Tribesman","hitpoints":39,"combatLevel":32,"attackSpeed":4,"attackLevel":23,"strengthLevel":27,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":6,"crushDef":6,"bonusAttack":8,"bonusStrength":5},"6408":{"name":"Broodoo victim","hitpoints":100,"combatLevel":60,"attackSpeed":6,"defenceLevel":26,"rangeLevel":1,"magicLevel":60,"magic":25,"stabDef":20,"slashDef":20,"crushDef":20},"6409":{"name":"Broodoo victim","hitpoints":100,"combatLevel":60,"attackSpeed":6,"defenceLevel":26,"rangeLevel":1,"magicLevel":60,"magic":25,"stabDef":20,"slashDef":20,"crushDef":20},"6410":{"name":"Broodoo victim","hitpoints":100,"combatLevel":60,"attackSpeed":6,"defenceLevel":26,"rangeLevel":1,"magicLevel":60,"magic":25,"stabDef":20,"slashDef":20,"crushDef":20},"6411":{"name":"Broodoo victim","hitpoints":100,"combatLevel":60,"attackSpeed":6,"defenceLevel":26,"rangeLevel":1,"magicLevel":60,"magic":25,"stabDef":20,"slashDef":20,"crushDef":20},"6412":{"name":"Broodoo victim","hitpoints":100,"combatLevel":60,"attackSpeed":6,"defenceLevel":26,"rangeLevel":1,"magicLevel":60,"magic":25,"stabDef":20,"slashDef":20,"crushDef":20},"6413":{"name":"Broodoo victim","hitpoints":100,"combatLevel":60,"attackSpeed":6,"defenceLevel":26,"rangeLevel":1,"magicLevel":60,"magic":25,"stabDef":20,"slashDef":20,"crushDef":20},"6434":{"name":"Cave goblin","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6435":{"name":"Cave goblin","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6436":{"name":"Cave goblin","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6437":{"name":"Cave goblin","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6438":{"name":"Animated steel armour","hitpoints":50,"combatLevel":53,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":2,"rangeLevel":2,"magicLevel":2,"stabDef":50,"slashDef":25,"crushDef":25,"rangeDef":400,"magicDef":400,"bonusAttack":4,"bonusStrength":5,"poisonImmune":true,"venomImmune":true},"6440":{"name":"Giant skeleton","hitpoints":110,"combatLevel":100,"attackSpeed":6,"attackLevel":84,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40,"bonusStrength":15},"6441":{"name":"Skeleton","hitpoints":85,"combatLevel":94,"attackSpeed":6,"attackLevel":70,"strengthLevel":80,"defenceLevel":60,"rangeLevel":1,"magicLevel":110,"stabDef":50,"slashDef":40,"rangeDef":40,"bonusStrength":5},"6442":{"name":"Skeleton","hitpoints":80,"combatLevel":77,"attackSpeed":4,"attackLevel":72,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40,"bonusStrength":5},"6443":{"name":"Skeleton","hitpoints":71,"combatLevel":81,"attackSpeed":4,"attackLevel":72,"strengthLevel":64,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"rangeDef":40},"6444":{"name":"Skeleton","hitpoints":40,"combatLevel":42,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40},"6445":{"name":"Skeleton","hitpoints":53,"combatLevel":59,"attackSpeed":4,"attackLevel":50,"strengthLevel":46,"defenceLevel":62,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40},"6446":{"name":"Skeleton","hitpoints":42,"combatLevel":42,"attackSpeed":4,"attackLevel":36,"strengthLevel":38,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40},"6447":{"name":"Skeleton","hitpoints":58,"combatLevel":63,"attackSpeed":4,"attackLevel":56,"strengthLevel":61,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40},"6448":{"name":"Skeleton","hitpoints":26,"combatLevel":40,"attackSpeed":4,"attackLevel":33,"strengthLevel":31,"defenceLevel":52,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40},"6449":{"name":"Zombie","hitpoints":38,"combatLevel":40,"attackSpeed":4,"attackLevel":36,"strengthLevel":35,"defenceLevel":33,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6450":{"name":"Zombie","hitpoints":40,"combatLevel":42,"attackSpeed":4,"attackLevel":39,"strengthLevel":35,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6451":{"name":"Zombie","hitpoints":42,"combatLevel":47,"attackSpeed":4,"attackLevel":42,"strengthLevel":39,"defenceLevel":42,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6452":{"name":"Zombie","hitpoints":48,"combatLevel":50,"attackSpeed":4,"attackLevel":44,"strengthLevel":42,"defenceLevel":44,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6453":{"name":"Zombie","hitpoints":50,"combatLevel":56,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":47,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6454":{"name":"Zombie","hitpoints":57,"combatLevel":61,"attackSpeed":4,"attackLevel":53,"strengthLevel":55,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6455":{"name":"Zombie","hitpoints":63,"combatLevel":67,"attackSpeed":4,"attackLevel":56,"strengthLevel":58,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6456":{"name":"Zombie","hitpoints":65,"combatLevel":70,"attackSpeed":4,"attackLevel":61,"strengthLevel":60,"defenceLevel":61,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6457":{"name":"Zombie","hitpoints":68,"combatLevel":72,"attackSpeed":4,"attackLevel":61,"strengthLevel":63,"defenceLevel":62,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6458":{"name":"Zombie","hitpoints":71,"combatLevel":76,"attackSpeed":4,"attackLevel":69,"strengthLevel":65,"defenceLevel":62,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6459":{"name":"Zombie","hitpoints":73,"combatLevel":80,"attackSpeed":4,"attackLevel":72,"strengthLevel":73,"defenceLevel":62,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6460":{"name":"Zombie","hitpoints":75,"combatLevel":85,"attackSpeed":4,"attackLevel":71,"strengthLevel":81,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6461":{"name":"Zombie","hitpoints":76,"combatLevel":86,"attackSpeed":4,"attackLevel":74,"strengthLevel":80,"defenceLevel":71,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6462":{"name":"Zombie","hitpoints":81,"combatLevel":90,"attackSpeed":4,"attackLevel":82,"strengthLevel":80,"defenceLevel":72,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6463":{"name":"Zombie","hitpoints":92,"combatLevel":95,"attackSpeed":4,"attackLevel":85,"strengthLevel":80,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6464":{"name":"Zombie","hitpoints":96,"combatLevel":98,"attackSpeed":4,"attackLevel":85,"strengthLevel":83,"defenceLevel":81,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6465":{"name":"Zombie","hitpoints":102,"combatLevel":100,"attackSpeed":4,"attackLevel":86,"strengthLevel":83,"defenceLevel":81,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6466":{"name":"Zombie","hitpoints":76,"combatLevel":81,"attackSpeed":4,"attackLevel":69,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"6467":{"name":"Skeleton","hitpoints":69,"combatLevel":72,"attackSpeed":4,"attackLevel":75,"strengthLevel":61,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40,"bonusStrength":15},"6468":{"name":"Skeleton","hitpoints":92,"combatLevel":87,"attackSpeed":4,"attackLevel":75,"strengthLevel":80,"defenceLevel":55,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":40,"rangeDef":40,"bonusStrength":15},"6469":{"name":"Possessed pickaxe","hitpoints":40,"combatLevel":50,"attackSpeed":4,"attackLevel":40,"strengthLevel":55,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"6470":{"name":"Animated spade","hitpoints":40,"combatLevel":50,"attackSpeed":4,"attackLevel":40,"strengthLevel":55,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"6473":{"name":"Terror dog","hitpoints":87,"combatLevel":110,"slayerLevel":40,"attackSpeed":4,"attackLevel":110,"strengthLevel":104,"defenceLevel":78,"rangeLevel":1,"magicLevel":1,"bonusStrength":20},"6474":{"name":"Terror dog","hitpoints":82,"combatLevel":100,"slayerLevel":40,"attackSpeed":4,"attackLevel":100,"strengthLevel":90,"defenceLevel":74,"rangeLevel":1,"magicLevel":1,"bonusStrength":10},"6476":{"name":"Tarn","hitpoints":80,"combatLevel":69,"slayerLevel":40,"attackSpeed":4,"attackLevel":40,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50},"6477":{"name":"Mutant tarn","hitpoints":80,"combatLevel":69,"slayerLevel":40,"attackSpeed":4,"attackLevel":40,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50},"6492":{"name":"Kree'arra","hitpoints":255,"combatLevel":580,"attackSpeed":3,"attackLevel":300,"strengthLevel":200,"defenceLevel":260,"rangeLevel":380,"magicLevel":200,"range":120,"stabDef":180,"slashDef":180,"crushDef":180,"rangeDef":200,"magicDef":200,"bonusAttack":136,"bonusStrength":12,"bonusRangeStrength":50},"6493":{"name":"Commander Zilyana","hitpoints":255,"combatLevel":596,"attackSpeed":2,"attackLevel":280,"strengthLevel":196,"defenceLevel":300,"rangeLevel":250,"magicLevel":300,"magic":200,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":100,"magicDef":100,"bonusAttack":195,"bonusStrength":20,"poisonImmune":true,"venomImmune":true},"6494":{"name":"General Graardor","hitpoints":255,"combatLevel":624,"attackSpeed":6,"attackLevel":280,"strengthLevel":350,"defenceLevel":250,"rangeLevel":350,"magicLevel":80,"range":100,"stabDef":90,"slashDef":90,"crushDef":90,"rangeDef":90,"magicDef":298,"bonusAttack":120,"bonusStrength":43,"bonusRangeStrength":40,"poisonImmune":true,"venomImmune":true},"6495":{"name":"K'ril Tsutsaroth","hitpoints":255,"combatLevel":650,"attackSpeed":6,"attackLevel":340,"strengthLevel":300,"defenceLevel":270,"rangeLevel":1,"magicLevel":200,"stabDef":80,"slashDef":80,"crushDef":80,"rangeDef":80,"magicDef":130,"bonusAttack":160,"bonusStrength":31},"6496":{"name":"Dagannoth Supreme","hitpoints":255,"combatLevel":303,"attackSpeed":4,"attackLevel":255,"strengthLevel":255,"defenceLevel":128,"rangeLevel":255,"magicLevel":255,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":550,"magicDef":255},"6497":{"name":"Dagannoth Prime","hitpoints":255,"combatLevel":303,"attackSpeed":4,"attackLevel":255,"strengthLevel":255,"defenceLevel":255,"magicLevel":255,"stabDef":255,"slashDef":255,"crushDef":255,"rangeDef":10,"magicDef":255},"6498":{"name":"Dagannoth Rex","hitpoints":255,"combatLevel":303,"attackSpeed":4,"attackLevel":255,"strengthLevel":255,"defenceLevel":255,"rangeLevel":255,"stabDef":255,"slashDef":255,"crushDef":255,"rangeDef":255,"magicDef":10},"6499":{"name":"Giant Mole","hitpoints":200,"combatLevel":230,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":200,"stabDef":60,"slashDef":80,"crushDef":100,"rangeDef":60,"magicDef":80},"6500":{"name":"Kalphite Queen","hitpoints":255,"combatLevel":333,"attackSpeed":4,"attackLevel":300,"strengthLevel":300,"defenceLevel":300,"rangeLevel":1,"magicLevel":150,"stabDef":50,"slashDef":50,"crushDef":10,"rangeDef":100,"magicDef":100},"6501":{"name":"Kalphite Queen","hitpoints":255,"combatLevel":333,"attackSpeed":4,"attackLevel":300,"strengthLevel":300,"defenceLevel":300,"rangeLevel":1,"magicLevel":150,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":10,"magicDef":10},"6503":{"name":"Callisto","hitpoints":255,"combatLevel":470,"attackSpeed":4,"attackLevel":350,"strengthLevel":370,"defenceLevel":440,"rangeLevel":1,"magicLevel":1,"stabDef":135,"slashDef":104,"crushDef":175,"rangeDef":230,"magicDef":900,"poisonImmune":true,"venomImmune":true},"6504":{"name":"Venenatis","hitpoints":255,"combatLevel":464,"attackLevel":470,"strengthLevel":490,"defenceLevel":490,"rangeLevel":1,"magicLevel":150,"stabDef":260,"slashDef":260,"crushDef":260,"rangeDef":100,"magicDef":850,"poisonImmune":true,"venomImmune":true},"6505":{"name":"Chaos Elemental","hitpoints":250,"combatLevel":305,"attackSpeed":5,"attackLevel":270,"strengthLevel":270,"defenceLevel":270,"rangeLevel":270,"magicLevel":270,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":70,"magicDef":70},"6506":{"name":"TzTok-Jad","hitpoints":250,"combatLevel":702,"attackSpeed":8,"attackLevel":640,"strengthLevel":960,"defenceLevel":480,"rangeLevel":960,"magicLevel":480,"magic":60},"6574":{"name":"Gnome guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6575":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6576":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6579":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6580":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6581":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6582":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6583":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6587":{"name":"Armadylian guard","hitpoints":132,"combatLevel":97,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":82,"rangeLevel":90,"magicLevel":80,"magicDef":3},"6588":{"name":"Bandosian guard","hitpoints":130,"combatLevel":125,"attackSpeed":5,"attackLevel":110,"strengthLevel":115,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":12,"slashDef":14,"crushDef":13,"rangeDef":13,"magicDef":5,"bonusAttack":19,"bonusStrength":4,"poisonImmune":true},"6593":{"name":"Lava dragon","hitpoints":230,"combatLevel":252,"attackSpeed":4,"attackLevel":240,"strengthLevel":220,"defenceLevel":220,"rangeLevel":1,"magicLevel":1,"stabDef":70,"slashDef":90,"crushDef":90,"rangeDef":70,"magicDef":80},"6594":{"name":"Ent","hitpoints":105,"combatLevel":101,"attackSpeed":4,"attackLevel":80,"strengthLevel":95,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":30,"magicDef":40},"6596":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"6597":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"6598":{"name":"Zombie","hitpoints":24,"combatLevel":18,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1},"6600":{"name":"Runite Golem","hitpoints":170,"combatLevel":178,"attackSpeed":4,"attackLevel":140,"strengthLevel":150,"defenceLevel":165,"rangeLevel":1,"magicLevel":1},"6603":{"name":"Rogue","hitpoints":125,"combatLevel":135,"attackSpeed":4,"attackLevel":140,"strengthLevel":140,"defenceLevel":150,"rangeLevel":1,"magicLevel":1},"6604":{"name":"Mammoth","hitpoints":130,"combatLevel":80,"attackSpeed":4,"attackLevel":55,"strengthLevel":60,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"6605":{"name":"Bandit","hitpoints":155,"combatLevel":130,"attackSpeed":4,"attackLevel":57,"strengthLevel":57,"defenceLevel":57,"rangeLevel":1,"magicLevel":1,"slashDef":23,"crushDef":22,"bonusAttack":57,"bonusStrength":52},"6606":{"name":"Dark warrior","hitpoints":165,"combatLevel":145,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":55,"rangeLevel":1,"magicLevel":1,"stabDef":106,"slashDef":109,"crushDef":139,"bonusAttack":80,"bonusStrength":76},"6607":{"name":"Elder Chaos druid","hitpoints":150,"combatLevel":129,"attackSpeed":4,"attackLevel":98,"strengthLevel":98,"defenceLevel":65,"rangeLevel":1,"magicLevel":110},"6608":{"name":"Ankou","hitpoints":70,"combatLevel":86,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"6609":{"name":"Callisto","hitpoints":255,"combatLevel":470,"attackSpeed":4,"attackLevel":350,"strengthLevel":370,"defenceLevel":440,"rangeLevel":1,"magicLevel":1,"stabDef":135,"slashDef":104,"crushDef":175,"rangeDef":230,"magicDef":900,"poisonImmune":true,"venomImmune":true},"6610":{"name":"Venenatis","hitpoints":255,"combatLevel":464,"attackLevel":470,"strengthLevel":490,"defenceLevel":490,"rangeLevel":1,"magicLevel":150,"stabDef":260,"slashDef":260,"crushDef":260,"rangeDef":100,"magicDef":850,"poisonImmune":true,"venomImmune":true},"6611":{"name":"Vet'ion","hitpoints":255,"combatLevel":454,"attackSpeed":4,"attackLevel":430,"strengthLevel":430,"defenceLevel":395,"rangeLevel":1,"magicLevel":300,"stabDef":201,"slashDef":200,"rangeDef":270,"magicDef":250},"6612":{"name":"Vet'ion Reborn","hitpoints":255,"combatLevel":454,"attackSpeed":4,"attackLevel":430,"strengthLevel":430,"defenceLevel":395,"rangeLevel":1,"magicLevel":300,"stabDef":201,"slashDef":200,"rangeDef":270,"magicDef":250},"6613":{"name":"Skeleton Hellhound","hitpoints":110,"combatLevel":214,"attackSpeed":4,"attackLevel":210,"strengthLevel":250,"defenceLevel":150,"rangeLevel":1,"magicLevel":1,"stabDef":101,"slashDef":103,"crushDef":10,"rangeDef":266,"magicDef":180},"6614":{"name":"Greater Skeleton Hellhound","hitpoints":190,"combatLevel":281,"attackSpeed":4,"attackLevel":240,"strengthLevel":310,"defenceLevel":220,"rangeLevel":1,"magicLevel":1,"stabDef":150,"slashDef":163,"crushDef":20,"rangeDef":275,"magicDef":210},"6615":{"name":"Scorpia","hitpoints":200,"combatLevel":225,"attackSpeed":4,"attackLevel":250,"strengthLevel":150,"defenceLevel":180,"rangeLevel":1,"magicLevel":1,"stabDef":246,"slashDef":284,"crushDef":284,"rangeDef":284,"magicDef":44,"bonusAttack":60},"6616":{"name":"Scorpia's offspring","hitpoints":2,"combatLevel":15,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":30,"magicLevel":1,"range":900},"6617":{"name":"Scorpia's guardian","hitpoints":70,"combatLevel":47,"attackLevel":1,"strengthLevel":1,"defenceLevel":60,"rangeLevel":30,"magicLevel":30},"6618":{"name":"Crazy archaeologist","hitpoints":225,"combatLevel":204,"attackSpeed":3,"attackLevel":160,"strengthLevel":90,"defenceLevel":240,"rangeLevel":180,"magicLevel":1,"range":75,"stabDef":5,"slashDef":5,"crushDef":30,"rangeDef":250,"bonusAttack":250,"bonusStrength":25},"6619":{"name":"Chaos Fanatic","hitpoints":225,"combatLevel":202,"attackSpeed":2,"attackLevel":1,"strengthLevel":1,"defenceLevel":220,"rangeLevel":1,"magicLevel":200,"range":75,"stabDef":260,"slashDef":260,"crushDef":250,"rangeDef":80,"magicDef":280},"6624":{"name":"Energy sprite","hitpoints":62},"6698":{"name":"Ghost guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6699":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6700":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6701":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6702":{"name":"Guard","hitpoints":800,"combatLevel":1337,"attackSpeed":4,"attackLevel":800,"strengthLevel":400,"defenceLevel":300,"rangeLevel":1,"magicLevel":1,"stabDef":8,"slashDef":9,"crushDef":7,"rangeDef":8,"bonusAttack":60,"bonusStrength":7},"6716":{"name":"Chaotic death spawn","hitpoints":50,"combatLevel":215,"attackSpeed":3,"attackLevel":380,"strengthLevel":1,"defenceLevel":70,"rangeLevel":380,"magicLevel":380},"6723":{"name":"Chaotic death spawn","hitpoints":50,"combatLevel":215,"attackSpeed":3,"attackLevel":380,"strengthLevel":1,"defenceLevel":70,"rangeLevel":380,"magicLevel":380},"6725":{"name":"Rock Golem","combatLevel":14,"attackSpeed":4},"6726":{"name":"Rock Golem","combatLevel":29,"attackSpeed":4},"6727":{"name":"Rock Golem","combatLevel":49,"attackSpeed":4},"6728":{"name":"Rock Golem","hitpoints":86,"combatLevel":79,"attackSpeed":4},"6729":{"name":"Rock Golem","hitpoints":120,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":75,"magicLevel":1},"6730":{"name":"Rock Golem","hitpoints":166,"combatLevel":159,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":80,"magicLevel":1},"6732":{"name":"River troll","combatLevel":14,"attackSpeed":4,"attackLevel":17,"strengthLevel":17,"defenceLevel":17,"rangeLevel":1,"magicLevel":1},"6733":{"name":"River troll","combatLevel":29,"attackSpeed":4,"attackLevel":32,"strengthLevel":32,"defenceLevel":32,"rangeLevel":1,"magicLevel":1},"6734":{"name":"River troll","combatLevel":49,"attackSpeed":4,"attackLevel":48,"strengthLevel":48,"defenceLevel":48,"rangeLevel":1,"magicLevel":1},"6735":{"name":"River troll","hitpoints":85,"combatLevel":79,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1},"6736":{"name":"River troll","hitpoints":120,"combatLevel":120,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"6737":{"name":"River troll","hitpoints":170,"combatLevel":159,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1},"6739":{"name":"Evil Chicken","hitpoints":120,"combatLevel":159,"attackSpeed":4,"defenceLevel":126,"magicLevel":200},"6740":{"name":"Shade","hitpoints":170,"combatLevel":159,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1},"6741":{"name":"Zombie","hitpoints":170,"combatLevel":159,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1},"6762":{"name":"Pyrelord","hitpoints":80,"combatLevel":60,"slayerLevel":30,"attackSpeed":4,"attackLevel":60,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10},"6766":{"name":"Lizardman shaman","hitpoints":150,"combatLevel":150,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":140,"rangeLevel":120,"magicLevel":130,"range":45,"slashDef":40,"crushDef":30,"magicDef":50,"bonusAttack":45,"bonusStrength":38,"bonusRangeStrength":38},"6767":{"name":"Lizardman shaman","hitpoints":150,"combatLevel":150,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":140,"rangeLevel":120,"magicLevel":130,"range":45,"slashDef":40,"crushDef":30,"magicDef":50,"bonusAttack":45,"bonusStrength":38,"bonusRangeStrength":38},"6794":{"name":"Monkey Archer","hitpoints":50,"combatLevel":86,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":110,"magicLevel":1},"6795":{"name":"Pyrelord","hitpoints":80,"combatLevel":60,"slayerLevel":30,"attackSpeed":4,"attackLevel":60,"strengthLevel":40,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":10,"rangeDef":10},"6805":{"name":"Kruk","hitpoints":210,"combatLevel":149,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":150,"rangeLevel":170,"magicLevel":130,"range":150,"stabDef":100,"slashDef":100,"crushDef":100,"rangeDef":100,"magicDef":250,"bonusAttack":160,"bonusStrength":90,"bonusRangeStrength":70},"6813":{"name":"Monkey Archer","hitpoints":50,"combatLevel":86,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":110,"magicLevel":1},"6815":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6818":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6824":{"name":"Giant bat","hitpoints":32,"combatLevel":27,"attackSpeed":4,"attackLevel":22,"strengthLevel":22,"defenceLevel":22,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":12,"rangeDef":8,"magicDef":10},"6896":{"name":"Gangster","hitpoints":40,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"crushDef":10,"bonusAttack":20,"bonusStrength":30},"6897":{"name":"Gangster","hitpoints":40,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"crushDef":10,"bonusAttack":20,"bonusStrength":30},"6898":{"name":"Gangster","hitpoints":50,"combatLevel":50,"attackSpeed":5,"attackLevel":50,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"crushDef":10,"bonusAttack":35,"bonusStrength":15},"6899":{"name":"Gangster","hitpoints":50,"combatLevel":50,"attackSpeed":5,"attackLevel":50,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"crushDef":10,"bonusAttack":35,"bonusStrength":15},"6900":{"name":"Gang boss","hitpoints":80,"combatLevel":83,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":70,"rangeLevel":80,"magicLevel":1,"range":50,"crushDef":10},"6901":{"name":"Gang boss","hitpoints":80,"combatLevel":83,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":70,"rangeLevel":80,"magicLevel":1,"range":50,"crushDef":10},"6902":{"name":"Gang boss","hitpoints":80,"combatLevel":76,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":70,"rangeLevel":80,"magicLevel":1,"range":50,"crushDef":10},"6903":{"name":"Gang boss","hitpoints":80,"combatLevel":76,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":70,"rangeLevel":80,"magicLevel":1,"range":50,"crushDef":10},"6904":{"name":"Soldier (tier 1)","hitpoints":50,"combatLevel":39,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":11,"poisonImmune":true,"venomImmune":true},"6905":{"name":"Soldier (tier 1)","hitpoints":50,"combatLevel":39,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":12,"bonusStrength":11,"poisonImmune":true,"venomImmune":true},"6906":{"name":"Soldier (tier 2)","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"bonusAttack":25,"bonusStrength":30,"poisonImmune":true,"venomImmune":true},"6907":{"name":"Soldier (tier 2)","hitpoints":50,"combatLevel":48,"attackSpeed":4,"attackLevel":40,"strengthLevel":40,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"bonusAttack":25,"bonusStrength":30,"poisonImmune":true,"venomImmune":true},"6908":{"name":"Soldier (tier 3)","hitpoints":55,"combatLevel":58,"attackSpeed":5,"attackLevel":50,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"bonusAttack":20,"bonusStrength":8,"poisonImmune":true,"venomImmune":true},"6909":{"name":"Soldier (tier 3)","hitpoints":55,"combatLevel":58,"attackSpeed":5,"attackLevel":50,"strengthLevel":50,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"bonusAttack":20,"bonusStrength":8,"poisonImmune":true,"venomImmune":true},"6910":{"name":"Soldier (tier 4)","hitpoints":65,"combatLevel":70,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":1,"bonusAttack":10,"bonusStrength":8,"poisonImmune":true,"venomImmune":true},"6911":{"name":"Soldier (tier 4)","hitpoints":65,"combatLevel":70,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"stabDef":1,"slashDef":1,"bonusAttack":10,"bonusStrength":8,"poisonImmune":true,"venomImmune":true},"6912":{"name":"Soldier (tier 5)","hitpoints":90,"combatLevel":99,"attackSpeed":4,"attackLevel":85,"strengthLevel":85,"defenceLevel":86,"rangeLevel":1,"magicLevel":1,"bonusAttack":5,"bonusStrength":5,"poisonImmune":true,"venomImmune":true},"6913":{"name":"Soldier (tier 5)","hitpoints":90,"combatLevel":99,"attackSpeed":4,"attackLevel":85,"strengthLevel":85,"defenceLevel":86,"rangeLevel":1,"magicLevel":1,"bonusAttack":5,"bonusStrength":5,"poisonImmune":true,"venomImmune":true},"6914":{"name":"Lizardman","hitpoints":60,"combatLevel":53,"attackSpeed":4,"attackLevel":43,"strengthLevel":43,"defenceLevel":43,"rangeLevel":43,"magicLevel":1,"range":22,"slashDef":25,"bonusAttack":22,"bonusStrength":20,"bonusRangeStrength":20},"6915":{"name":"Lizardman","hitpoints":60,"combatLevel":53,"attackSpeed":4,"attackLevel":43,"strengthLevel":43,"defenceLevel":43,"rangeLevel":43,"magicLevel":1,"range":22,"slashDef":25,"bonusAttack":22,"bonusStrength":20,"bonusRangeStrength":20},"6916":{"name":"Lizardman","hitpoints":60,"combatLevel":62,"attackSpeed":4,"attackLevel":53,"strengthLevel":53,"defenceLevel":52,"rangeLevel":1,"magicLevel":1,"range":28,"slashDef":20,"crushDef":5,"bonusAttack":28,"bonusStrength":25,"bonusRangeStrength":25},"6917":{"name":"Lizardman","hitpoints":60,"combatLevel":62,"attackSpeed":4,"attackLevel":53,"strengthLevel":53,"defenceLevel":52,"rangeLevel":1,"magicLevel":1,"range":28,"slashDef":20,"crushDef":5,"bonusAttack":28,"bonusStrength":25,"bonusRangeStrength":25},"6918":{"name":"Lizardman brute","hitpoints":60,"combatLevel":73,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"range":34,"slashDef":30,"crushDef":10,"bonusAttack":34,"bonusStrength":30,"bonusRangeStrength":30},"6919":{"name":"Lizardman brute","hitpoints":60,"combatLevel":73,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"range":34,"slashDef":30,"crushDef":10,"bonusAttack":34,"bonusStrength":30,"bonusRangeStrength":30},"6956":{"name":"Monk","hitpoints":15,"combatLevel":5,"attackSpeed":4,"attackLevel":2,"strengthLevel":2,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"6987":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6988":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6989":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6990":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6991":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6992":{"name":"Woman","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"6993":{"name":"Pirate","hitpoints":20,"combatLevel":23,"attackSpeed":5,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"slashDef":1,"bonusAttack":9,"bonusStrength":10},"6994":{"name":"Pirate","hitpoints":20,"combatLevel":23,"attackSpeed":5,"attackLevel":21,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":2,"bonusAttack":8,"bonusStrength":10},"6995":{"name":"Pirate","hitpoints":20,"combatLevel":23,"attackSpeed":5,"attackLevel":21,"strengthLevel":21,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":2,"bonusAttack":8,"bonusStrength":10},"6996":{"name":"Mugger","hitpoints":8,"combatLevel":6,"attackSpeed":4,"attackLevel":5,"strengthLevel":5,"defenceLevel":5,"rangeLevel":1,"magicLevel":1},"7016":{"name":"Kourend guard","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":25,"crushDef":19,"rangeDef":20,"bonusAttack":9,"bonusStrength":7},"7017":{"name":"Kourend head guard","hitpoints":86,"combatLevel":84,"attackSpeed":4,"attackLevel":75,"strengthLevel":73,"defenceLevel":61,"rangeLevel":1,"magicLevel":1,"stabDef":16,"slashDef":16,"crushDef":18,"rangeDef":27,"bonusAttack":17,"bonusStrength":15},"7018":{"name":"Reanimated goblin","hitpoints":5,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"7019":{"name":"Reanimated monkey","hitpoints":5,"attackSpeed":4,"attackLevel":2,"strengthLevel":2,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"7020":{"name":"Reanimated imp","hitpoints":5,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"7021":{"name":"Reanimated minotaur","hitpoints":10,"attackSpeed":4,"attackLevel":12,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"7022":{"name":"Reanimated scorpion","hitpoints":15,"attackSpeed":4,"attackLevel":11,"strengthLevel":12,"defenceLevel":11,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"magicDef":5},"7023":{"name":"Reanimated bear","hitpoints":15,"attackSpeed":4,"attackLevel":17,"strengthLevel":18,"defenceLevel":15,"rangeLevel":1,"magicLevel":1},"7024":{"name":"Reanimated unicorn","hitpoints":15,"attackSpeed":4,"attackLevel":11,"strengthLevel":13,"defenceLevel":13,"rangeLevel":1,"magicLevel":1},"7025":{"name":"Reanimated dog","hitpoints":35,"attackSpeed":4,"attackLevel":53,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1},"7026":{"name":"Reanimated chaos druid","hitpoints":35,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":12,"rangeLevel":1,"magicLevel":10},"7027":{"name":"Reanimated giant","hitpoints":35,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"7028":{"name":"Reanimated ogre","hitpoints":35,"attackSpeed":6,"attackLevel":43,"strengthLevel":43,"defenceLevel":43,"rangeLevel":1,"magicLevel":1,"bonusAttack":22,"bonusStrength":20},"7029":{"name":"Reanimated elf","hitpoints":35,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":20,"crushDef":40,"rangeDef":50,"magicDef":60},"7030":{"name":"Reanimated troll","hitpoints":35,"attackSpeed":4,"attackLevel":40,"strengthLevel":75,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"crushDef":10,"rangeDef":200,"magicDef":200,"bonusAttack":20,"bonusStrength":20},"7031":{"name":"Reanimated horror","hitpoints":35,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":55,"rangeLevel":1,"magicLevel":1},"7032":{"name":"Reanimated kalphite","hitpoints":35,"attackSpeed":4,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":5,"rangeDef":50,"magicDef":50},"7033":{"name":"Reanimated dagannoth","hitpoints":35,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":81,"rangeLevel":50,"magicLevel":1},"7034":{"name":"Reanimated bloodveld","hitpoints":35,"slayerLevel":50,"attackSpeed":4,"attackLevel":75,"strengthLevel":45,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"7035":{"name":"Reanimated TzHaar","hitpoints":35,"attackSpeed":5,"attackLevel":120,"strengthLevel":140,"defenceLevel":120,"rangeLevel":1,"magicLevel":40},"7036":{"name":"Reanimated demon","hitpoints":35,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7037":{"name":"Reanimated aviansie","hitpoints":35,"attackSpeed":5,"attackLevel":71,"strengthLevel":1,"defenceLevel":70,"rangeLevel":1,"magicLevel":1},"7038":{"name":"Reanimated abyssal","hitpoints":35,"slayerLevel":85,"attackSpeed":4,"attackLevel":97,"strengthLevel":67,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20},"7039":{"name":"Reanimated dragon","hitpoints":35,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"7095":{"name":"Tortured gorilla","hitpoints":110,"combatLevel":142,"attackSpeed":4,"attackLevel":180,"strengthLevel":90,"defenceLevel":110,"rangeLevel":1,"magicLevel":110,"bonusAttack":90,"bonusStrength":123},"7096":{"name":"Tortured gorilla","hitpoints":110,"combatLevel":142,"attackSpeed":4,"attackLevel":180,"strengthLevel":90,"defenceLevel":110,"rangeLevel":1,"magicLevel":110,"bonusAttack":90,"bonusStrength":123},"7097":{"name":"Tortured gorilla","hitpoints":110,"combatLevel":142,"attackSpeed":4,"attackLevel":180,"strengthLevel":90,"defenceLevel":110,"rangeLevel":1,"magicLevel":110,"bonusAttack":90,"bonusStrength":123},"7101":{"name":"Glough","hitpoints":575,"combatLevel":378,"attackSpeed":6,"attackLevel":260,"strengthLevel":270,"defenceLevel":248,"rangeLevel":262,"magicLevel":250,"range":100,"magic":80,"bonusAttack":122,"bonusStrength":75,"bonusRangeStrength":40,"bonusMagicDamage":40},"7102":{"name":"Glough","hitpoints":575,"combatLevel":378,"attackSpeed":6,"attackLevel":260,"strengthLevel":270,"defenceLevel":248,"rangeLevel":262,"magicLevel":250,"range":100,"magic":80,"bonusAttack":122,"bonusStrength":75,"bonusRangeStrength":40,"bonusMagicDamage":40},"7103":{"name":"Glough","hitpoints":575,"combatLevel":378,"attackSpeed":6,"attackLevel":260,"strengthLevel":270,"defenceLevel":248,"rangeLevel":262,"magicLevel":250,"range":100,"magic":80,"bonusAttack":122,"bonusStrength":75,"bonusRangeStrength":40,"bonusMagicDamage":40},"7104":{"name":"Keef","hitpoints":180,"combatLevel":178,"attackSpeed":5,"attackLevel":165,"strengthLevel":120,"defenceLevel":165,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"bonusAttack":190,"bonusStrength":170},"7105":{"name":"Keef","hitpoints":180,"combatLevel":178,"attackSpeed":5,"attackLevel":165,"strengthLevel":120,"defenceLevel":165,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"bonusAttack":190,"bonusStrength":170},"7106":{"name":"Kob","hitpoints":200,"combatLevel":185,"attackSpeed":5,"attackLevel":180,"strengthLevel":175,"defenceLevel":80,"stabDef":85,"slashDef":85,"crushDef":90,"rangeDef":200,"magicDef":200,"bonusAttack":200,"bonusStrength":135,"poisonImmune":true,"venomImmune":true},"7107":{"name":"Kob","hitpoints":200,"combatLevel":185,"attackSpeed":5,"attackLevel":180,"strengthLevel":175,"defenceLevel":80,"stabDef":85,"slashDef":85,"crushDef":90,"rangeDef":200,"magicDef":200,"bonusAttack":200,"bonusStrength":135,"poisonImmune":true,"venomImmune":true},"7118":{"name":"Maniacal monkey","hitpoints":65,"combatLevel":140,"attackSpeed":4,"attackLevel":200,"strengthLevel":175,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"7119":{"name":"Maniacal Monkey Archer","hitpoints":60,"combatLevel":132,"attackSpeed":4,"attackLevel":120,"strengthLevel":80,"defenceLevel":80,"rangeLevel":200,"magicLevel":1},"7144":{"name":"Demonic gorilla","hitpoints":380,"combatLevel":275,"attackSpeed":5,"attackLevel":205,"strengthLevel":195,"defenceLevel":200,"rangeLevel":195,"magicLevel":195,"range":43,"magic":40,"magicDef":50,"bonusAttack":43,"bonusStrength":29,"bonusRangeStrength":29},"7145":{"name":"Demonic gorilla","hitpoints":380,"combatLevel":275,"attackSpeed":5,"attackLevel":205,"strengthLevel":195,"defenceLevel":200,"rangeLevel":195,"magicLevel":195,"range":43,"magic":40,"magicDef":50,"bonusAttack":43,"bonusStrength":29,"bonusRangeStrength":29},"7146":{"name":"Demonic gorilla","hitpoints":380,"combatLevel":275,"attackSpeed":5,"attackLevel":205,"strengthLevel":195,"defenceLevel":200,"rangeLevel":195,"magicLevel":195,"range":43,"magic":40,"magicDef":50,"bonusAttack":43,"bonusStrength":29,"bonusRangeStrength":29},"7147":{"name":"Demonic gorilla","hitpoints":380,"combatLevel":275,"attackSpeed":5,"attackLevel":205,"strengthLevel":195,"defenceLevel":200,"rangeLevel":195,"magicLevel":195,"range":43,"magic":40,"magicDef":50,"bonusAttack":43,"bonusStrength":29,"bonusRangeStrength":29},"7148":{"name":"Demonic gorilla","hitpoints":380,"combatLevel":275,"attackSpeed":5,"attackLevel":205,"strengthLevel":195,"defenceLevel":200,"rangeLevel":195,"magicLevel":195,"range":43,"magic":40,"magicDef":50,"bonusAttack":43,"bonusStrength":29,"bonusRangeStrength":29},"7149":{"name":"Demonic gorilla","hitpoints":380,"combatLevel":275,"attackSpeed":5,"attackLevel":205,"strengthLevel":195,"defenceLevel":200,"rangeLevel":195,"magicLevel":195,"range":43,"magic":40,"magicDef":50,"bonusAttack":43,"bonusStrength":29,"bonusRangeStrength":29},"7150":{"name":"Tortured gorilla","hitpoints":210,"combatLevel":141,"attackSpeed":5,"attackLevel":105,"strengthLevel":95,"defenceLevel":95,"rangeLevel":95,"magicLevel":95,"range":22,"magic":20,"magicDef":25,"bonusAttack":22,"bonusStrength":16,"bonusRangeStrength":16,"bonusMagicDamage":16},"7151":{"name":"Tortured gorilla","hitpoints":210,"combatLevel":141,"attackSpeed":5,"attackLevel":105,"strengthLevel":95,"defenceLevel":95,"rangeLevel":95,"magicLevel":95,"range":22,"magic":20,"magicDef":25,"bonusAttack":22,"bonusStrength":16,"bonusRangeStrength":16,"bonusMagicDamage":16},"7152":{"name":"Demonic gorilla","hitpoints":380,"combatLevel":275,"attackSpeed":5,"attackLevel":205,"strengthLevel":195,"defenceLevel":200,"rangeLevel":195,"magicLevel":195,"range":43,"magic":40,"magicDef":50,"bonusAttack":43,"bonusStrength":29,"bonusRangeStrength":29},"7153":{"name":"Tortured gorilla","hitpoints":210,"combatLevel":141,"attackSpeed":5,"attackLevel":105,"strengthLevel":95,"defenceLevel":95,"rangeLevel":95,"magicLevel":95,"range":22,"magic":20,"magicDef":25,"bonusAttack":22,"bonusStrength":16,"bonusRangeStrength":16,"bonusMagicDamage":16},"7206":{"name":"Sand Crab","hitpoints":60,"combatLevel":15,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"7207":{"name":"Sandy rocks","hitpoints":60,"combatLevel":15,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"7209":{"name":"Guard dog","hitpoints":49,"combatLevel":44,"attackSpeed":4,"attackLevel":35,"strengthLevel":36,"defenceLevel":37,"rangeLevel":1,"magicLevel":1},"7234":{"name":"Ent","hitpoints":75,"combatLevel":86,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":30,"magicDef":40,"bonusAttack":35,"bonusStrength":65},"7238":{"name":"Forester","hitpoints":17,"combatLevel":15,"attackSpeed":4,"attackLevel":14,"strengthLevel":13,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"stabDef":4,"slashDef":3,"crushDef":5},"7241":{"name":"Abyssal demon","hitpoints":150,"combatLevel":124,"slayerLevel":85,"attackSpeed":4,"attackLevel":97,"strengthLevel":67,"defenceLevel":135,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20},"7242":{"name":"Black demon","hitpoints":170,"combatLevel":184,"attackSpeed":4,"attackLevel":155,"strengthLevel":158,"defenceLevel":162,"rangeLevel":1,"magicLevel":1},"7243":{"name":"Black demon","hitpoints":160,"combatLevel":178,"attackSpeed":4,"attackLevel":145,"strengthLevel":148,"defenceLevel":175,"rangeLevel":1,"magicLevel":1},"7244":{"name":"Greater demon","hitpoints":120,"combatLevel":101,"attackSpeed":4,"attackLevel":90,"strengthLevel":90,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"7245":{"name":"Greater demon","hitpoints":115,"combatLevel":100,"attackSpeed":4,"attackLevel":90,"strengthLevel":70,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"7246":{"name":"Greater demon","hitpoints":130,"combatLevel":113,"attackSpeed":4,"attackLevel":120,"strengthLevel":90,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"7247":{"name":"Lesser demon","hitpoints":87,"combatLevel":87,"attackSpeed":4,"attackLevel":80,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7248":{"name":"Lesser demon","hitpoints":98,"combatLevel":94,"attackSpeed":4,"attackLevel":80,"strengthLevel":70,"defenceLevel":85,"rangeLevel":1,"magicLevel":1},"7249":{"name":"Dust devil","hitpoints":130,"combatLevel":110,"slayerLevel":65,"attackSpeed":4,"attackLevel":120,"strengthLevel":90,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"7250":{"name":"Dark beast","hitpoints":220,"combatLevel":182,"slayerLevel":90,"attackSpeed":4,"attackLevel":140,"strengthLevel":160,"defenceLevel":120,"rangeLevel":1,"magicLevel":160,"stabDef":30,"slashDef":40,"crushDef":100,"rangeDef":100,"magicDef":90},"7251":{"name":"Fire giant","hitpoints":150,"combatLevel":109,"attackSpeed":5,"attackLevel":90,"strengthLevel":80,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"7252":{"name":"Fire giant","hitpoints":130,"combatLevel":104,"attackSpeed":5,"attackLevel":65,"strengthLevel":65,"defenceLevel":120,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":10,"crushDef":10,"magicDef":50,"bonusAttack":50,"bonusStrength":20},"7253":{"name":"Bronze dragon","hitpoints":122,"combatLevel":143,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":112,"rangeLevel":1,"magicLevel":130,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"7254":{"name":"Iron dragon","hitpoints":195,"combatLevel":215,"attackSpeed":4,"attackLevel":185,"strengthLevel":185,"defenceLevel":185,"rangeLevel":1,"magicLevel":120,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"7255":{"name":"Steel dragon","hitpoints":250,"combatLevel":274,"attackSpeed":4,"attackLevel":235,"strengthLevel":235,"defenceLevel":235,"rangeLevel":1,"magicLevel":130,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"7256":{"name":"Hellhound","hitpoints":116,"combatLevel":122,"attackSpeed":4,"attackLevel":105,"strengthLevel":104,"defenceLevel":102,"rangeLevel":1,"magicLevel":1},"7257":{"name":"Ankou","hitpoints":60,"combatLevel":95,"attackSpeed":4,"attackLevel":100,"strengthLevel":70,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"7258":{"name":"Shade","hitpoints":115,"combatLevel":140,"attackSpeed":6,"attackLevel":130,"strengthLevel":136,"defenceLevel":100,"rangeLevel":50,"magicLevel":50},"7259":{"name":"Dagannoth","hitpoints":70,"combatLevel":74,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":50,"rangeLevel":1,"magicLevel":1},"7260":{"name":"Dagannoth","hitpoints":120,"combatLevel":92,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7261":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"7262":{"name":"Moss giant","hitpoints":60,"combatLevel":42,"attackSpeed":6,"attackLevel":30,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"bonusAttack":33,"bonusStrength":31},"7263":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"7264":{"name":"Ghost","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":13,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"crushDef":5,"rangeDef":5},"7265":{"name":"Skeleton","hitpoints":29,"combatLevel":22,"attackSpeed":4,"attackLevel":15,"strengthLevel":18,"defenceLevel":17,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":5,"rangeDef":5},"7266":{"name":"King Sand Crab","hitpoints":200,"combatLevel":107,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"7267":{"name":"Sandy Boulder","hitpoints":200,"combatLevel":107,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"7268":{"name":"Possessed pickaxe","hitpoints":40,"combatLevel":50,"attackSpeed":4,"attackLevel":40,"strengthLevel":55,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"7269":{"name":"Magic axe","hitpoints":44,"combatLevel":42,"attackSpeed":4,"attackLevel":38,"strengthLevel":38,"defenceLevel":29,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":5,"crushDef":15,"rangeDef":10,"magicDef":5},"7270":{"name":"Cyclops","hitpoints":100,"combatLevel":76,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"7271":{"name":"Cyclops","hitpoints":75,"combatLevel":56,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":35,"rangeLevel":1,"magicLevel":1},"7272":{"name":"Twisted Banshee","hitpoints":100,"combatLevel":89,"slayerLevel":15,"attackSpeed":4,"attackLevel":75,"strengthLevel":85,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":15,"crushDef":15,"rangeDef":15},"7273":{"name":"Brutal blue dragon","hitpoints":245,"combatLevel":271,"attackSpeed":4,"attackLevel":298,"strengthLevel":198,"defenceLevel":198,"magicLevel":198,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60,"poisonImmune":true,"venomImmune":true},"7274":{"name":"Brutal red dragon","hitpoints":285,"combatLevel":289,"attackSpeed":4,"attackLevel":310,"strengthLevel":210,"defenceLevel":198,"magicLevel":250,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60,"poisonImmune":true,"venomImmune":true},"7275":{"name":"Brutal black dragon","hitpoints":315,"combatLevel":318,"slayerLevel":77,"attackSpeed":4,"attackLevel":330,"strengthLevel":210,"defenceLevel":258,"magicLevel":250,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60,"bonusStrength":20,"poisonImmune":true,"venomImmune":true},"7276":{"name":"Mutated Bloodveld","hitpoints":170,"combatLevel":123,"slayerLevel":50,"attackSpeed":4,"attackLevel":110,"strengthLevel":115,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"7277":{"name":"Warped Jelly","hitpoints":140,"combatLevel":112,"slayerLevel":52,"attackSpeed":4,"attackLevel":95,"strengthLevel":90,"defenceLevel":70,"rangeLevel":1,"magicLevel":95},"7278":{"name":"Greater Nechryael","hitpoints":205,"combatLevel":200,"slayerLevel":80,"attackSpeed":4,"attackLevel":197,"strengthLevel":197,"defenceLevel":85,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50},"7279":{"name":"Deviant spectre","hitpoints":190,"combatLevel":169,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":90,"rangeLevel":1,"magicLevel":205,"stabDef":80,"slashDef":80,"crushDef":80,"rangeDef":85},"7286":{"name":"Skotizo","hitpoints":450,"combatLevel":321,"attackSpeed":6,"attackLevel":240,"strengthLevel":250,"defenceLevel":200,"rangeLevel":1,"magicLevel":280,"stabDef":80,"slashDef":80,"crushDef":80,"rangeDef":130,"magicDef":130,"bonusAttack":160,"bonusStrength":31,"poisonImmune":true,"venomImmune":true},"7287":{"name":"Reanimated demon spawn","hitpoints":85,"combatLevel":87,"attackSpeed":4,"attackLevel":80,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7288":{"name":"Awakened Altar","hitpoints":100},"7290":{"name":"Awakened Altar","hitpoints":100},"7292":{"name":"Awakened Altar","hitpoints":100},"7294":{"name":"Awakened Altar","hitpoints":100},"7296":{"name":"Dark Ankou","hitpoints":60,"combatLevel":95,"attackSpeed":4,"attackLevel":100,"strengthLevel":70,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"7307":{"name":"Ancient Wizard","hitpoints":80,"combatLevel":98,"attackSpeed":4,"attackLevel":50,"strengthLevel":30,"defenceLevel":20,"rangeLevel":1,"magicLevel":150},"7308":{"name":"Ancient Wizard","hitpoints":80,"combatLevel":98,"attackSpeed":4,"attackLevel":50,"strengthLevel":30,"defenceLevel":20,"rangeLevel":150,"magicLevel":80,"bonusRangeStrength":30},"7309":{"name":"Ancient Wizard","hitpoints":80,"combatLevel":112,"attackSpeed":4,"attackLevel":120,"strengthLevel":150,"defenceLevel":20,"rangeLevel":1,"magicLevel":80,"bonusAttack":80,"bonusStrength":80},"7310":{"name":"Brassican Mage","hitpoints":150,"combatLevel":140,"attackSpeed":3,"attackLevel":80,"strengthLevel":70,"defenceLevel":80,"rangeLevel":140,"magicLevel":170},"7312":{"name":"Double agent","hitpoints":160,"combatLevel":141,"attackSpeed":4,"attackLevel":130,"strengthLevel":105,"defenceLevel":100,"rangeLevel":1,"magicLevel":95,"stabDef":20,"slashDef":20,"crushDef":20,"bonusAttack":40,"bonusStrength":40},"7322":{"name":"Wild dog","hitpoints":62,"combatLevel":63,"attackSpeed":4,"attackLevel":53,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1},"7388":{"name":"Crushing hand","hitpoints":55,"combatLevel":45,"slayerLevel":5,"attackSpeed":4,"attackLevel":45,"strengthLevel":42,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"poisonImmune":true},"7389":{"name":"Chasm Crawler","hitpoints":64,"combatLevel":68,"slayerLevel":10,"attackSpeed":4,"attackLevel":22,"strengthLevel":18,"defenceLevel":18,"rangeLevel":1,"magicLevel":1,"stabDef":10,"slashDef":10,"crushDef":5,"rangeDef":10,"magicDef":5},"7390":{"name":"Screaming banshee","hitpoints":61,"combatLevel":70,"slayerLevel":15,"attackSpeed":4,"attackLevel":65,"strengthLevel":61,"defenceLevel":56,"rangeLevel":1,"magicLevel":1,"stabDef":13,"slashDef":13,"crushDef":13,"rangeDef":13,"poisonImmune":true,"venomImmune":true},"7391":{"name":"Screaming twisted banshee","hitpoints":220,"combatLevel":144,"slayerLevel":15,"attackSpeed":4,"attackLevel":103,"strengthLevel":110,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":25,"rangeDef":25,"poisonImmune":true,"venomImmune":true},"7392":{"name":"Giant rockslug","hitpoints":77,"combatLevel":86,"slayerLevel":20,"attackSpeed":6,"attackLevel":72,"strengthLevel":77,"defenceLevel":77,"rangeLevel":1,"magicLevel":1},"7393":{"name":"Cockathrice","hitpoints":95,"combatLevel":89,"slayerLevel":25,"attackSpeed":4,"attackLevel":63,"strengthLevel":78,"defenceLevel":78,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"magicDef":20,"poisonImmune":true,"venomImmune":true},"7394":{"name":"Flaming pyrelord","hitpoints":126,"combatLevel":97,"slayerLevel":30,"attackSpeed":4,"attackLevel":98,"strengthLevel":65,"defenceLevel":52,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":18,"crushDef":18,"rangeDef":18,"poisonImmune":true,"venomImmune":true},"7395":{"name":"Monstrous basilisk","hitpoints":170,"combatLevel":135,"slayerLevel":40,"attackSpeed":4,"attackLevel":88,"strengthLevel":98,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":35,"magicDef":35,"poisonImmune":true,"venomImmune":true},"7396":{"name":"Malevolent Mage","hitpoints":175,"combatLevel":162,"slayerLevel":45,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":135,"rangeLevel":1,"magicLevel":175,"poisonImmune":true,"venomImmune":true},"7397":{"name":"Insatiable Bloodveld","hitpoints":380,"combatLevel":202,"slayerLevel":50,"attackSpeed":4,"attackLevel":190,"strengthLevel":145,"defenceLevel":85,"rangeLevel":1,"magicLevel":1,"venomImmune":true},"7398":{"name":"Insatiable mutated Bloodveld","hitpoints":410,"combatLevel":278,"slayerLevel":50,"attackSpeed":4,"attackLevel":250,"strengthLevel":190,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"venomImmune":true},"7399":{"name":"Vitreous Jelly","hitpoints":190,"combatLevel":206,"slayerLevel":52,"attackSpeed":4,"attackLevel":170,"strengthLevel":150,"defenceLevel":220,"rangeLevel":1,"magicLevel":150},"7400":{"name":"Vitreous warped Jelly","hitpoints":220,"combatLevel":241,"slayerLevel":52,"attackSpeed":4,"attackLevel":200,"strengthLevel":180,"defenceLevel":250,"rangeLevel":1,"magicLevel":180},"7401":{"name":"Cave abomination","hitpoints":130,"combatLevel":206,"slayerLevel":58,"attackLevel":230,"strengthLevel":195,"defenceLevel":142,"rangeLevel":1,"magicLevel":230},"7402":{"name":"Abhorrent spectre","hitpoints":250,"combatLevel":253,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":180,"rangeLevel":1,"magicLevel":300,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":30,"venomImmune":true},"7403":{"name":"Repugnant spectre","hitpoints":390,"combatLevel":335,"slayerLevel":60,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":220,"rangeLevel":1,"magicLevel":380,"stabDef":120,"slashDef":120,"crushDef":120,"rangeDef":115,"venomImmune":true},"7404":{"name":"Choke devil","hitpoints":300,"combatLevel":264,"slayerLevel":65,"attackSpeed":4,"attackLevel":260,"strengthLevel":230,"defenceLevel":120,"rangeLevel":1,"magicLevel":1},"7405":{"name":"King kurask","hitpoints":420,"combatLevel":295,"slayerLevel":70,"attackSpeed":4,"attackLevel":190,"strengthLevel":320,"defenceLevel":250,"rangeLevel":1,"magicLevel":1,"slashDef":50,"crushDef":50,"poisonImmune":true,"venomImmune":true},"7406":{"name":"Nuclear smoke devil","hitpoints":240,"combatLevel":280,"slayerLevel":93,"attackSpeed":4,"attackLevel":240,"strengthLevel":230,"defenceLevel":390,"rangeLevel":280,"magicLevel":1,"rangeDef":80,"magicDef":850,"poisonImmune":true,"venomImmune":true},"7407":{"name":"Marble gargoyle","hitpoints":270,"combatLevel":349,"slayerLevel":75,"attackSpeed":4,"attackLevel":230,"strengthLevel":250,"defenceLevel":190,"rangeLevel":220,"magicLevel":1,"stabDef":50,"slashDef":50,"rangeDef":50,"magicDef":50,"bonusRangeStrength":20,"venomImmune":true},"7408":{"name":"Marble gargoyle","hitpoints":270,"combatLevel":349,"slayerLevel":75,"attackSpeed":4,"attackLevel":230,"strengthLevel":250,"defenceLevel":190,"rangeLevel":220,"magicLevel":1,"stabDef":50,"slashDef":50,"rangeDef":50,"magicDef":50,"bonusRangeStrength":20,"venomImmune":true},"7409":{"name":"Night beast","hitpoints":550,"combatLevel":374,"slayerLevel":90,"attackSpeed":4,"attackLevel":270,"strengthLevel":290,"defenceLevel":220,"rangeLevel":1,"magicLevel":300,"stabDef":75,"slashDef":80,"crushDef":200,"rangeDef":200,"magicDef":190,"venomImmune":true},"7410":{"name":"Greater abyssal demon","hitpoints":400,"combatLevel":342,"slayerLevel":85,"attackSpeed":4,"attackLevel":300,"strengthLevel":260,"defenceLevel":240,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"venomImmune":true},"7411":{"name":"Nechryarch","hitpoints":320,"combatLevel":300,"slayerLevel":80,"attackSpeed":4,"attackLevel":310,"strengthLevel":260,"defenceLevel":140,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":30,"crushDef":30,"rangeDef":30},"7416":{"name":"Obor","hitpoints":120,"combatLevel":106,"attackSpeed":6,"attackLevel":90,"strengthLevel":100,"defenceLevel":60,"rangeLevel":120,"magicLevel":1,"range":100,"stabDef":35,"slashDef":40,"crushDef":45,"rangeDef":20,"magicDef":20,"bonusAttack":100,"bonusStrength":68,"bonusRangeStrength":65},"7418":{"name":"Zamorak warrior","hitpoints":50,"combatLevel":84,"attackSpeed":4,"attackLevel":85,"strengthLevel":70,"defenceLevel":90,"rangeLevel":1,"magicLevel":1},"7419":{"name":"Zamorak warrior","hitpoints":50,"combatLevel":85,"attackSpeed":4,"attackLevel":90,"strengthLevel":70,"defenceLevel":90,"rangeLevel":1,"magicLevel":1},"7420":{"name":"Zamorak ranger","hitpoints":50,"combatLevel":81,"attackSpeed":4,"attackLevel":75,"strengthLevel":75,"defenceLevel":80,"rangeLevel":80,"magicLevel":1},"7421":{"name":"Zamorak ranger","hitpoints":50,"combatLevel":82,"attackSpeed":4,"attackLevel":75,"strengthLevel":80,"defenceLevel":80,"rangeLevel":80,"magicLevel":1},"7422":{"name":"Zamorak mage","hitpoints":50,"combatLevel":84,"attackSpeed":4,"attackLevel":78,"strengthLevel":81,"defenceLevel":82,"rangeLevel":1,"magicLevel":85},"7423":{"name":"Zamorak mage","hitpoints":50,"combatLevel":82,"attackSpeed":4,"attackLevel":75,"strengthLevel":80,"defenceLevel":80,"rangeLevel":1,"magicLevel":85},"7424":{"name":"Cave lizard","hitpoints":20,"combatLevel":37,"attackSpeed":4,"attackLevel":38,"strengthLevel":36,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"7426":{"name":"Zamorak crafter","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":15,"strengthLevel":15,"defenceLevel":12,"rangeLevel":1,"magicLevel":20},"7427":{"name":"Zamorak crafter","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":15,"strengthLevel":15,"defenceLevel":12,"rangeLevel":1,"magicLevel":20},"7485":{"name":"Zombie","hitpoints":65,"combatLevel":70,"attackSpeed":4,"attackLevel":61,"strengthLevel":60,"defenceLevel":61,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"7486":{"name":"Zombie","hitpoints":50,"combatLevel":56,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":47,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"7487":{"name":"Zombie","hitpoints":71,"combatLevel":76,"attackSpeed":4,"attackLevel":69,"strengthLevel":65,"defenceLevel":62,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"7488":{"name":"Zombie","hitpoints":68,"combatLevel":72,"attackSpeed":4,"attackLevel":61,"strengthLevel":63,"defenceLevel":62,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"rangeDef":10,"bonusStrength":10},"7513":{"name":"Derwen","hitpoints":320,"combatLevel":235,"attackSpeed":6,"attackLevel":250,"strengthLevel":150,"defenceLevel":100,"rangeLevel":1,"magicLevel":80,"magic":180,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":200,"bonusMagicDamage":80,"poisonImmune":true,"venomImmune":true},"7515":{"name":"Porazdir","hitpoints":320,"combatLevel":235,"attackSpeed":6,"attackLevel":250,"strengthLevel":150,"defenceLevel":100,"rangeLevel":1,"magicLevel":180,"magic":80,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":200,"bonusMagicDamage":80,"poisonImmune":true,"venomImmune":true},"7527":{"name":"Vanguard","hitpoints":280,"attackSpeed":4,"attackLevel":225,"strengthLevel":225,"defenceLevel":240,"rangeLevel":225,"magicLevel":225,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":400,"magicDef":20,"bonusAttack":20,"bonusStrength":10},"7528":{"name":"Vanguard","hitpoints":280,"attackSpeed":4,"attackLevel":225,"strengthLevel":225,"defenceLevel":240,"rangeLevel":225,"magicLevel":225,"range":40,"stabDef":55,"slashDef":60,"crushDef":100,"rangeDef":300,"magicDef":400,"bonusRangeStrength":25},"7529":{"name":"Vanguard","hitpoints":280,"attackSpeed":4,"attackLevel":225,"strengthLevel":225,"defenceLevel":240,"rangeLevel":225,"magicLevel":225,"magic":40,"stabDef":315,"slashDef":340,"crushDef":400,"rangeDef":50,"magicDef":110,"bonusMagicDamage":25},"7530":{"name":"Vespula","hitpoints":200,"attackSpeed":3,"rangeDef":60,"magicDef":70},"7531":{"name":"Vespula","hitpoints":200,"attackSpeed":3,"rangeDef":60,"magicDef":70},"7532":{"name":"Vespula","hitpoints":200,"attackSpeed":3,"rangeDef":60,"magicDef":70},"7533":{"name":"Abyssal portal","hitpoints":250,"attackSpeed":2,"attackLevel":1,"strengthLevel":1,"rangeLevel":1,"rangeDef":140,"magicDef":60},"7538":{"name":"Vespine soldier","hitpoints":100,"attackSpeed":4,"rangeLevel":1,"magicDef":30},"7539":{"name":"Vespine soldier","hitpoints":100,"attackSpeed":4,"rangeLevel":1,"magicDef":30},"7540":{"name":"Tekton","hitpoints":300,"attackSpeed":3,"attackLevel":390,"strengthLevel":390,"defenceLevel":205,"rangeLevel":1,"magicLevel":205,"stabDef":155,"slashDef":165,"crushDef":105,"bonusAttack":64,"bonusStrength":20},"7541":{"name":"Tekton","hitpoints":300,"attackSpeed":3,"attackLevel":390,"strengthLevel":390,"defenceLevel":205,"rangeLevel":1,"magicLevel":205,"stabDef":155,"slashDef":165,"crushDef":105,"bonusAttack":64,"bonusStrength":20},"7542":{"name":"Tekton","hitpoints":300,"attackSpeed":3,"attackLevel":390,"strengthLevel":390,"defenceLevel":205,"rangeLevel":1,"magicLevel":205,"stabDef":155,"slashDef":165,"crushDef":105,"bonusAttack":64,"bonusStrength":20},"7543":{"name":"Tekton (enraged)","hitpoints":300,"attackSpeed":3,"attackLevel":390,"strengthLevel":390,"defenceLevel":205,"rangeLevel":1,"magicLevel":205,"stabDef":280,"slashDef":290,"crushDef":180,"bonusAttack":64,"bonusStrength":30},"7544":{"name":"Tekton (enraged)","hitpoints":420,"attackSpeed":3,"attackLevel":585,"strengthLevel":585,"defenceLevel":246,"rangeLevel":1,"magicLevel":246,"stabDef":280,"slashDef":290,"crushDef":180,"bonusAttack":64,"bonusStrength":30},"7545":{"name":"Tekton","hitpoints":420,"attackSpeed":3,"attackLevel":585,"strengthLevel":585,"defenceLevel":246,"rangeLevel":1,"magicLevel":246,"stabDef":155,"slashDef":165,"crushDef":105,"bonusAttack":64,"bonusStrength":20},"7548":{"name":"Scavenger beast","hitpoints":30,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":45,"rangeLevel":1,"magicLevel":1},"7549":{"name":"Scavenger beast","hitpoints":45,"attackSpeed":4,"attackLevel":180,"strengthLevel":180,"defenceLevel":67,"rangeLevel":1,"magicLevel":1},"7550":{"name":"Great Olm (Right claw)","hitpoints":600,"combatLevel":549,"attackSpeed":4,"attackLevel":250,"strengthLevel":250,"defenceLevel":175,"rangeLevel":250,"magicLevel":87,"range":60,"magic":60,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":200,"magicDef":50},"7551":{"name":"Great Olm","hitpoints":800,"combatLevel":1043,"attackSpeed":4,"attackLevel":250,"strengthLevel":250,"defenceLevel":150,"rangeLevel":250,"magicLevel":250,"range":60,"magic":60,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":50,"magicDef":200},"7552":{"name":"Great Olm (Left claw)","hitpoints":600,"combatLevel":750,"attackSpeed":4,"attackLevel":250,"strengthLevel":250,"defenceLevel":175,"rangeLevel":250,"magicLevel":175,"range":60,"magic":60,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50},"7553":{"name":"Great Olm (Right claw)","hitpoints":600,"combatLevel":549,"attackSpeed":4,"attackLevel":375,"strengthLevel":375,"defenceLevel":262,"rangeLevel":375,"magicLevel":130,"range":60,"magic":60,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":200,"magicDef":50},"7554":{"name":"Great Olm","hitpoints":800,"combatLevel":1043,"attackSpeed":4,"attackLevel":375,"strengthLevel":375,"defenceLevel":225,"rangeLevel":375,"magicLevel":375,"range":60,"magic":60,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":50,"magicDef":200},"7555":{"name":"Great Olm (Left claw)","hitpoints":600,"combatLevel":750,"attackSpeed":4,"attackLevel":375,"strengthLevel":375,"defenceLevel":262,"rangeLevel":375,"magicLevel":262,"range":60,"magic":60,"stabDef":50,"slashDef":50,"crushDef":50,"rangeDef":50,"magicDef":50},"7559":{"name":"Deathly ranger","hitpoints":120,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"range":120,"bonusRangeStrength":70},"7560":{"name":"Deathly mage","hitpoints":120,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"rangeLevel":1,"magic":120},"7561":{"name":"Muttadile","hitpoints":250,"attackSpeed":4},"7562":{"name":"Muttadile","hitpoints":250,"attackSpeed":4},"7563":{"name":"Muttadile","hitpoints":250,"attackSpeed":4},"7566":{"name":"Vasa Nistirio","hitpoints":300,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"range":100,"stabDef":170,"slashDef":190,"crushDef":50,"rangeDef":60,"magicDef":400},"7567":{"name":"Vasa Nistirio","hitpoints":300,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"range":100,"stabDef":170,"slashDef":190,"crushDef":50,"rangeDef":60,"magicDef":400},"7568":{"name":"Glowing crystal","hitpoints":120,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"rangeLevel":1,"slashDef":180,"crushDef":180},"7569":{"name":"Guardian","hitpoints":250,"attackSpeed":4,"attackLevel":140,"strengthLevel":140,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":80,"slashDef":180,"bonusStrength":20},"7570":{"name":"Guardian","hitpoints":375,"attackSpeed":4,"attackLevel":210,"strengthLevel":210,"defenceLevel":150,"rangeLevel":1,"magicLevel":1,"stabDef":80,"slashDef":180,"bonusStrength":20},"7571":{"name":"Guardian","hitpoints":250,"attackSpeed":4,"attackLevel":140,"strengthLevel":140,"defenceLevel":100,"rangeLevel":1,"magicLevel":1,"stabDef":80,"slashDef":180,"bonusStrength":20},"7572":{"name":"Guardian","hitpoints":375,"attackSpeed":4,"attackLevel":210,"strengthLevel":210,"defenceLevel":150,"rangeLevel":1,"magicLevel":1,"stabDef":80,"slashDef":180,"bonusStrength":20},"7573":{"name":"Lizardman shaman","hitpoints":190,"attackSpeed":4,"range":56,"stabDef":102,"slashDef":160,"crushDef":150,"magicDef":160,"bonusAttack":58,"bonusStrength":52,"bonusRangeStrength":49},"7574":{"name":"Lizardman shaman","hitpoints":190,"attackSpeed":4,"range":56,"stabDef":102,"slashDef":160,"crushDef":150,"magicDef":160,"bonusAttack":58,"bonusStrength":52,"bonusRangeStrength":49},"7584":{"name":"Ice demon","hitpoints":140,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":160,"rangeLevel":390,"magicLevel":390,"stabDef":70,"slashDef":70,"crushDef":110,"rangeDef":140,"magicDef":60},"7585":{"name":"Ice demon","hitpoints":210,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":240,"rangeLevel":585,"magicLevel":585,"stabDef":70,"slashDef":70,"crushDef":110,"rangeDef":140,"magicDef":60},"7604":{"name":"Skeletal Mystic","hitpoints":160,"attackSpeed":4,"rangeLevel":1,"magic":40,"stabDef":155,"slashDef":155,"crushDef":115,"rangeDef":115,"magicDef":140,"bonusAttack":85,"bonusStrength":50,"bonusMagicDamage":38},"7605":{"name":"Skeletal Mystic","hitpoints":160,"attackSpeed":4,"rangeLevel":1,"magic":40,"stabDef":155,"slashDef":155,"crushDef":115,"rangeDef":115,"magicDef":140,"bonusAttack":85,"bonusStrength":50,"bonusMagicDamage":38},"7606":{"name":"Skeletal Mystic","hitpoints":160,"attackSpeed":4,"rangeLevel":1,"magic":40,"stabDef":155,"slashDef":155,"crushDef":115,"rangeDef":115,"magicDef":140,"bonusAttack":85,"bonusStrength":50,"bonusMagicDamage":38},"7620":{"name":"Temple guardian","hitpoints":45,"combatLevel":30,"attackSpeed":4,"attackLevel":20,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"7621":{"name":"Khazard warlord","hitpoints":170,"combatLevel":112,"attackSpeed":4,"attackLevel":75,"strengthLevel":78,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"7622":{"name":"Khazard warlord","hitpoints":170,"combatLevel":112,"attackSpeed":4,"attackLevel":75,"strengthLevel":78,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"7649":{"name":"Chaotic death spawn","hitpoints":50,"combatLevel":215,"attackSpeed":3,"attackLevel":380,"strengthLevel":1,"defenceLevel":70,"rangeLevel":380,"magicLevel":380},"7656":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7657":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7658":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"7659":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"7660":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"7661":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"7662":{"name":"Mummy","hitpoints":90,"combatLevel":84,"attackSpeed":4,"attackLevel":90,"strengthLevel":30,"defenceLevel":90,"rangeLevel":1,"magicLevel":1,"stabDef":90,"slashDef":90,"crushDef":30,"magicDef":90,"bonusStrength":30},"7664":{"name":"Lesser demon","hitpoints":81,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7679":{"name":"TzHaar-Ket","hitpoints":200,"combatLevel":221,"attackSpeed":4,"attackLevel":200,"strengthLevel":180,"defenceLevel":190,"rangeLevel":1,"magicLevel":40},"7682":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"7683":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"7684":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"7685":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"7686":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"7687":{"name":"TzHaar-Hur","hitpoints":80,"combatLevel":74,"attackSpeed":5,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":80},"7691":{"name":"Jal-Nib","hitpoints":10,"combatLevel":32,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":15,"rangeLevel":1,"magicLevel":15},"7692":{"name":"Jal-MejRah","hitpoints":25,"combatLevel":85,"attackSpeed":3,"defenceLevel":55,"rangeLevel":120,"magicLevel":120,"range":30,"stabDef":30,"slashDef":30,"crushDef":30,"rangeDef":45,"bonusRangeStrength":30},"7693":{"name":"Jal-Ak","hitpoints":40,"combatLevel":165,"attackSpeed":6,"attackLevel":160,"strengthLevel":160,"defenceLevel":95,"rangeLevel":160,"magicLevel":160,"range":45,"magic":45,"stabDef":25,"slashDef":25,"crushDef":25,"rangeDef":25,"magicDef":25,"bonusStrength":45,"bonusRangeStrength":45,"bonusMagicDamage":45},"7694":{"name":"Jal-AkRek-Mej","hitpoints":15,"combatLevel":70,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":95,"rangeLevel":1,"magicLevel":120,"magic":25,"magicDef":25,"bonusMagicDamage":25},"7695":{"name":"Jal-AkRek-Xil","hitpoints":15,"combatLevel":70,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":95,"rangeLevel":120,"magicLevel":1,"range":25,"rangeDef":25,"bonusRangeStrength":25},"7696":{"name":"Jal-AkRek-Ket","hitpoints":15,"combatLevel":70,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":25,"crushDef":25,"bonusStrength":25},"7697":{"name":"Jal-ImKot","hitpoints":75,"combatLevel":240,"attackSpeed":4,"attackLevel":210,"strengthLevel":290,"defenceLevel":120,"rangeLevel":220,"magicLevel":120,"stabDef":65,"slashDef":65,"crushDef":65,"rangeDef":50,"magicDef":30,"bonusStrength":40},"7698":{"name":"Jal-Xil","hitpoints":130,"combatLevel":370,"attackSpeed":4,"attackLevel":140,"strengthLevel":180,"defenceLevel":60,"rangeLevel":250,"magicLevel":90,"range":40,"bonusRangeStrength":50},"7699":{"name":"Jal-Zek","hitpoints":220,"combatLevel":490,"attackSpeed":4,"attackLevel":370,"strengthLevel":510,"defenceLevel":260,"rangeLevel":510,"magicLevel":300,"magic":80},"7700":{"name":"JalTok-Jad","hitpoints":350,"combatLevel":900,"attackSpeed":8,"attackLevel":750,"strengthLevel":1020,"defenceLevel":480,"rangeLevel":1020,"magicLevel":510,"range":80,"magic":100,"bonusMagicDamage":75},"7701":{"name":"Yt-HurKot","hitpoints":90,"combatLevel":141,"attackSpeed":4,"attackLevel":165,"strengthLevel":125,"defenceLevel":100,"rangeLevel":150,"magicLevel":150,"range":80,"magic":100,"rangeDef":130,"magicDef":130},"7702":{"name":"Jal-Xil","hitpoints":130,"combatLevel":370,"attackSpeed":4,"attackLevel":140,"strengthLevel":180,"defenceLevel":60,"rangeLevel":250,"magicLevel":90,"range":40,"bonusRangeStrength":50},"7703":{"name":"Jal-Zek","hitpoints":220,"combatLevel":490,"attackSpeed":4,"attackLevel":370,"strengthLevel":510,"defenceLevel":260,"rangeLevel":510,"magicLevel":300,"magic":80},"7704":{"name":"JalTok-Jad","hitpoints":350,"combatLevel":900,"attackSpeed":8,"attackLevel":750,"strengthLevel":1020,"defenceLevel":480,"rangeLevel":1020,"magicLevel":510,"range":80,"magic":100,"bonusMagicDamage":75},"7705":{"name":"Yt-HurKot","hitpoints":90,"combatLevel":141,"attackSpeed":4,"attackLevel":165,"strengthLevel":125,"defenceLevel":100,"rangeLevel":150,"magicLevel":150,"range":80,"magic":100,"rangeDef":130,"magicDef":130},"7706":{"name":"TzKal-Zuk","hitpoints":1200,"combatLevel":1400,"attackLevel":350,"strengthLevel":600,"defenceLevel":260,"rangeLevel":400,"magicLevel":150,"range":550,"magic":550,"rangeDef":100,"magicDef":350,"bonusStrength":200,"bonusRangeStrength":200,"bonusMagicDamage":450},"7708":{"name":"Jal-MejJak","hitpoints":80,"combatLevel":250,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":1,"magicLevel":1},"7744":{"name":"Lizardman shaman","hitpoints":150,"combatLevel":150,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":140,"rangeLevel":120,"magicLevel":130,"range":45,"slashDef":40,"crushDef":30,"magicDef":50,"bonusAttack":45,"bonusStrength":38,"bonusRangeStrength":38},"7745":{"name":"Lizardman shaman","hitpoints":150,"combatLevel":150,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":140,"rangeLevel":120,"magicLevel":130,"range":45,"slashDef":40,"crushDef":30,"magicDef":50,"bonusAttack":45,"bonusStrength":38,"bonusRangeStrength":38},"7792":{"name":"Long-tailed Wyvern","hitpoints":200,"combatLevel":152,"slayerLevel":66,"attackSpeed":6,"attackLevel":125,"strengthLevel":120,"defenceLevel":90,"rangeLevel":90,"magicLevel":90,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":120,"magicDef":140},"7793":{"name":"Taloned Wyvern","hitpoints":200,"combatLevel":147,"slayerLevel":66,"attackSpeed":6,"attackLevel":120,"strengthLevel":110,"defenceLevel":90,"rangeLevel":90,"magicLevel":90,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":120,"magicDef":140},"7794":{"name":"Spitting Wyvern","hitpoints":200,"combatLevel":139,"slayerLevel":66,"attackSpeed":6,"attackLevel":125,"strengthLevel":80,"defenceLevel":90,"rangeLevel":125,"magicLevel":125,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":120,"magicDef":140},"7795":{"name":"Ancient Wyvern","hitpoints":300,"combatLevel":210,"slayerLevel":82,"attackSpeed":5,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":90,"magicLevel":90,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":120,"magicDef":170},"7796":{"name":"Lobstrosity","hitpoints":50,"combatLevel":68,"attackSpeed":4,"attackLevel":60,"strengthLevel":80,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":15,"rangeDef":70,"magicDef":99},"7797":{"name":"Ancient Zygomite","hitpoints":150,"combatLevel":109,"slayerLevel":57,"attackSpeed":4,"attackLevel":80,"strengthLevel":80,"defenceLevel":80,"rangeLevel":80,"magicLevel":80,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":30,"magicDef":30,"bonusAttack":50},"7799":{"name":"Ammonite Crab","hitpoints":100,"combatLevel":25,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"7802":{"name":"Hoop Snake","hitpoints":25,"combatLevel":19,"attackSpeed":4,"attackLevel":15,"strengthLevel":10,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"7804":{"name":"Tar Monster","hitpoints":200,"combatLevel":132,"attackSpeed":4,"attackLevel":120,"strengthLevel":80,"defenceLevel":70,"rangeLevel":100,"magicLevel":1},"7806":{"name":"Deranged archaeologist","hitpoints":200,"combatLevel":276,"attackSpeed":3,"attackLevel":280,"strengthLevel":160,"defenceLevel":280,"rangeLevel":320,"magicLevel":1,"range":90,"stabDef":20,"slashDef":20,"crushDef":50,"rangeDef":300,"magicDef":300,"bonusAttack":280,"bonusStrength":30,"bonusRangeStrength":90},"7817":{"name":"Lava beast","hitpoints":65,"attackSpeed":4},"7851":{"name":"Dusk","hitpoints":450,"combatLevel":248,"slayerLevel":75,"attackSpeed":6,"attackLevel":200,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":140},"7852":{"name":"Dawn","hitpoints":450,"combatLevel":228,"slayerLevel":75,"attackSpeed":6,"attackLevel":140,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":100,"magicDef":80,"poisonImmune":true,"venomImmune":true},"7853":{"name":"Dawn","hitpoints":450,"combatLevel":228,"slayerLevel":75,"attackSpeed":6,"attackLevel":140,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":100,"magicDef":80,"poisonImmune":true,"venomImmune":true},"7854":{"name":"Dusk","hitpoints":450,"combatLevel":248,"slayerLevel":75,"attackSpeed":6,"attackLevel":200,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":140},"7855":{"name":"Dusk","hitpoints":450,"combatLevel":248,"slayerLevel":75,"attackSpeed":6,"attackLevel":200,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":140},"7858":{"name":"Justiciar Zachariah","hitpoints":320,"combatLevel":348,"attackSpeed":6,"attackLevel":500,"strengthLevel":250,"defenceLevel":100,"rangeLevel":1,"magicLevel":180,"magic":80,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":200,"bonusAttack":200,"bonusMagicDamage":80,"poisonImmune":true,"venomImmune":true},"7859":{"name":"Derwen","hitpoints":320,"combatLevel":235,"attackSpeed":6,"attackLevel":250,"strengthLevel":150,"defenceLevel":100,"rangeLevel":1,"magicLevel":80,"magic":180,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":200,"bonusMagicDamage":80,"poisonImmune":true,"venomImmune":true},"7860":{"name":"Porazdir","hitpoints":320,"combatLevel":235,"attackSpeed":6,"attackLevel":250,"strengthLevel":150,"defenceLevel":100,"rangeLevel":1,"magicLevel":180,"magic":80,"stabDef":200,"slashDef":200,"crushDef":200,"rangeDef":200,"bonusMagicDamage":80,"poisonImmune":true,"venomImmune":true},"7861":{"name":"Black dragon","hitpoints":250,"combatLevel":247,"attackSpeed":4,"attackLevel":200,"strengthLevel":215,"defenceLevel":200,"rangeLevel":1,"magicLevel":150,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"7862":{"name":"Black dragon","hitpoints":250,"combatLevel":247,"attackSpeed":4,"attackLevel":200,"strengthLevel":215,"defenceLevel":200,"rangeLevel":1,"magicLevel":150,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"7863":{"name":"Black dragon","hitpoints":250,"combatLevel":247,"attackSpeed":4,"attackLevel":200,"strengthLevel":215,"defenceLevel":200,"rangeLevel":1,"magicLevel":150,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"7864":{"name":"Ankou","hitpoints":100,"combatLevel":98,"attackSpeed":4,"attackLevel":75,"strengthLevel":90,"defenceLevel":80,"rangeLevel":1,"magicLevel":1},"7865":{"name":"Lesser demon","hitpoints":110,"combatLevel":94,"attackSpeed":4,"attackLevel":68,"strengthLevel":85,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7866":{"name":"Lesser demon","hitpoints":110,"combatLevel":94,"attackSpeed":4,"attackLevel":68,"strengthLevel":85,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7867":{"name":"Lesser demon","hitpoints":110,"combatLevel":94,"attackSpeed":4,"attackLevel":68,"strengthLevel":85,"defenceLevel":71,"rangeLevel":1,"magicLevel":1},"7868":{"name":"Green dragon","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":68,"strengthLevel":75,"defenceLevel":68,"rangeLevel":1,"magicLevel":75,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"7869":{"name":"Green dragon","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":68,"strengthLevel":75,"defenceLevel":68,"rangeLevel":1,"magicLevel":75,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"7870":{"name":"Green dragon","hitpoints":100,"combatLevel":88,"attackSpeed":4,"attackLevel":68,"strengthLevel":75,"defenceLevel":68,"rangeLevel":1,"magicLevel":75,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"7871":{"name":"Greater demon","hitpoints":120,"combatLevel":104,"attackSpeed":4,"attackLevel":76,"strengthLevel":90,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"7872":{"name":"Greater demon","hitpoints":120,"combatLevel":104,"attackSpeed":4,"attackLevel":76,"strengthLevel":90,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"7873":{"name":"Greater demon","hitpoints":120,"combatLevel":104,"attackSpeed":4,"attackLevel":76,"strengthLevel":90,"defenceLevel":81,"rangeLevel":1,"magicLevel":1},"7874":{"name":"Black demon","hitpoints":200,"combatLevel":188,"attackSpeed":4,"attackLevel":145,"strengthLevel":165,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"7875":{"name":"Black demon","hitpoints":200,"combatLevel":188,"attackSpeed":4,"attackLevel":145,"strengthLevel":165,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"7876":{"name":"Black demon","hitpoints":200,"combatLevel":188,"attackSpeed":4,"attackLevel":145,"strengthLevel":165,"defenceLevel":152,"rangeLevel":1,"magicLevel":1},"7877":{"name":"Hellhound","hitpoints":150,"combatLevel":136,"attackSpeed":4,"attackLevel":105,"strengthLevel":120,"defenceLevel":102,"rangeLevel":1,"magicLevel":1},"7878":{"name":"Ice giant","hitpoints":100,"combatLevel":67,"attackSpeed":5,"attackLevel":40,"strengthLevel":60,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"7879":{"name":"Ice giant","hitpoints":100,"combatLevel":67,"attackSpeed":5,"attackLevel":40,"strengthLevel":60,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"7880":{"name":"Ice giant","hitpoints":100,"combatLevel":67,"attackSpeed":5,"attackLevel":40,"strengthLevel":60,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"slashDef":3,"crushDef":2,"bonusAttack":29,"bonusStrength":31},"7881":{"name":"Revenant imp","hitpoints":10,"combatLevel":7,"attackSpeed":5,"attackLevel":5,"strengthLevel":5,"defenceLevel":4,"rangeLevel":5,"magicLevel":9,"magic":5,"magicDef":5},"7882":{"name":"Dusk","hitpoints":450,"combatLevel":248,"slayerLevel":75,"attackSpeed":6,"attackLevel":200,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":140},"7883":{"name":"Dusk","hitpoints":450,"combatLevel":248,"slayerLevel":75,"attackSpeed":6,"attackLevel":200,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":140},"7884":{"name":"Dawn","hitpoints":450,"combatLevel":228,"slayerLevel":75,"attackSpeed":6,"attackLevel":140,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":100,"magicDef":80,"poisonImmune":true,"venomImmune":true},"7885":{"name":"Dawn","hitpoints":450,"combatLevel":228,"slayerLevel":75,"attackSpeed":6,"attackLevel":140,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":100,"magicDef":80,"poisonImmune":true,"venomImmune":true},"7886":{"name":"Dusk","hitpoints":450,"combatLevel":248,"slayerLevel":75,"attackSpeed":6,"attackLevel":200,"strengthLevel":140,"defenceLevel":100,"rangeLevel":140,"magicLevel":140},"7887":{"name":"Dusk","hitpoints":450,"combatLevel":328,"slayerLevel":75,"attackSpeed":6,"attackLevel":300,"strengthLevel":250,"defenceLevel":150,"rangeLevel":250,"magicLevel":250},"7888":{"name":"Dusk","hitpoints":450,"combatLevel":328,"slayerLevel":75,"attackSpeed":6,"attackLevel":300,"strengthLevel":250,"defenceLevel":150,"rangeLevel":250,"magicLevel":250},"7889":{"name":"Dusk","hitpoints":450,"combatLevel":328,"slayerLevel":75,"attackSpeed":6,"attackLevel":300,"strengthLevel":250,"defenceLevel":150,"rangeLevel":250,"magicLevel":250},"7894":{"name":"Sand Snake (hard)","hitpoints":180,"combatLevel":154,"attackSpeed":4,"attackLevel":180,"strengthLevel":140,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"7895":{"name":"Sand Snake","hitpoints":60,"combatLevel":36,"attackSpeed":4,"attackLevel":30,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"7903":{"name":"Sand Snake","hitpoints":60,"combatLevel":36,"attackSpeed":4,"attackLevel":30,"strengthLevel":20,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"7914":{"name":"Thief","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":24,"slashDef":14,"crushDef":19,"rangeDef":16,"magicDef":4,"bonusAttack":9,"bonusStrength":7},"7915":{"name":"Thief","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":24,"slashDef":14,"crushDef":19,"rangeDef":16,"magicDef":4,"bonusAttack":9,"bonusStrength":7},"7916":{"name":"Thief","hitpoints":22,"combatLevel":21,"attackSpeed":4,"attackLevel":19,"strengthLevel":18,"defenceLevel":14,"rangeLevel":1,"magicLevel":1,"stabDef":24,"slashDef":14,"crushDef":19,"rangeDef":16,"magicDef":4,"bonusAttack":9,"bonusStrength":7},"7931":{"name":"Revenant goblin","hitpoints":14,"combatLevel":15,"attackSpeed":5,"attackLevel":13,"strengthLevel":14,"defenceLevel":14,"rangeLevel":15,"magicLevel":12,"range":21,"magic":37,"stabDef":25,"slashDef":28,"crushDef":31,"rangeDef":31,"magicDef":1,"bonusAttack":6,"bonusStrength":8,"bonusRangeStrength":12,"bonusMagicDamage":12},"7932":{"name":"Revenant pyrefiend","hitpoints":48,"combatLevel":52,"attackSpeed":5,"attackLevel":60,"strengthLevel":40,"defenceLevel":33,"rangeLevel":40,"magicLevel":67,"stabDef":45,"slashDef":40,"crushDef":50,"rangeDef":10,"magicDef":15},"7933":{"name":"Revenant hobgoblin","hitpoints":72,"combatLevel":60,"attackSpeed":4,"attackLevel":50,"strengthLevel":50,"defenceLevel":41,"rangeLevel":60,"magicLevel":55,"range":25,"magic":5,"stabDef":65,"slashDef":60,"crushDef":68,"rangeDef":50,"magicDef":30,"bonusAttack":20,"bonusStrength":24},"7934":{"name":"Revenant cyclops","hitpoints":110,"combatLevel":82,"attackSpeed":6,"attackLevel":60,"strengthLevel":73,"defenceLevel":49,"rangeLevel":70,"magicLevel":65,"stabDef":140,"slashDef":130,"crushDef":135,"rangeDef":135,"magicDef":10,"bonusAttack":53,"bonusStrength":64},"7935":{"name":"Revenant hellhound","hitpoints":80,"combatLevel":90,"attackSpeed":5,"attackLevel":76,"strengthLevel":80,"defenceLevel":80,"rangeLevel":80,"magicLevel":104,"magic":30,"stabDef":138,"slashDef":140,"crushDef":142,"rangeDef":140,"magicDef":62,"bonusAttack":38,"bonusStrength":40},"7936":{"name":"Revenant demon","hitpoints":80,"combatLevel":98,"attackSpeed":5,"attackLevel":83,"strengthLevel":76,"defenceLevel":80,"rangeLevel":80,"magicLevel":120,"range":40,"magic":50,"stabDef":124,"slashDef":118,"crushDef":130,"rangeDef":90,"magicDef":85,"bonusAttack":30,"bonusStrength":50,"bonusRangeStrength":30,"bonusMagicDamage":24},"7937":{"name":"Revenant ork","hitpoints":105,"combatLevel":105,"attackSpeed":5,"attackLevel":99,"strengthLevel":100,"defenceLevel":60,"rangeLevel":130,"magicLevel":110,"range":50,"stabDef":148,"slashDef":150,"crushDef":146,"rangeDef":148,"magicDef":50,"bonusAttack":60,"bonusStrength":55},"7938":{"name":"Revenant dark beast","hitpoints":140,"combatLevel":120,"attackSpeed":5,"attackLevel":93,"strengthLevel":110,"defenceLevel":80,"rangeLevel":135,"magicLevel":130,"range":45,"stabDef":153,"slashDef":152,"crushDef":155,"rangeDef":158,"magicDef":70,"bonusAttack":65,"bonusStrength":60},"7939":{"name":"Revenant knight","hitpoints":143,"combatLevel":126,"attackSpeed":5,"attackLevel":100,"strengthLevel":119,"defenceLevel":80,"rangeLevel":146,"magicLevel":146,"range":55,"magic":55,"stabDef":195,"slashDef":200,"crushDef":180,"rangeDef":190,"magicDef":95,"bonusAttack":69,"bonusStrength":71,"bonusRangeStrength":45,"bonusMagicDamage":50},"7940":{"name":"Revenant dragon","hitpoints":155,"combatLevel":135,"attackSpeed":5,"attackLevel":106,"strengthLevel":126,"defenceLevel":87,"rangeLevel":151,"magicLevel":150,"range":60,"magic":61,"stabDef":201,"slashDef":206,"crushDef":188,"rangeDef":197,"magicDef":101,"bonusAttack":72,"bonusStrength":78,"bonusRangeStrength":51,"bonusMagicDamage":35},"7948":{"name":"Corsair Traitor (hard)","hitpoints":160,"combatLevel":103,"attackSpeed":3,"attackLevel":1,"strengthLevel":1,"defenceLevel":80,"rangeLevel":1,"magicLevel":90},"7949":{"name":"Corsair Traitor","hitpoints":55,"combatLevel":35,"attackSpeed":5,"attackLevel":1,"strengthLevel":1,"defenceLevel":30,"rangeLevel":1,"magicLevel":30},"7955":{"name":"Baby black dragon","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":30,"slashDef":50,"crushDef":50,"rangeDef":30,"magicDef":40},"7964":{"name":"Ithoi the Navigator","hitpoints":55,"combatLevel":35,"attackSpeed":5,"attackLevel":1,"strengthLevel":1,"defenceLevel":30,"rangeLevel":1,"magicLevel":30},"7989":{"name":"Ogress Warrior","hitpoints":82,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":82,"rangeLevel":1,"magicLevel":60,"stabDef":10,"slashDef":12,"crushDef":12,"rangeDef":16,"magicDef":14},"7990":{"name":"Ogress Warrior","hitpoints":82,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":70,"defenceLevel":82,"rangeLevel":1,"magicLevel":60,"stabDef":10,"slashDef":12,"crushDef":12,"rangeDef":16,"magicDef":14},"7991":{"name":"Ogress Shaman","hitpoints":82,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":20,"defenceLevel":82,"rangeLevel":1,"magicLevel":68,"stabDef":12,"slashDef":14,"crushDef":14,"rangeDef":8,"magicDef":16},"7992":{"name":"Ogress Shaman","hitpoints":82,"combatLevel":82,"attackSpeed":4,"attackLevel":68,"strengthLevel":20,"defenceLevel":82,"rangeLevel":1,"magicLevel":68,"stabDef":12,"slashDef":14,"crushDef":14,"rangeDef":8,"magicDef":16},"7996":{"name":"Corrupt Lizardman (hard)","hitpoints":150,"combatLevel":152,"attackSpeed":4,"attackLevel":162,"strengthLevel":162,"defenceLevel":38,"rangeLevel":162,"magicLevel":1,"range":22,"slashDef":25,"bonusAttack":22,"bonusStrength":20,"bonusRangeStrength":20},"7997":{"name":"Corrupt Lizardman","hitpoints":50,"combatLevel":46,"attackSpeed":4,"attackLevel":38,"strengthLevel":38,"defenceLevel":38,"rangeLevel":38,"magicLevel":1,"range":22,"slashDef":25,"bonusAttack":22,"bonusStrength":20,"bonusRangeStrength":20},"8000":{"name":"Corrupt Lizardman","hitpoints":50,"combatLevel":46,"attackSpeed":4,"attackLevel":38,"strengthLevel":38,"defenceLevel":38,"rangeLevel":38,"magicLevel":1,"range":22,"slashDef":25,"bonusAttack":22,"bonusStrength":20,"bonusRangeStrength":20},"8027":{"name":"Rune dragon","hitpoints":330,"combatLevel":380,"attackSpeed":4,"attackLevel":284,"strengthLevel":284,"defenceLevel":276,"rangeLevel":246,"magicLevel":196,"stabDef":30,"slashDef":115,"crushDef":90,"rangeDef":95,"magicDef":30,"bonusRangeStrength":14,"poisonImmune":true,"venomImmune":true},"8030":{"name":"Adamant dragon","hitpoints":295,"combatLevel":338,"attackSpeed":4,"attackLevel":280,"strengthLevel":280,"defenceLevel":272,"rangeLevel":186,"magicLevel":186,"stabDef":30,"slashDef":110,"crushDef":85,"rangeDef":95,"magicDef":30},"8031":{"name":"Rune dragon","hitpoints":330,"combatLevel":380,"attackSpeed":4,"attackLevel":284,"strengthLevel":284,"defenceLevel":276,"rangeLevel":246,"magicLevel":196,"stabDef":30,"slashDef":115,"crushDef":90,"rangeDef":95,"magicDef":30,"bonusRangeStrength":14},"8033":{"name":"Elvarg","hitpoints":80,"combatLevel":83,"attackSpeed":4,"attackLevel":70,"strengthLevel":70,"defenceLevel":70,"rangeLevel":1,"magicLevel":70,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"8056":{"name":"Spawn","hitpoints":80,"combatLevel":100,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"range":1,"magic":1,"stabDef":5,"slashDef":15,"crushDef":15,"rangeDef":5},"8057":{"name":"Robert the Strong","hitpoints":280,"combatLevel":194,"attackSpeed":4,"defenceLevel":140,"rangeLevel":328,"magicLevel":128,"range":126,"stabDef":140,"slashDef":180,"crushDef":60,"rangeDef":860,"magicDef":940,"poisonImmune":true,"venomImmune":true},"8058":{"name":"Vorkath","hitpoints":460,"combatLevel":392,"attackSpeed":5,"attackLevel":560,"strengthLevel":268,"defenceLevel":164,"rangeLevel":268,"magicLevel":148,"range":96,"magic":148,"stabDef":66,"slashDef":126,"crushDef":126,"rangeDef":80,"magicDef":204,"bonusAttack":14},"8059":{"name":"Vorkath","hitpoints":750,"combatLevel":732,"attackSpeed":5,"attackLevel":560,"strengthLevel":308,"defenceLevel":214,"rangeLevel":308,"magicLevel":150,"range":78,"magic":150,"stabDef":26,"slashDef":108,"crushDef":108,"rangeDef":26,"magicDef":240,"bonusAttack":16},"8060":{"name":"Vorkath","hitpoints":460,"combatLevel":392,"attackSpeed":5,"attackLevel":560,"strengthLevel":268,"defenceLevel":164,"rangeLevel":268,"magicLevel":148,"range":96,"magic":148,"stabDef":66,"slashDef":126,"crushDef":126,"rangeDef":80,"magicDef":204,"bonusAttack":14},"8061":{"name":"Vorkath","hitpoints":750,"combatLevel":732,"attackSpeed":5,"attackLevel":560,"strengthLevel":308,"defenceLevel":214,"rangeLevel":308,"magicLevel":150,"range":78,"magic":150,"stabDef":26,"slashDef":108,"crushDef":108,"rangeDef":26,"magicDef":240,"bonusAttack":16},"8062":{"name":"Zombified Spawn","hitpoints":8,"combatLevel":55,"attackLevel":80,"strengthLevel":80,"defenceLevel":4,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":3,"crushDef":3,"rangeDef":3},"8063":{"name":"Zombified Spawn","hitpoints":38,"combatLevel":64,"attackLevel":82,"strengthLevel":82,"defenceLevel":6,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":3,"crushDef":3,"rangeDef":3},"8064":{"name":"Stone Guardian","hitpoints":62,"combatLevel":124,"attackSpeed":5,"attackLevel":150,"strengthLevel":148,"defenceLevel":138,"rangeLevel":1,"magicLevel":118},"8065":{"name":"Stone Guardian","hitpoints":62,"combatLevel":124,"attackSpeed":5,"attackLevel":1,"strengthLevel":1,"defenceLevel":138,"rangeLevel":128,"magicLevel":118},"8066":{"name":"Stone Guardian","hitpoints":62,"combatLevel":124,"attackSpeed":5,"attackLevel":1,"strengthLevel":1,"defenceLevel":138,"rangeLevel":1,"magicLevel":128},"8067":{"name":"Zombie","hitpoints":54,"combatLevel":132,"attackSpeed":5,"attackLevel":160,"strengthLevel":180,"defenceLevel":62,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":20,"crushDef":25,"rangeDef":30,"magicDef":10,"bonusAttack":30},"8068":{"name":"Zombie","hitpoints":54,"combatLevel":132,"attackSpeed":5,"attackLevel":160,"strengthLevel":1,"defenceLevel":62,"rangeLevel":180,"magicLevel":1,"range":20,"stabDef":15,"slashDef":10,"crushDef":15,"rangeDef":20,"magicDef":30},"8069":{"name":"Zombie","hitpoints":54,"combatLevel":132,"attackSpeed":5,"attackLevel":160,"strengthLevel":1,"defenceLevel":62,"rangeLevel":1,"magicLevel":180,"magic":20,"stabDef":35,"slashDef":30,"crushDef":35,"rangeDef":10,"magicDef":20},"8070":{"name":"Skeleton","hitpoints":54,"combatLevel":132,"attackSpeed":5,"attackLevel":160,"strengthLevel":180,"defenceLevel":62,"rangeLevel":1,"magicLevel":1,"stabDef":25,"slashDef":20,"crushDef":25,"rangeDef":30,"magicDef":10,"bonusAttack":30},"8071":{"name":"Skeleton","hitpoints":54,"combatLevel":132,"attackSpeed":5,"attackLevel":160,"strengthLevel":1,"defenceLevel":62,"rangeLevel":180,"magicLevel":1,"range":20,"stabDef":15,"slashDef":10,"crushDef":15,"rangeDef":20,"magicDef":30},"8072":{"name":"Skeleton","hitpoints":54,"combatLevel":132,"attackSpeed":5,"attackLevel":160,"strengthLevel":1,"defenceLevel":62,"rangeLevel":1,"magicLevel":180,"magic":20,"stabDef":35,"slashDef":30,"crushDef":35,"rangeDef":10,"magicDef":20},"8073":{"name":"Green dragon","hitpoints":75,"combatLevel":79,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"8075":{"name":"Red dragon","hitpoints":140,"combatLevel":152,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"8076":{"name":"Green dragon","hitpoints":75,"combatLevel":79,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"8078":{"name":"Red dragon","hitpoints":140,"combatLevel":152,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"8079":{"name":"Red dragon","hitpoints":140,"combatLevel":152,"attackSpeed":4,"attackLevel":130,"strengthLevel":130,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"8080":{"name":"Iron dragon","hitpoints":165,"combatLevel":189,"attackSpeed":4,"attackLevel":165,"strengthLevel":165,"defenceLevel":165,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"8081":{"name":"Brutal green dragon","hitpoints":175,"combatLevel":227,"attackSpeed":4,"attackLevel":268,"strengthLevel":168,"defenceLevel":168,"magicLevel":168,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60,"poisonImmune":true,"venomImmune":true},"8082":{"name":"Green dragon","hitpoints":75,"combatLevel":79,"attackSpeed":4,"attackLevel":68,"strengthLevel":68,"defenceLevel":68,"rangeLevel":1,"magicLevel":68,"stabDef":20,"slashDef":40,"crushDef":40,"rangeDef":20,"magicDef":30},"8083":{"name":"Blue dragon","hitpoints":105,"combatLevel":111,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":95,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"8084":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"8085":{"name":"Black dragon","hitpoints":190,"combatLevel":227,"attackSpeed":4,"attackLevel":200,"strengthLevel":200,"defenceLevel":200,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"8086":{"name":"Steel dragon","hitpoints":210,"combatLevel":246,"attackSpeed":4,"attackLevel":215,"strengthLevel":215,"defenceLevel":215,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":90,"magicDef":30},"8087":{"name":"Brutal red dragon","hitpoints":285,"combatLevel":289,"attackSpeed":4,"attackLevel":310,"strengthLevel":210,"defenceLevel":198,"magicLevel":250,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60,"poisonImmune":true,"venomImmune":true},"8088":{"name":"Mithril dragon","hitpoints":254,"combatLevel":304,"attackSpeed":4,"attackLevel":268,"strengthLevel":268,"defenceLevel":268,"rangeLevel":168,"magicLevel":168,"stabDef":50,"slashDef":100,"crushDef":70,"rangeDef":90,"magicDef":30,"poisonImmune":true,"venomImmune":true},"8089":{"name":"Mithril dragon","hitpoints":254,"combatLevel":304,"attackSpeed":4,"attackLevel":268,"strengthLevel":268,"defenceLevel":268,"rangeLevel":168,"magicLevel":168,"stabDef":50,"slashDef":100,"crushDef":70,"rangeDef":90,"magicDef":30,"poisonImmune":true,"venomImmune":true},"8090":{"name":"Adamant dragon","hitpoints":295,"combatLevel":338,"attackSpeed":4,"attackLevel":280,"strengthLevel":280,"defenceLevel":272,"rangeLevel":186,"magicLevel":186,"stabDef":30,"slashDef":110,"crushDef":85,"rangeDef":95,"magicDef":30},"8091":{"name":"Rune dragon","hitpoints":330,"combatLevel":380,"attackSpeed":4,"attackLevel":284,"strengthLevel":284,"defenceLevel":276,"rangeLevel":246,"magicLevel":196,"stabDef":30,"slashDef":115,"crushDef":90,"rangeDef":95,"magicDef":30,"bonusRangeStrength":14},"8092":{"name":"Brutal black dragon","hitpoints":315,"combatLevel":318,"slayerLevel":77,"attackSpeed":4,"attackLevel":330,"strengthLevel":210,"defenceLevel":258,"magicLevel":250,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60,"bonusStrength":20,"poisonImmune":true,"venomImmune":true},"8093":{"name":"Brutal black dragon","hitpoints":315,"combatLevel":318,"slayerLevel":77,"attackSpeed":4,"attackLevel":330,"strengthLevel":210,"defenceLevel":258,"magicLevel":250,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60,"bonusStrength":20,"poisonImmune":true,"venomImmune":true},"8094":{"name":"Galvek","hitpoints":1200,"combatLevel":608,"attackSpeed":6,"attackLevel":632,"strengthLevel":268,"defenceLevel":188,"rangeLevel":246,"magicLevel":160,"range":180,"magic":160,"stabDef":80,"slashDef":140,"crushDef":140,"rangeDef":86,"magicDef":280,"bonusAttack":34,"bonusRangeStrength":6,"bonusMagicDamage":42},"8095":{"name":"Galvek","hitpoints":1200,"combatLevel":608,"attackSpeed":6,"attackLevel":632,"strengthLevel":268,"defenceLevel":188,"rangeLevel":246,"magicLevel":160,"range":180,"magic":160,"stabDef":80,"slashDef":140,"crushDef":140,"rangeDef":86,"magicDef":280,"bonusAttack":34,"bonusRangeStrength":6,"bonusMagicDamage":42},"8096":{"name":"Galvek","hitpoints":1200,"combatLevel":608,"attackSpeed":6,"attackLevel":632,"strengthLevel":268,"defenceLevel":188,"rangeLevel":246,"magicLevel":160,"range":180,"magic":160,"stabDef":80,"slashDef":140,"crushDef":140,"rangeDef":86,"magicDef":280,"bonusAttack":34,"bonusRangeStrength":6,"bonusMagicDamage":42},"8097":{"name":"Galvek","hitpoints":1200,"combatLevel":608,"attackSpeed":6,"attackLevel":632,"strengthLevel":268,"defenceLevel":188,"rangeLevel":246,"magicLevel":160,"range":180,"magic":160,"stabDef":80,"slashDef":140,"crushDef":140,"rangeDef":86,"magicDef":280,"bonusAttack":34,"bonusRangeStrength":6,"bonusMagicDamage":42},"8098":{"name":"Galvek","hitpoints":1200,"combatLevel":608,"attackSpeed":6,"attackLevel":632,"strengthLevel":268,"defenceLevel":188,"rangeLevel":246,"magicLevel":160,"range":180,"magic":160,"stabDef":80,"slashDef":140,"crushDef":140,"rangeDef":86,"magicDef":280,"bonusAttack":34,"bonusRangeStrength":6,"bonusMagicDamage":42},"8137":{"name":"Spider","hitpoints":4,"combatLevel":35,"attackSpeed":4,"attackLevel":45,"strengthLevel":47,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":10,"rangeDef":20,"magicDef":17},"8138":{"name":"Spider","hitpoints":4,"combatLevel":35,"attackSpeed":4,"attackLevel":45,"strengthLevel":47,"defenceLevel":20,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"crushDef":10,"rangeDef":20,"magicDef":17},"8149":{"name":"White Knight","hitpoints":52,"combatLevel":36,"attackSpeed":7,"attackLevel":27,"strengthLevel":29,"defenceLevel":21,"rangeLevel":1,"magicLevel":1,"stabDef":83,"slashDef":76,"crushDef":70,"rangeDef":74,"bonusAttack":30,"bonusStrength":31},"8150":{"name":"Paladin","hitpoints":66,"combatLevel":62,"attackSpeed":5,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":22},"8177":{"name":"Galvek","hitpoints":1200,"combatLevel":608,"attackSpeed":6,"attackLevel":632,"strengthLevel":268,"defenceLevel":188,"rangeLevel":246,"magicLevel":160,"range":180,"magic":160,"stabDef":80,"slashDef":140,"crushDef":140,"rangeDef":86,"magicDef":280,"bonusAttack":34,"bonusRangeStrength":6,"bonusMagicDamage":42},"8178":{"name":"Galvek","hitpoints":1200,"combatLevel":608,"attackSpeed":6,"attackLevel":632,"strengthLevel":268,"defenceLevel":188,"rangeLevel":246,"magicLevel":160,"range":180,"magic":160,"stabDef":80,"slashDef":140,"crushDef":140,"rangeDef":86,"magicDef":280,"bonusAttack":34,"bonusRangeStrength":6,"bonusMagicDamage":42},"8179":{"name":"Galvek","hitpoints":1200,"combatLevel":608,"attackSpeed":6,"attackLevel":632,"strengthLevel":268,"defenceLevel":188,"rangeLevel":246,"magicLevel":160,"range":180,"magic":160,"stabDef":80,"slashDef":140,"crushDef":140,"rangeDef":86,"magicDef":280,"bonusAttack":34,"bonusRangeStrength":6,"bonusMagicDamage":42},"8194":{"name":"Growthling","hitpoints":10,"combatLevel":37,"attackSpeed":4,"attackLevel":40,"strengthLevel":38,"defenceLevel":40,"rangeLevel":1,"magicLevel":1,"poisonImmune":true,"venomImmune":true},"8195":{"name":"Bryophyta","hitpoints":115,"combatLevel":128,"attackSpeed":6,"attackLevel":130,"strengthLevel":100,"defenceLevel":100,"rangeLevel":1,"magicLevel":90,"bonusAttack":33,"bonusStrength":31},"8239":{"name":"Vanstrom Klause","hitpoints":155,"combatLevel":169,"attackSpeed":8,"attackLevel":150,"strengthLevel":136,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"poisonImmune":true,"venomImmune":true},"8240":{"name":"Vanstrom Klause","hitpoints":155,"combatLevel":169,"attackSpeed":8,"attackLevel":150,"strengthLevel":136,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"poisonImmune":true,"venomImmune":true},"8242":{"name":"Ranis Drakan","hitpoints":400,"combatLevel":233,"attackSpeed":5,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":120,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":34,"bonusMagicDamage":34,"poisonImmune":true,"venomImmune":true},"8243":{"name":"Ranis Drakan","hitpoints":400,"combatLevel":233,"attackSpeed":5,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":120,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":34,"bonusMagicDamage":34,"poisonImmune":true,"venomImmune":true},"8244":{"name":"Ranis Drakan","hitpoints":400,"combatLevel":233,"attackSpeed":5,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":120,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":34,"bonusMagicDamage":34,"poisonImmune":true,"venomImmune":true},"8245":{"name":"Ranis Drakan","hitpoints":400,"combatLevel":233,"attackSpeed":5,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":120,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":34,"bonusMagicDamage":34,"poisonImmune":true,"venomImmune":true},"8246":{"name":"Ranis Drakan","hitpoints":400,"combatLevel":233,"attackSpeed":5,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":120,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":34,"bonusMagicDamage":34,"poisonImmune":true,"venomImmune":true},"8247":{"name":"Ranis Drakan","hitpoints":400,"combatLevel":233,"attackSpeed":5,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":120,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusStrength":34,"bonusMagicDamage":34,"poisonImmune":true,"venomImmune":true},"8248":{"name":"Ranis Drakan","hitpoints":400,"combatLevel":233,"attackSpeed":2,"attackLevel":120,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":120,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":60,"magicDef":60,"bonusAttack":160,"bonusMagicDamage":34,"poisonImmune":true,"venomImmune":true},"8251":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8252":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8253":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8254":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8255":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8256":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8257":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8258":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8259":{"name":"Vyrewatch","hitpoints":75,"combatLevel":87,"attackSpeed":8,"attackLevel":80,"strengthLevel":75,"defenceLevel":75,"rangeLevel":1,"magicLevel":75},"8260":{"name":"Abomination","hitpoints":200,"combatLevel":149,"attackSpeed":5,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":110,"magicLevel":110,"stabDef":80,"slashDef":80,"crushDef":80,"rangeDef":180,"magicDef":20},"8261":{"name":"Abomination","hitpoints":200,"combatLevel":149,"attackSpeed":5,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":110,"magicLevel":110,"stabDef":80,"slashDef":80,"crushDef":80,"rangeDef":180,"magicDef":20},"8262":{"name":"Abomination","hitpoints":200,"combatLevel":149,"attackSpeed":5,"attackLevel":110,"strengthLevel":110,"defenceLevel":110,"rangeLevel":110,"magicLevel":110,"stabDef":80,"slashDef":80,"crushDef":80,"rangeDef":180,"magicDef":20},"8297":{"name":"Swamp Crab","hitpoints":75,"combatLevel":55,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":50,"rangeLevel":1,"magicLevel":50,"stabDef":100,"slashDef":100,"crushDef":100,"magicDef":100},"8298":{"name":"Swamp Crab","hitpoints":75,"combatLevel":55,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":50,"rangeLevel":1,"magicLevel":50,"stabDef":100,"slashDef":100,"crushDef":100,"magicDef":100},"8299":{"name":"Swampy log","hitpoints":75,"combatLevel":55,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":50,"rangeLevel":1,"magicLevel":50,"stabDef":100,"slashDef":100,"crushDef":100,"magicDef":100},"8300":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"8301":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"8302":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"8303":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"8304":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"8305":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"8306":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"8307":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"8340":{"name":"Xarpus","hitpoints":5080,"combatLevel":960,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":250,"rangeLevel":1,"magicLevel":220,"range":57,"rangeDef":160},"8341":{"name":"Xarpus","hitpoints":5080,"combatLevel":960,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":250,"rangeLevel":1,"magicLevel":220,"range":57,"rangeDef":160},"8342":{"name":"Nylocas Ischyros","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8343":{"name":"Nylocas Toxobolos","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8344":{"name":"Nylocas Hagios","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8345":{"name":"Nylocas Ischyros","hitpoints":22,"combatLevel":260,"attackSpeed":3,"attackLevel":250,"strengthLevel":230,"defenceLevel":20,"rangeLevel":250,"magicLevel":20,"magic":500,"bonusMagicDamage":500},"8346":{"name":"Nylocas Toxobolos","hitpoints":22,"combatLevel":260,"attackSpeed":3,"attackLevel":250,"strengthLevel":230,"defenceLevel":20,"rangeLevel":250,"magicLevel":20,"magic":500,"bonusMagicDamage":500},"8347":{"name":"Nylocas Hagios","hitpoints":22,"combatLevel":260,"attackSpeed":3,"attackLevel":250,"strengthLevel":230,"defenceLevel":20,"rangeLevel":250,"magicLevel":20,"magic":500,"bonusMagicDamage":500},"8348":{"name":"Nylocas Ischyros","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8349":{"name":"Nylocas Toxobolos","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8350":{"name":"Nylocas Hagios","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8351":{"name":"Nylocas Ischyros","hitpoints":22,"combatLevel":260,"attackSpeed":3,"attackLevel":250,"strengthLevel":230,"defenceLevel":20,"rangeLevel":250,"magicLevel":20,"magic":500,"bonusMagicDamage":500},"8352":{"name":"Nylocas Toxobolos","hitpoints":22,"combatLevel":260,"attackSpeed":3,"attackLevel":250,"strengthLevel":230,"defenceLevel":20,"rangeLevel":250,"magicLevel":20,"magic":500,"bonusMagicDamage":500},"8353":{"name":"Nylocas Hagios","hitpoints":22,"combatLevel":260,"attackSpeed":3,"attackLevel":250,"strengthLevel":230,"defenceLevel":20,"rangeLevel":250,"magicLevel":20,"magic":500,"bonusMagicDamage":500},"8355":{"name":"Nylocas Vasilias","hitpoints":2500,"combatLevel":800,"attackSpeed":4,"attackLevel":400,"strengthLevel":350,"defenceLevel":50,"rangeLevel":350,"magicLevel":50,"magic":600,"bonusStrength":60,"bonusRangeStrength":60,"bonusMagicDamage":600},"8356":{"name":"Nylocas Vasilias","hitpoints":2500,"combatLevel":800,"attackSpeed":4,"attackLevel":400,"strengthLevel":350,"defenceLevel":50,"rangeLevel":350,"magicLevel":50,"magic":600,"bonusStrength":60,"bonusRangeStrength":60,"bonusMagicDamage":600},"8357":{"name":"Nylocas Vasilias","hitpoints":2500,"combatLevel":800,"attackSpeed":4,"attackLevel":400,"strengthLevel":350,"defenceLevel":50,"rangeLevel":350,"magicLevel":50,"magic":600,"bonusStrength":60,"bonusRangeStrength":60,"bonusMagicDamage":600},"8359":{"name":"Pestilent Bloat","hitpoints":2000,"combatLevel":870,"attackSpeed":1,"attackLevel":250,"strengthLevel":340,"defenceLevel":100,"rangeLevel":180,"magicLevel":150,"range":180,"stabDef":40,"slashDef":20,"crushDef":40,"rangeDef":800,"magicDef":600,"bonusAttack":150,"bonusStrength":82,"bonusRangeStrength":4},"8360":{"name":"The Maiden of Sugadinti","hitpoints":3500,"combatLevel":940,"attackSpeed":10,"attackLevel":350,"strengthLevel":350,"defenceLevel":200,"rangeLevel":350,"magicLevel":350,"magic":300},"8361":{"name":"The Maiden of Sugadinti","hitpoints":3500,"combatLevel":940,"attackSpeed":10,"attackLevel":350,"strengthLevel":350,"defenceLevel":200,"rangeLevel":350,"magicLevel":350,"magic":300},"8362":{"name":"The Maiden of Sugadinti","hitpoints":3500,"combatLevel":940,"attackSpeed":10,"attackLevel":350,"strengthLevel":350,"defenceLevel":200,"rangeLevel":350,"magicLevel":350,"magic":300},"8363":{"name":"The Maiden of Sugadinti","hitpoints":3500,"combatLevel":940,"attackSpeed":10,"attackLevel":350,"strengthLevel":350,"defenceLevel":200,"rangeLevel":350,"magicLevel":350,"magic":300},"8366":{"name":"Nylocas Matomenos","hitpoints":200,"combatLevel":115,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":100,"magicLevel":100},"8367":{"name":"Blood spawn","hitpoints":120,"combatLevel":55,"attackLevel":1,"strengthLevel":1,"rangeLevel":1},"8369":{"name":"Verzik Vitur","hitpoints":2000,"combatLevel":1040,"attackSpeed":4,"attackLevel":400,"strengthLevel":400,"defenceLevel":20,"rangeLevel":400,"magicLevel":400,"range":80,"magic":80,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusRangeStrength":80,"bonusMagicDamage":150},"8370":{"name":"Verzik Vitur","hitpoints":2000,"combatLevel":1040,"attackSpeed":4,"attackLevel":400,"strengthLevel":400,"defenceLevel":20,"rangeLevel":400,"magicLevel":400,"range":80,"magic":80,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusRangeStrength":80,"bonusMagicDamage":150},"8371":{"name":"Verzik Vitur","hitpoints":2000,"combatLevel":1040,"attackSpeed":4,"attackLevel":400,"strengthLevel":400,"defenceLevel":20,"rangeLevel":400,"magicLevel":400,"range":80,"magic":80,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusRangeStrength":80,"bonusMagicDamage":150},"8372":{"name":"Verzik Vitur","hitpoints":3250,"combatLevel":1265,"attackSpeed":4,"attackLevel":400,"strengthLevel":400,"defenceLevel":200,"rangeLevel":400,"magicLevel":400,"range":80,"magic":80,"stabDef":100,"slashDef":60,"crushDef":100,"rangeDef":250,"magicDef":70,"bonusRangeStrength":80,"bonusMagicDamage":80},"8373":{"name":"Verzik Vitur","hitpoints":3250,"combatLevel":1265,"attackSpeed":4,"attackLevel":400,"strengthLevel":400,"defenceLevel":200,"rangeLevel":400,"magicLevel":400,"range":80,"magic":80,"stabDef":100,"slashDef":60,"crushDef":100,"rangeDef":250,"magicDef":70,"bonusRangeStrength":80,"bonusMagicDamage":80},"8374":{"name":"Verzik Vitur","hitpoints":3250,"combatLevel":1520,"attackSpeed":7,"attackLevel":400,"strengthLevel":400,"defenceLevel":150,"rangeLevel":300,"magicLevel":300,"range":80,"magic":80,"stabDef":70,"slashDef":30,"crushDef":70,"rangeDef":230,"magicDef":100,"bonusAttack":80,"bonusStrength":30,"bonusRangeStrength":5,"bonusMagicDamage":5},"8375":{"name":"Verzik Vitur","hitpoints":3250,"combatLevel":1520,"attackSpeed":7,"attackLevel":400,"strengthLevel":400,"defenceLevel":150,"rangeLevel":300,"magicLevel":300,"range":80,"magic":80,"stabDef":70,"slashDef":30,"crushDef":70,"rangeDef":230,"magicDef":100,"bonusAttack":80,"bonusStrength":30,"bonusRangeStrength":5,"bonusMagicDamage":5},"8381":{"name":"Nylocas Ischyros","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8382":{"name":"Nylocas Toxobolos","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8383":{"name":"Nylocas Hagios","hitpoints":11,"combatLevel":162,"attackSpeed":3,"attackLevel":200,"strengthLevel":160,"defenceLevel":1,"rangeLevel":200,"magicLevel":1,"magic":1073,"bonusMagicDamage":928},"8384":{"name":"Nylocas Athanatos","hitpoints":180,"combatLevel":350,"attackLevel":1,"strengthLevel":1,"defenceLevel":50,"rangeLevel":1,"magicLevel":50},"8385":{"name":"Nylocas Matomenos","hitpoints":200,"combatLevel":115,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":100,"magicLevel":100},"8387":{"name":"Sotetseg","hitpoints":4000,"combatLevel":995,"attackSpeed":5,"attackLevel":250,"strengthLevel":250,"defenceLevel":200,"magicLevel":250,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":150,"magicDef":30,"bonusStrength":48,"poisonImmune":true,"venomImmune":true},"8388":{"name":"Sotetseg","hitpoints":4000,"combatLevel":995,"attackSpeed":5,"attackLevel":250,"strengthLevel":250,"defenceLevel":200,"magicLevel":250,"stabDef":70,"slashDef":70,"crushDef":70,"rangeDef":150,"magicDef":30,"bonusStrength":48,"poisonImmune":true,"venomImmune":true},"8400":{"name":"Monk of Zamorak","hitpoints":20,"combatLevel":22,"attackSpeed":4,"attackLevel":18,"strengthLevel":18,"defenceLevel":22,"rangeLevel":1,"magicLevel":25},"8401":{"name":"Monk of Zamorak","hitpoints":10,"combatLevel":17,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":12,"rangeLevel":1,"magicLevel":25},"8439":{"name":"Don't Know What","hitpoints":220,"combatLevel":163,"attackSpeed":3,"attackLevel":120,"strengthLevel":90,"defenceLevel":160,"rangeLevel":115,"magicLevel":60,"magicDef":200,"bonusRangeStrength":40},"8474":{"name":"Black Guard","hitpoints":40,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":45,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":40,"bonusAttack":30,"bonusStrength":30},"8475":{"name":"Black Guard","hitpoints":40,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":45,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":40,"bonusAttack":30,"bonusStrength":30},"8476":{"name":"Black Guard","hitpoints":40,"combatLevel":48,"attackSpeed":3,"attackLevel":40,"strengthLevel":45,"defenceLevel":45,"rangeLevel":1,"magicLevel":1,"stabDef":40,"slashDef":40,"crushDef":40,"rangeDef":40,"bonusAttack":30,"bonusStrength":30},"8496":{"name":"Dwarf","hitpoints":18,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"rangeDef":10,"magicDef":5,"bonusAttack":5,"bonusStrength":7},"8512":{"name":"Tormented Soul","hitpoints":20,"combatLevel":16,"attackSpeed":4,"attackLevel":16,"strengthLevel":12,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"8513":{"name":"Tormented Soul","hitpoints":20,"combatLevel":16,"attackSpeed":4,"attackLevel":16,"strengthLevel":12,"defenceLevel":10,"rangeLevel":1,"magicLevel":1},"8514":{"name":"Trapped Soul","hitpoints":30,"combatLevel":30,"attackSpeed":4,"attackLevel":25,"strengthLevel":29,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"8528":{"name":"Trapped Soul","hitpoints":30,"combatLevel":30,"attackSpeed":4,"attackLevel":25,"strengthLevel":29,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"8529":{"name":"Trapped Soul (hard)","hitpoints":100,"combatLevel":101,"attackSpeed":4,"attackLevel":100,"strengthLevel":120,"defenceLevel":20,"rangeLevel":1,"magicLevel":1},"8563":{"name":"Lizardman","hitpoints":60,"combatLevel":62,"attackSpeed":4,"attackLevel":53,"strengthLevel":53,"defenceLevel":52,"rangeLevel":1,"magicLevel":1,"range":28,"slashDef":20,"crushDef":5,"bonusAttack":28,"bonusStrength":25,"bonusRangeStrength":25},"8564":{"name":"Lizardman brute","hitpoints":60,"combatLevel":73,"attackSpeed":4,"attackLevel":65,"strengthLevel":65,"defenceLevel":65,"rangeLevel":1,"magicLevel":1,"range":34,"slashDef":30,"crushDef":10,"bonusAttack":34,"bonusStrength":30,"bonusRangeStrength":30},"8565":{"name":"Lizardman shaman","hitpoints":150,"combatLevel":150,"attackSpeed":4,"attackLevel":120,"strengthLevel":120,"defenceLevel":140,"rangeLevel":120,"magicLevel":130,"range":45,"slashDef":40,"crushDef":30,"magicDef":50,"bonusAttack":45,"bonusStrength":38,"bonusRangeStrength":38},"8578":{"name":"Swamp frog","hitpoints":25,"combatLevel":24,"attackSpeed":4,"attackLevel":25,"strengthLevel":20,"defenceLevel":15,"rangeLevel":1,"magicLevel":1},"8583":{"name":"Hespori","hitpoints":300,"combatLevel":284,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":120,"rangeLevel":150,"magicLevel":126,"range":150,"magic":150,"stabDef":60,"slashDef":20,"crushDef":60,"rangeDef":80,"magicDef":80},"8584":{"name":"Flower","hitpoints":10},"8609":{"name":"Hydra","hitpoints":300,"combatLevel":194,"slayerLevel":95,"attackSpeed":6,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":210,"magicLevel":210,"stabDef":160,"slashDef":160,"crushDef":160,"magicDef":160},"8610":{"name":"Wyrm","hitpoints":130,"combatLevel":99,"slayerLevel":62,"attackSpeed":4,"attackLevel":85,"strengthLevel":60,"defenceLevel":80,"rangeLevel":80,"magicLevel":80,"stabDef":20,"slashDef":50,"crushDef":50,"rangeDef":20,"magicDef":50,"bonusStrength":30,"bonusMagicDamage":30},"8611":{"name":"Wyrm","hitpoints":130,"combatLevel":99,"slayerLevel":62,"attackSpeed":4,"attackLevel":85,"strengthLevel":60,"defenceLevel":80,"rangeLevel":80,"magicLevel":80,"stabDef":20,"slashDef":50,"crushDef":50,"rangeDef":20,"magicDef":50,"bonusStrength":30,"bonusMagicDamage":30},"8612":{"name":"Drake","hitpoints":250,"combatLevel":192,"slayerLevel":84,"attackSpeed":4,"attackLevel":140,"strengthLevel":118,"defenceLevel":120,"rangeLevel":140,"magicLevel":112,"range":40,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":100,"magicDef":20,"bonusAttack":40,"bonusStrength":12,"bonusRangeStrength":40},"8613":{"name":"Drake","hitpoints":250,"combatLevel":192,"slayerLevel":84,"attackSpeed":4,"attackLevel":140,"strengthLevel":118,"defenceLevel":120,"rangeLevel":140,"magicLevel":112,"range":40,"stabDef":60,"slashDef":60,"crushDef":60,"rangeDef":100,"magicDef":20,"bonusAttack":40,"bonusStrength":12,"bonusRangeStrength":40},"8614":{"name":"Sulphur Lizard","hitpoints":50,"combatLevel":50,"slayerLevel":44,"attackSpeed":4,"attackLevel":50,"strengthLevel":45,"defenceLevel":30,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":25,"crushDef":25,"rangeDef":15},"8615":{"name":"Alchemical Hydra","hitpoints":1100,"combatLevel":426,"slayerLevel":95,"attackSpeed":6,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":260,"magicLevel":260,"range":45,"magic":45,"stabDef":75,"slashDef":150,"crushDef":150,"rangeDef":45,"magicDef":150,"bonusRangeStrength":20,"bonusMagicDamage":20},"8616":{"name":"Alchemical Hydra","hitpoints":1100,"combatLevel":426,"slayerLevel":95,"attackSpeed":6,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":260,"magicLevel":260,"range":45,"magic":45,"stabDef":75,"slashDef":150,"crushDef":150,"rangeDef":45,"magicDef":150,"bonusRangeStrength":20,"bonusMagicDamage":20},"8617":{"name":"Alchemical Hydra","hitpoints":1100,"combatLevel":426,"slayerLevel":95,"attackSpeed":6,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":260,"magicLevel":260,"range":45,"magic":45,"stabDef":75,"slashDef":150,"crushDef":150,"rangeDef":45,"magicDef":150,"bonusRangeStrength":20,"bonusMagicDamage":20},"8618":{"name":"Alchemical Hydra","hitpoints":1100,"combatLevel":426,"slayerLevel":95,"attackSpeed":6,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":260,"magicLevel":260,"range":45,"magic":45,"stabDef":75,"slashDef":150,"crushDef":150,"rangeDef":45,"magicDef":150,"bonusRangeStrength":20,"bonusMagicDamage":20},"8619":{"name":"Alchemical Hydra","hitpoints":1100,"combatLevel":426,"slayerLevel":95,"attackSpeed":6,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":260,"magicLevel":260,"range":45,"magic":45,"stabDef":75,"slashDef":150,"crushDef":150,"rangeDef":45,"magicDef":150,"bonusRangeStrength":20,"bonusMagicDamage":20},"8620":{"name":"Alchemical Hydra","hitpoints":1100,"combatLevel":426,"slayerLevel":95,"attackSpeed":6,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":260,"magicLevel":260,"range":45,"magic":45,"stabDef":75,"slashDef":150,"crushDef":150,"rangeDef":45,"magicDef":150,"bonusRangeStrength":20,"bonusMagicDamage":20},"8621":{"name":"Alchemical Hydra","hitpoints":1100,"combatLevel":426,"slayerLevel":95,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":260,"magicLevel":260,"range":45,"magic":45,"stabDef":75,"slashDef":150,"crushDef":150,"rangeDef":45,"magicDef":150,"bonusRangeStrength":20,"bonusMagicDamage":20},"8622":{"name":"Alchemical Hydra","hitpoints":1100,"combatLevel":426,"slayerLevel":95,"attackSpeed":4,"attackLevel":100,"strengthLevel":100,"defenceLevel":100,"rangeLevel":260,"magicLevel":260,"range":45,"magic":45,"stabDef":75,"slashDef":150,"crushDef":150,"rangeDef":45,"magicDef":150,"bonusRangeStrength":20,"bonusMagicDamage":20},"8633":{"name":"The Mimic","hitpoints":230,"combatLevel":186,"attackSpeed":3,"attackLevel":185,"strengthLevel":120,"defenceLevel":120,"rangeLevel":1,"magicLevel":60,"magic":180,"stabDef":160,"slashDef":165,"crushDef":150,"rangeDef":145,"magicDef":30,"bonusAttack":135,"bonusStrength":48},"8635":{"name":"Third Age Warrior","hitpoints":40,"combatLevel":83,"attackSpeed":5,"attackLevel":90,"strengthLevel":75,"defenceLevel":80,"rangeLevel":1,"magicLevel":1,"stabDef":96,"slashDef":108,"crushDef":113,"rangeDef":97,"bonusAttack":105,"bonusStrength":75},"8636":{"name":"Third Age Ranger","hitpoints":40,"combatLevel":76,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":80,"rangeLevel":95,"magicLevel":1,"range":140,"stabDef":55,"slashDef":47,"crushDef":60,"rangeDef":55,"magicDef":60,"bonusRangeStrength":7},"8637":{"name":"Third Age Mage","hitpoints":40,"combatLevel":83,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":80,"rangeLevel":1,"magicLevel":110,"magic":145,"stabDef":5,"slashDef":6,"crushDef":5,"magicDef":65,"bonusMagicDamage":24},"8678":{"name":"Feral Vampyre","hitpoints":50,"combatLevel":72,"attackSpeed":4,"attackLevel":65,"strengthLevel":70,"defenceLevel":65,"rangeLevel":1,"magicLevel":50},"8698":{"name":"Monk of Zamorak","hitpoints":16,"combatLevel":23,"attackSpeed":4,"attackLevel":15,"strengthLevel":13,"defenceLevel":18,"rangeLevel":1,"magicLevel":30},"8700":{"name":"Giant frog","hitpoints":23,"combatLevel":13,"attackSpeed":4,"attackLevel":10,"strengthLevel":8,"defenceLevel":6,"rangeLevel":1,"magicLevel":1},"8701":{"name":"Big frog","hitpoints":18,"combatLevel":10,"attackSpeed":4,"attackLevel":8,"strengthLevel":6,"defenceLevel":4,"rangeLevel":1,"magicLevel":1},"8702":{"name":"Frog","hitpoints":8,"combatLevel":5,"attackSpeed":4,"attackLevel":5,"strengthLevel":4,"defenceLevel":3,"rangeLevel":1,"magicLevel":1},"8703":{"name":"Temple Spider","hitpoints":70,"combatLevel":75,"attackSpeed":4,"attackLevel":120,"strengthLevel":50,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":30,"crushDef":10,"rangeDef":20,"magicDef":25},"8709":{"name":"Shaeded Beast","hitpoints":210,"combatLevel":186,"attackSpeed":4,"attackLevel":50,"strengthLevel":80,"defenceLevel":50,"rangeLevel":1,"magicLevel":250,"stabDef":50,"slashDef":30,"crushDef":100,"rangeDef":50,"magicDef":150,"bonusMagicDamage":10},"8713":{"name":"Sarachnis","hitpoints":400,"combatLevel":318,"attackSpeed":4,"attackLevel":200,"strengthLevel":240,"defenceLevel":150,"rangeLevel":300,"magicLevel":150,"range":15,"stabDef":60,"slashDef":40,"crushDef":10,"rangeDef":300,"magicDef":150,"bonusAttack":30,"bonusRangeStrength":15},"8714":{"name":"Spawn of Sarachnis","hitpoints":30,"combatLevel":107,"attackSpeed":3,"attackLevel":150,"strengthLevel":120,"defenceLevel":50,"rangeLevel":1,"magicLevel":1,"stabDef":50,"slashDef":50,"crushDef":20,"rangeDef":150,"bonusAttack":50},"8715":{"name":"Spawn of Sarachnis","hitpoints":30,"combatLevel":68,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":50,"rangeLevel":1,"magicLevel":100,"stabDef":50,"slashDef":50,"crushDef":50,"magicDef":150},"8736":{"name":"Moss Giant","hitpoints":120,"combatLevel":84,"attackSpeed":6,"attackLevel":60,"strengthLevel":60,"defenceLevel":60,"rangeLevel":1,"magicLevel":1,"bonusAttack":66,"bonusStrength":62},"8759":{"name":"Iorwerth Warrior","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":80,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"8760":{"name":"Iorwerth Archer","hitpoints":105,"combatLevel":90,"attackSpeed":5,"attackLevel":10,"strengthLevel":10,"defenceLevel":80,"rangeLevel":90,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"8847":{"name":"Essyllt","hitpoints":320,"combatLevel":236,"attackSpeed":4,"attackLevel":268,"strengthLevel":268,"defenceLevel":104,"rangeLevel":342,"magicLevel":104,"stabDef":40,"slashDef":40,"crushDef":20,"rangeDef":120,"magicDef":30,"bonusStrength":28,"bonusRangeStrength":28},"8858":{"name":"Man","hitpoints":7,"combatLevel":2,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"8871":{"name":"Essyllt","hitpoints":320,"combatLevel":236,"attackSpeed":4,"attackLevel":268,"strengthLevel":268,"defenceLevel":104,"rangeLevel":342,"magicLevel":104,"stabDef":40,"slashDef":40,"crushDef":20,"rangeDef":120,"magicDef":30,"bonusStrength":28,"bonusRangeStrength":28},"8872":{"name":"Essyllt","hitpoints":320,"combatLevel":236,"attackSpeed":4,"attackLevel":268,"strengthLevel":268,"defenceLevel":104,"rangeLevel":342,"magicLevel":104,"stabDef":40,"slashDef":40,"crushDef":20,"rangeDef":120,"magicDef":30,"bonusStrength":28,"bonusRangeStrength":28},"8917":{"name":"Fragment of Seren","hitpoints":1000,"combatLevel":494,"attackSpeed":7,"attackLevel":242,"strengthLevel":288,"defenceLevel":102,"rangeLevel":235,"magicLevel":102,"range":224,"stabDef":320,"slashDef":220,"crushDef":320,"rangeDef":480,"magicDef":10,"bonusAttack":236,"bonusStrength":44},"8918":{"name":"Fragment of Seren","hitpoints":1000,"combatLevel":494,"attackSpeed":7,"attackLevel":242,"strengthLevel":288,"defenceLevel":102,"rangeLevel":235,"magicLevel":102,"range":224,"stabDef":320,"slashDef":220,"crushDef":320,"rangeDef":480,"magicDef":10,"bonusAttack":236,"bonusStrength":44},"8919":{"name":"Fragment of Seren","hitpoints":1000,"combatLevel":494,"attackSpeed":7,"attackLevel":242,"strengthLevel":288,"defenceLevel":102,"rangeLevel":235,"magicLevel":102,"range":224,"stabDef":320,"slashDef":220,"crushDef":320,"rangeDef":480,"magicDef":10,"bonusAttack":236,"bonusStrength":44},"8920":{"name":"Fragment of Seren","hitpoints":1000,"combatLevel":494,"attackSpeed":7,"attackLevel":242,"strengthLevel":288,"defenceLevel":102,"rangeLevel":235,"magicLevel":102,"range":224,"stabDef":320,"slashDef":220,"crushDef":320,"rangeDef":480,"magicDef":10,"bonusAttack":236,"bonusStrength":44},"8950":{"name":"Essyllt","hitpoints":320,"combatLevel":236,"attackSpeed":4,"attackLevel":268,"strengthLevel":268,"defenceLevel":104,"rangeLevel":342,"magicLevel":104,"stabDef":40,"slashDef":40,"crushDef":20,"rangeDef":120,"magicDef":30,"bonusStrength":28,"bonusRangeStrength":28},"8978":{"name":"Blessed spider","hitpoints":32,"combatLevel":39,"attackSpeed":4,"attackLevel":35,"strengthLevel":35,"defenceLevel":35,"rangeLevel":1,"magicLevel":1,"stabDef":15,"slashDef":16,"crushDef":7,"rangeDef":16,"magicDef":12},"8987":{"name":"Sir Jerro","hitpoints":57,"combatLevel":62,"attackSpeed":5,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":22},"8988":{"name":"Sir Carl","hitpoints":57,"combatLevel":62,"attackSpeed":5,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":22},"8989":{"name":"Sir Harry","hitpoints":57,"combatLevel":62,"attackSpeed":5,"attackLevel":54,"strengthLevel":54,"defenceLevel":54,"rangeLevel":1,"magicLevel":1,"stabDef":87,"slashDef":84,"crushDef":76,"rangeDef":79,"bonusAttack":20,"bonusStrength":22},"8993":{"name":"Kalrag","hitpoints":78,"combatLevel":89,"attackSpeed":4,"attackLevel":78,"strengthLevel":78,"defenceLevel":78,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":17,"crushDef":12,"rangeDef":13,"magicDef":13},"8994":{"name":"Othainian","hitpoints":87,"combatLevel":91,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":77,"rangeLevel":1,"magicLevel":1},"8995":{"name":"Doomion","hitpoints":87,"combatLevel":91,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":77,"rangeLevel":1,"magicLevel":1},"8996":{"name":"Holthion","hitpoints":87,"combatLevel":91,"attackSpeed":4,"attackLevel":76,"strengthLevel":78,"defenceLevel":77,"rangeLevel":1,"magicLevel":1},"8997":{"name":"Disciple of Iban","hitpoints":20,"combatLevel":13,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":12,"rangeLevel":1,"magicLevel":1},"9008":{"name":"Mourner","hitpoints":19,"combatLevel":13,"attackSpeed":4,"attackLevel":10,"strengthLevel":10,"defenceLevel":10,"rangeLevel":1,"magicLevel":1,"stabDef":3,"slashDef":2,"crushDef":4},"9013":{"name":"Mourner","hitpoints":19,"combatLevel":11,"attackSpeed":4,"attackLevel":8,"strengthLevel":8,"defenceLevel":8,"rangeLevel":1,"magicLevel":1,"stabDef":6,"slashDef":6,"crushDef":9,"bonusAttack":6,"bonusStrength":2},"9016":{"name":"Essyllt","hitpoints":320,"combatLevel":236,"attackSpeed":4,"attackLevel":268,"strengthLevel":268,"defenceLevel":104,"rangeLevel":342,"magicLevel":104,"stabDef":40,"slashDef":40,"crushDef":20,"rangeDef":120,"magicDef":30,"bonusStrength":28,"bonusRangeStrength":28},"9017":{"name":"Mourner","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":80,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9021":{"name":"Crystalline Hunllef","hitpoints":600,"combatLevel":674,"attackSpeed":5,"attackLevel":600,"strengthLevel":240,"defenceLevel":240,"rangeLevel":240,"magicLevel":240,"range":76,"magic":76,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusAttack":76,"bonusStrength":64,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9022":{"name":"Crystalline Hunllef","hitpoints":600,"combatLevel":674,"attackSpeed":5,"attackLevel":600,"strengthLevel":240,"defenceLevel":240,"rangeLevel":240,"magicLevel":240,"range":76,"magic":76,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusAttack":76,"bonusStrength":64,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9023":{"name":"Crystalline Hunllef","hitpoints":600,"combatLevel":674,"attackSpeed":5,"attackLevel":600,"strengthLevel":240,"defenceLevel":240,"rangeLevel":240,"magicLevel":240,"range":76,"magic":76,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusAttack":76,"bonusStrength":64,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9024":{"name":"Crystalline Hunllef","hitpoints":600,"combatLevel":674,"attackSpeed":5,"attackLevel":600,"strengthLevel":240,"defenceLevel":240,"rangeLevel":240,"magicLevel":240,"range":76,"magic":76,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusAttack":76,"bonusStrength":64,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9026":{"name":"Crystalline Rat","hitpoints":14,"combatLevel":24,"attackSpeed":4,"attackLevel":28,"strengthLevel":32,"defenceLevel":6,"magicLevel":6,"poisonImmune":true,"venomImmune":true},"9028":{"name":"Crystalline Bat","hitpoints":14,"combatLevel":33,"attackSpeed":4,"attackLevel":24,"strengthLevel":68,"defenceLevel":2,"magicLevel":2,"poisonImmune":true,"venomImmune":true},"9029":{"name":"Crystalline Unicorn","hitpoints":44,"combatLevel":48,"attackSpeed":4,"attackLevel":50,"strengthLevel":48,"defenceLevel":24,"magicLevel":24,"poisonImmune":true,"venomImmune":true},"9030":{"name":"Crystalline Scorpion","hitpoints":38,"combatLevel":64,"attackSpeed":4,"attackLevel":48,"strengthLevel":106,"defenceLevel":18,"magicLevel":18,"poisonImmune":true,"venomImmune":true},"9031":{"name":"Crystalline Wolf","hitpoints":52,"combatLevel":74,"attackSpeed":4,"attackLevel":106,"strengthLevel":66,"defenceLevel":22,"magicLevel":22,"poisonImmune":true,"venomImmune":true},"9032":{"name":"Crystalline Bear","hitpoints":100,"combatLevel":172,"attackSpeed":4,"attackLevel":98,"strengthLevel":98,"defenceLevel":98,"rangeLevel":98,"magicLevel":98,"bonusAttack":54,"bonusStrength":106,"poisonImmune":true,"venomImmune":true},"9033":{"name":"Crystalline Dragon","hitpoints":100,"combatLevel":172,"attackSpeed":4,"attackLevel":98,"strengthLevel":98,"defenceLevel":98,"rangeLevel":98,"magicLevel":98,"magic":54,"bonusMagicDamage":106,"poisonImmune":true,"venomImmune":true},"9034":{"name":"Crystalline Dark Beast","hitpoints":100,"combatLevel":172,"attackSpeed":4,"attackLevel":98,"strengthLevel":98,"defenceLevel":98,"rangeLevel":98,"magicLevel":98,"range":54,"bonusRangeStrength":106,"poisonImmune":true,"venomImmune":true},"9035":{"name":"Corrupted Hunllef","hitpoints":1000,"combatLevel":894,"attackSpeed":5,"attackLevel":240,"strengthLevel":240,"defenceLevel":240,"rangeLevel":240,"magicLevel":240,"range":90,"magic":90,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusAttack":90,"bonusStrength":112,"bonusRangeStrength":112,"bonusMagicDamage":112,"poisonImmune":true,"venomImmune":true},"9036":{"name":"Corrupted Hunllef","hitpoints":1000,"combatLevel":894,"attackSpeed":5,"attackLevel":240,"strengthLevel":240,"defenceLevel":240,"rangeLevel":240,"magicLevel":240,"range":90,"magic":90,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusAttack":90,"bonusStrength":112,"bonusRangeStrength":112,"bonusMagicDamage":112,"poisonImmune":true,"venomImmune":true},"9037":{"name":"Corrupted Hunllef","hitpoints":1000,"combatLevel":894,"attackSpeed":5,"attackLevel":240,"strengthLevel":240,"defenceLevel":240,"rangeLevel":240,"magicLevel":240,"range":90,"magic":90,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusAttack":90,"bonusStrength":112,"bonusRangeStrength":112,"bonusMagicDamage":112,"poisonImmune":true,"venomImmune":true},"9038":{"name":"Corrupted Hunllef","hitpoints":1000,"combatLevel":894,"attackSpeed":5,"attackLevel":240,"strengthLevel":240,"defenceLevel":240,"rangeLevel":240,"magicLevel":240,"range":90,"magic":90,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":20,"magicDef":20,"bonusAttack":90,"bonusStrength":112,"bonusRangeStrength":112,"bonusMagicDamage":112,"poisonImmune":true,"venomImmune":true},"9040":{"name":"Corrupted Rat","hitpoints":14,"combatLevel":34,"attackSpeed":4,"attackLevel":42,"strengthLevel":48,"defenceLevel":6,"magicLevel":6,"poisonImmune":true,"venomImmune":true},"9041":{"name":"Corrupted Spider","hitpoints":12,"combatLevel":32,"attackSpeed":4,"attackLevel":48,"strengthLevel":39,"defenceLevel":4,"magicLevel":4,"poisonImmune":true,"venomImmune":true},"9042":{"name":"Corrupted Bat","combatLevel":48,"attackSpeed":4,"attackLevel":36,"strengthLevel":102,"defenceLevel":2,"magicLevel":2,"poisonImmune":true,"venomImmune":true},"9043":{"name":"Corrupted Unicorn","hitpoints":44,"combatLevel":64,"attackSpeed":4,"attackLevel":75,"strengthLevel":72,"defenceLevel":24,"magicLevel":24,"poisonImmune":true,"venomImmune":true},"9044":{"name":"Corrupted Scorpion","hitpoints":38,"combatLevel":89,"attackSpeed":4,"attackLevel":72,"strengthLevel":159,"defenceLevel":18,"magicLevel":18,"poisonImmune":true,"venomImmune":true},"9045":{"name":"Corrupted Wolf","hitpoints":52,"combatLevel":102,"attackSpeed":4,"attackLevel":159,"strengthLevel":99,"defenceLevel":22,"magicLevel":22,"poisonImmune":true,"venomImmune":true},"9046":{"name":"Corrupted Bear","hitpoints":100,"combatLevel":258,"attackSpeed":4,"attackLevel":147,"strengthLevel":147,"defenceLevel":98,"rangeLevel":147,"magicLevel":98,"bonusAttack":81,"bonusStrength":132,"poisonImmune":true,"venomImmune":true},"9047":{"name":"Corrupted Dragon","hitpoints":100,"combatLevel":258,"attackSpeed":4,"attackLevel":147,"strengthLevel":147,"defenceLevel":98,"rangeLevel":147,"magicLevel":98,"magic":146,"bonusMagicDamage":226,"poisonImmune":true,"venomImmune":true},"9048":{"name":"Corrupted Dark Beast","hitpoints":100,"combatLevel":258,"attackSpeed":4,"attackLevel":147,"strengthLevel":147,"defenceLevel":98,"rangeLevel":147,"magicLevel":98,"range":81,"bonusRangeStrength":132,"poisonImmune":true,"venomImmune":true},"9049":{"name":"Zalcano","hitpoints":1000,"combatLevel":336,"attackSpeed":5,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"9051":{"name":"Golem","hitpoints":14,"combatLevel":64},"9118":{"name":"Rabbit","hitpoints":2000,"combatLevel":2,"attackSpeed":3,"attackLevel":600,"strengthLevel":200,"defenceLevel":450,"rangeLevel":1,"magicLevel":300,"stabDef":150,"slashDef":150,"crushDef":150,"rangeDef":200,"magicDef":150,"bonusAttack":80,"bonusStrength":60},"9181":{"name":"Dire Wolf","hitpoints":74,"combatLevel":72,"attackSpeed":4,"attackLevel":60,"strengthLevel":61,"defenceLevel":62,"rangeLevel":1,"magicLevel":1},"9182":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9183":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9184":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9185":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9186":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9187":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9188":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9189":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9190":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9191":{"name":"Guard","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":80,"defenceLevel":95,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9247":{"name":"Essyllt (hard)","hitpoints":320,"combatLevel":395,"attackSpeed":4,"attackLevel":408,"strengthLevel":408,"defenceLevel":104,"rangeLevel":512,"magicLevel":104,"range":168,"stabDef":40,"slashDef":40,"crushDef":20,"rangeDef":120,"magicDef":30,"bonusAttack":14,"bonusStrength":28,"bonusRangeStrength":28},"9249":{"name":"Essyllt (hard)","hitpoints":320,"combatLevel":395,"attackSpeed":4,"attackLevel":408,"strengthLevel":408,"defenceLevel":104,"rangeLevel":512,"magicLevel":104,"range":168,"stabDef":40,"slashDef":40,"crushDef":20,"rangeDef":120,"magicDef":30,"bonusAttack":14,"bonusStrength":28,"bonusRangeStrength":28},"9258":{"name":"Basilisk Sentinel","hitpoints":520,"combatLevel":358,"slayerLevel":60,"attackSpeed":4,"attackLevel":274,"strengthLevel":274,"defenceLevel":274,"rangeLevel":1,"magicLevel":274,"stabDef":50,"slashDef":50,"crushDef":10,"rangeDef":10,"magicDef":50,"bonusAttack":12,"bonusMagicDamage":12},"9282":{"name":"Basilisk Youngling","hitpoints":60,"combatLevel":57,"slayerLevel":40,"attackSpeed":4,"attackLevel":25,"strengthLevel":40,"defenceLevel":70,"rangeLevel":1,"magicLevel":1,"stabDef":20,"slashDef":20,"magicDef":20},"9287":{"name":"Monstrous Basilisk","hitpoints":170,"combatLevel":135,"slayerLevel":40,"attackSpeed":4,"attackLevel":88,"strengthLevel":98,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":35,"magicDef":35,"poisonImmune":true,"venomImmune":true},"9288":{"name":"Monstrous Basilisk","hitpoints":170,"combatLevel":135,"slayerLevel":40,"attackSpeed":4,"attackLevel":88,"strengthLevel":98,"defenceLevel":130,"rangeLevel":1,"magicLevel":1,"stabDef":35,"slashDef":35,"magicDef":35,"poisonImmune":true,"venomImmune":true},"9289":{"name":"The Jormungand","hitpoints":600,"combatLevel":363,"slayerLevel":40,"attackSpeed":5,"attackLevel":214,"strengthLevel":214,"defenceLevel":214,"rangeLevel":214,"magicLevel":214,"stabDef":50,"slashDef":50,"crushDef":10,"rangeDef":50,"magicDef":50},"9290":{"name":"The Jormungand","hitpoints":600,"combatLevel":363,"slayerLevel":40,"attackSpeed":5,"attackLevel":214,"strengthLevel":214,"defenceLevel":214,"rangeLevel":214,"magicLevel":214,"stabDef":50,"slashDef":50,"crushDef":10,"rangeDef":50,"magicDef":50},"9291":{"name":"The Jormungand","hitpoints":600,"combatLevel":363,"slayerLevel":40,"attackSpeed":5,"attackLevel":214,"strengthLevel":214,"defenceLevel":214,"rangeLevel":214,"magicLevel":214,"stabDef":50,"slashDef":50,"crushDef":10,"rangeDef":50,"magicDef":50},"9292":{"name":"The Jormungand","hitpoints":600,"combatLevel":363,"slayerLevel":40,"attackSpeed":5,"attackLevel":214,"strengthLevel":214,"defenceLevel":214,"rangeLevel":214,"magicLevel":214,"stabDef":50,"slashDef":50,"crushDef":10,"rangeDef":50,"magicDef":50},"9293":{"name":"Basilisk Knight","hitpoints":300,"combatLevel":204,"slayerLevel":60,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":186,"rangeLevel":1,"magicLevel":186,"stabDef":30,"slashDef":30,"magicDef":30},"9295":{"name":"Typhor","hitpoints":360,"combatLevel":218,"slayerLevel":40,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":186,"rangeLevel":1,"magicLevel":186,"stabDef":30,"slashDef":30,"magicDef":30,"poisonImmune":true,"venomImmune":true},"9296":{"name":"Typhor","hitpoints":360,"combatLevel":218,"slayerLevel":40,"attackSpeed":4,"attackLevel":186,"strengthLevel":186,"defenceLevel":186,"rangeLevel":1,"magicLevel":186,"stabDef":30,"slashDef":30,"magicDef":30,"poisonImmune":true,"venomImmune":true},"9415":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9416":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9417":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9418":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9419":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9420":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9421":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9422":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9423":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9424":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9425":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9426":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9427":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9428":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9429":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9430":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9431":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9432":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9433":{"name":"The Nightmare","hitpoints":2400,"combatLevel":814,"attackSpeed":6,"attackLevel":150,"strengthLevel":150,"defenceLevel":150,"rangeLevel":150,"magicLevel":150,"range":140,"magic":140,"stabDef":120,"slashDef":180,"crushDef":40,"rangeDef":600,"magicDef":600,"bonusAttack":140,"bonusStrength":128,"bonusRangeStrength":64,"bonusMagicDamage":64,"poisonImmune":true,"venomImmune":true},"9446":{"name":"Sleepwalker","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1},"9447":{"name":"Sleepwalker","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1},"9448":{"name":"Sleepwalker","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1},"9449":{"name":"Sleepwalker","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1},"9450":{"name":"Sleepwalker","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1},"9451":{"name":"Sleepwalker","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1},"9452":{"name":"Parasite","hitpoints":40,"combatLevel":86,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":40,"rangeLevel":60,"magicLevel":60,"range":12,"magic":12,"stabDef":50,"slashDef":70,"crushDef":10,"rangeDef":100,"magicDef":100,"bonusRangeStrength":26,"bonusMagicDamage":26},"9453":{"name":"Parasite","hitpoints":40,"combatLevel":57,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":40,"rangeLevel":60,"magicLevel":60,"range":12,"magic":12,"stabDef":50,"slashDef":70,"crushDef":10,"rangeDef":100,"magicDef":100,"bonusRangeStrength":26,"bonusMagicDamage":26},"9454":{"name":"Husk","hitpoints":20,"combatLevel":48,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":20,"rangeLevel":80,"magicLevel":80,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":80,"magicDef":80},"9455":{"name":"Husk","hitpoints":20,"combatLevel":48,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":20,"rangeLevel":80,"magicLevel":80,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":80,"magicDef":80},"9465":{"name":"Infernal pyrelord","hitpoints":184,"combatLevel":134,"slayerLevel":30,"attackSpeed":4,"attackLevel":124,"strengthLevel":96,"defenceLevel":68,"rangeLevel":1,"magicLevel":1,"stabDef":18,"slashDef":18,"crushDef":18,"rangeDef":18},"9466":{"name":"Husk","hitpoints":20,"combatLevel":104,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":20,"magicLevel":80,"magic":168,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":80,"magicDef":80,"bonusMagicDamage":82},"9467":{"name":"Husk","hitpoints":20,"combatLevel":104,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":20,"rangeLevel":80,"range":168,"stabDef":20,"slashDef":20,"crushDef":20,"rangeDef":80,"magicDef":80,"bonusRangeStrength":82},"9468":{"name":"Parasite","hitpoints":80,"combatLevel":129,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":60,"rangeLevel":60,"magicLevel":60,"range":85,"magic":84,"stabDef":60,"slashDef":80,"crushDef":20,"rangeDef":120,"magicDef":120,"bonusRangeStrength":102,"bonusMagicDamage":102},"9469":{"name":"Parasite","hitpoints":40,"combatLevel":91,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":40,"rangeLevel":60,"magicLevel":60,"range":44,"magic":44,"stabDef":50,"slashDef":70,"crushDef":10,"rangeDef":100,"magicDef":100,"bonusRangeStrength":62,"bonusMagicDamage":62},"9470":{"name":"Sleepwalker","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"poisonImmune":true,"venomImmune":true},"9502":{"name":"Iorwerth Warrior","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":80,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9503":{"name":"Iorwerth Warrior","hitpoints":105,"combatLevel":108,"attackSpeed":4,"attackLevel":95,"strengthLevel":95,"defenceLevel":80,"magicLevel":1,"stabDef":50,"slashDef":70,"crushDef":70,"rangeDef":50,"magicDef":60},"9573":{"name":"Acidic Bloodveld","hitpoints":5,"combatLevel":56,"attackSpeed":4,"attackLevel":80,"strengthLevel":46,"defenceLevel":2,"rangeLevel":1,"magicLevel":1},"9609":{"name":"Seagull","hitpoints":10,"combatLevel":3,"attackSpeed":4,"attackLevel":1,"strengthLevel":1,"defenceLevel":1,"rangeLevel":1,"magicLevel":1},"9727":{"name":"Vampyre Juvinate","hitpoints":85,"combatLevel":54,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"9728":{"name":"Vampyre Juvinate","hitpoints":85,"combatLevel":54,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":40,"rangeLevel":1,"magicLevel":1},"9731":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"9732":{"name":"Vampyre Juvenile","hitpoints":60,"combatLevel":45,"attackSpeed":4,"attackLevel":40,"strengthLevel":30,"defenceLevel":30,"rangeLevel":1,"magicLevel":1},"9735":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"9736":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"9737":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"9738":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"9739":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"9740":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"9741":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"9742":{"name":"Vyrewatch","hitpoints":90,"combatLevel":105,"attackSpeed":8,"attackLevel":105,"strengthLevel":86,"defenceLevel":85,"rangeLevel":105,"magicLevel":105},"9756":{"name":"Vyrewatch Sentinel","hitpoints":150,"combatLevel":151,"slayerLevel":38,"attackSpeed":4,"attackLevel":120,"strengthLevel":160,"defenceLevel":180,"rangeLevel":120,"magicLevel":120,"bonusAttack":135,"bonusStrength":150},"9757":{"name":"Vyrewatch Sentinel","hitpoints":150,"combatLevel":151,"slayerLevel":38,"attackSpeed":4,"attackLevel":120,"strengthLevel":160,"defenceLevel":180,"rangeLevel":120,"magicLevel":120,"bonusAttack":135,"bonusStrength":150},"9758":{"name":"Vyrewatch Sentinel","hitpoints":150,"combatLevel":151,"slayerLevel":38,"attackSpeed":4,"attackLevel":120,"strengthLevel":160,"defenceLevel":180,"rangeLevel":120,"magicLevel":120,"bonusAttack":135,"bonusStrength":150},"9759":{"name":"Vyrewatch Sentinel","hitpoints":150,"combatLevel":151,"slayerLevel":38,"attackSpeed":4,"attackLevel":120,"strengthLevel":160,"defenceLevel":180,"rangeLevel":120,"magicLevel":120,"bonusAttack":135,"bonusStrength":150},"9760":{"name":"Vyrewatch Sentinel","hitpoints":150,"combatLevel":151,"slayerLevel":38,"attackSpeed":4,"attackLevel":120,"strengthLevel":160,"defenceLevel":180,"rangeLevel":120,"magicLevel":120,"bonusAttack":135,"bonusStrength":150},"9761":{"name":"Vyrewatch Sentinel","hitpoints":150,"combatLevel":151,"slayerLevel":38,"attackSpeed":4,"attackLevel":120,"strengthLevel":160,"defenceLevel":180,"rangeLevel":120,"magicLevel":120,"bonusAttack":135,"bonusStrength":150},"9762":{"name":"Vyrewatch Sentinel","hitpoints":150,"combatLevel":151,"slayerLevel":38,"attackSpeed":4,"attackLevel":120,"strengthLevel":160,"defenceLevel":180,"rangeLevel":120,"magicLevel":120,"bonusAttack":135,"bonusStrength":150},"9763":{"name":"Vyrewatch Sentinel","hitpoints":150,"combatLevel":151,"slayerLevel":38,"attackSpeed":4,"attackLevel":120,"strengthLevel":160,"defenceLevel":180,"rangeLevel":120,"magicLevel":120,"bonusAttack":135,"bonusStrength":150},"10374":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"10375":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"10376":{"name":"Hill Giant","hitpoints":35,"combatLevel":28,"attackSpeed":6,"attackLevel":18,"strengthLevel":22,"defenceLevel":26,"rangeLevel":1,"magicLevel":1,"bonusAttack":18,"bonusStrength":16},"10397":{"name":"Spiked Turoth","hitpoints":195,"combatLevel":244,"slayerLevel":55,"attackSpeed":4,"attackLevel":204,"strengthLevel":280,"defenceLevel":154,"rangeLevel":1,"magicLevel":1,"slashDef":40,"crushDef":40},"10398":{"name":"Shadow Wyrm","hitpoints":320,"combatLevel":267,"slayerLevel":62,"attackSpeed":4,"attackLevel":300,"strengthLevel":180,"defenceLevel":125,"rangeLevel":125,"magicLevel":160,"stabDef":20,"slashDef":100,"crushDef":100,"rangeDef":20,"magicDef":50,"bonusStrength":30,"bonusMagicDamage":30},"10400":{"name":"Guardian Drake","hitpoints":630,"combatLevel":386,"slayerLevel":84,"attackLevel":270,"strengthLevel":280,"defenceLevel":200,"rangeLevel":240,"magicLevel":300,"range":40,"stabDef":60,"slashDef":100,"crushDef":100,"rangeDef":150,"magicDef":20,"bonusStrength":12},"10402":{"name":"Colossal Hydra","hitpoints":750,"combatLevel":309,"slayerLevel":95,"attackLevel":1,"strengthLevel":1,"defenceLevel":100,"rangeLevel":250,"magicLevel":250,"stabDef":100,"slashDef":200,"crushDef":200,"rangeDef":20,"magicDef":200},"10435":{"name":"Pig Thing","hitpoints":40,"combatLevel":37,"attackLevel":35,"strengthLevel":30,"defenceLevel":25,"rangeLevel":35,"magicLevel":25,"range":35,"magic":25,"slashDef":30,"crushDef":10,"magicDef":30,"bonusAttack":18,"bonusStrength":16,"bonusRangeStrength":20},"10436":{"name":"Pig Thing","hitpoints":40,"combatLevel":37,"attackLevel":35,"strengthLevel":30,"defenceLevel":25,"rangeLevel":35,"magicLevel":25,"range":35,"magic":25,"slashDef":30,"crushDef":10,"magicDef":30,"bonusAttack":18,"bonusStrength":16,"bonusRangeStrength":20},"10437":{"name":"Pig Thing","hitpoints":40,"combatLevel":37,"attackLevel":35,"strengthLevel":30,"defenceLevel":25,"rangeLevel":35,"magicLevel":25,"range":35,"magic":25,"slashDef":30,"crushDef":10,"magicDef":30,"bonusAttack":18,"bonusStrength":16,"bonusRangeStrength":20}} \ No newline at end of file diff --git a/runelite-client/src/main/resources/openosrs.properties b/runelite-client/src/main/resources/openosrs.properties new file mode 100644 index 0000000000..ad6f017739 --- /dev/null +++ b/runelite-client/src/main/resources/openosrs.properties @@ -0,0 +1 @@ +oprs.version=@open.osrs.version@ \ No newline at end of file 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 d3444285ec..644a1346d3 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -609,16 +609,6 @@ public abstract class RSClientMixin implements RSClient return getWidget(groupId, childId); } - @Inject - @Override - public Widget getWidget(com.openosrs.api.widgets.WidgetInfo widget) - { - int groupId = widget.getGroupId(); - int childId = widget.getChildId(); - - return getWidget(groupId, childId); - } - @Inject @Override public Widget getWidget(int id) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java index 2200710ada..72d008feaa 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java @@ -46,8 +46,8 @@ import static net.runelite.api.SkullIcon.SKULL; import static net.runelite.api.SkullIcon.SKULL_FIGHT_PIT; import net.runelite.api.coords.LocalPoint; import net.runelite.api.events.PlayerChanged; -import net.runelite.api.events.player.headicon.OverheadPrayerChanged; -import net.runelite.api.events.player.headicon.PlayerSkullChanged; +import net.runelite.api.events.OverheadPrayerChanged; +import net.runelite.api.events.PlayerSkullChanged; import net.runelite.api.mixins.Copy; import net.runelite.api.mixins.FieldHook; import net.runelite.api.mixins.Inject; @@ -143,23 +143,7 @@ public abstract class RSPlayerMixin implements RSPlayer @Inject private HeadIcon getHeadIcon(int overheadIcon) { - switch (overheadIcon) - { - case 0: - return MELEE; - case 1: - return RANGED; - case 2: - return MAGIC; - case 3: - return RETRIBUTION; - case 4: - return SMITE; - case 5: - return REDEMPTION; - default: - return null; - } + return HeadIcon.values()[overheadIcon]; } @Inject From 8c72f10b650eb35e5bc3651fe3eefea647a13dd9 Mon Sep 17 00:00:00 2001 From: zeruth Date: Thu, 4 Feb 2021 02:13:44 -0500 Subject: [PATCH 022/133] externals: update plugin list after install --- .../client/plugins/ExternalPluginManager.java | 11 +++++++---- .../main/java/net/runelite/client/RuneLite.java | 2 +- .../runelite/client/plugins/PluginManager.java | 17 +++++++++++++++-- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginManager.java b/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginManager.java index add932f725..5b965f37b9 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginManager.java +++ b/runelite-client/src/main/java/com/openosrs/client/plugins/ExternalPluginManager.java @@ -75,6 +75,7 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; import net.runelite.client.events.ConfigChanged; +import net.runelite.client.events.ExternalPluginsChanged; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.PluginInstantiationException; import net.runelite.client.plugins.PluginManager; @@ -421,9 +422,9 @@ public class ExternalPluginManager .directed() .build(); - for (Plugin plugin : plugins) + for (net.runelite.client.plugins.Plugin plugin : plugins) { - Class clazz = plugin.getClass(); + Class clazz = plugin.getClass(); PluginDescriptor pluginDescriptor = clazz.getAnnotation(PluginDescriptor.class); try @@ -820,6 +821,8 @@ public class ExternalPluginManager groups.broadcastSring("STARTEXTERNAL;" + pluginId); scanAndInstantiate(loadPlugin(pluginId), true, false); + ExternalPluginsChanged event = new ExternalPluginsChanged(null); + eventBus.post(event); return true; } @@ -853,9 +856,9 @@ public class ExternalPluginManager } updateManager.installPlugin(pluginId, null); - scanAndInstantiate(loadPlugin(pluginId), true, true); - + ExternalPluginsChanged event = new ExternalPluginsChanged(null); + eventBus.post(event); groups.broadcastSring("STARTEXTERNAL;" + pluginId); } catch (DependencyResolver.DependenciesNotFoundException ex) 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 f2b4a9ac8d..aa7c7aa45e 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -95,7 +95,7 @@ public class RuneLite { public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".openosrs"); public static final File CACHE_DIR = new File(RUNELITE_DIR, "cache"); - public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugins"); + public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugins-hub"); public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles"); public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots"); public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs"); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index b4d5934f2f..01c2d9ba7d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -164,8 +164,21 @@ public class PluginManager { try { - final Injector injector = plugin.getInjector(); - + Injector injector = plugin.getInjector(); + if (injector == null) + { + // Create injector for the module + Module pluginModule = (Binder binder) -> + { + // Since the plugin itself is a module, it won't bind itself, so we'll bind it here + binder.bind((Class) plugin.getClass()).toInstance(plugin); + binder.install(plugin); + }; + Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule); + pluginInjector.injectMembers(plugin); + plugin.injector = pluginInjector; + injector = pluginInjector; + } for (Key key : injector.getBindings().keySet()) { Class type = key.getTypeLiteral().getRawType(); From 4023066b5d812b06c9e13980c215a026a29882f5 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 4 Feb 2021 07:16:17 +0000 Subject: [PATCH 023/133] 2/n --- .../api/events/MenuOptionClicked.java | 86 +++----- .../openosrs/client/game/SoundManager.java | 3 +- .../openosrs/client/game/WorldLocation.java | 35 ++- .../client/ui/components/InfoPanel.java | 2 +- .../com/openosrs/client/util/LinkBrowser.java | 83 ------- .../com/openosrs/client/util/MiscUtils.java | 202 ------------------ .../com/openosrs/client/util/PvPUtil.java | 135 ------------ .../com/openosrs/client/util/SwingUtil.java | 19 -- ...heduledExecutorServiceExceptionLogger.java | 27 ++- .../net/runelite/client/RuneLiteModule.java | 1 - .../plugins/OPRSExternalPluginManager.java | 30 ++- .../client/plugins/PluginManager.java | 5 - .../plugins/openosrs/OpenOSRSPlugin.java | 8 +- .../openosrs/externals/ExternalBox.java | 2 +- .../externals/ExternalPluginManagerPanel.java | 2 +- .../openosrs/externals/PluginsPanel.java | 2 +- .../openosrs/externals/RepositoryBox.java | 2 +- .../openosrs/externals/RepositoryPanel.java | 2 +- .../net/runelite/client/util/ImageUtil.java | 130 +++++++++++ .../net/runelite/client/util/LinkBrowser.java | 50 +++++ .../net/runelite/client/util/PvPUtil.java | 3 +- .../net/runelite/client/util/SwingUtil.java | 16 ++ .../plugins/openosrs/externalmanager_icon.png | Bin .../plugins/openosrs/externals/add_icon.png | Bin .../openosrs/externals/add_raw_icon.png | Bin .../openosrs/externals/delete_icon.png | Bin .../openosrs/externals/discord_icon.png | Bin .../plugins/openosrs/externals/gh_icon.png | Bin .../net/runelite/mixins/RSClientMixin.java | 39 ++-- 29 files changed, 329 insertions(+), 555 deletions(-) delete mode 100644 runelite-client/src/main/java/com/openosrs/client/util/LinkBrowser.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/util/MiscUtils.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/util/PvPUtil.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/util/SwingUtil.java rename runelite-client/src/main/java/{com/openosrs/client/util => net/runelite/client}/NonScheduledExecutorServiceExceptionLogger.java (65%) rename runelite-client/src/main/java/{com/openosrs => net/runelite}/client/plugins/openosrs/OpenOSRSPlugin.java (95%) rename runelite-client/src/main/java/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/ExternalBox.java (98%) rename runelite-client/src/main/java/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java (99%) rename runelite-client/src/main/java/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/PluginsPanel.java (99%) rename runelite-client/src/main/java/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/RepositoryBox.java (98%) rename runelite-client/src/main/java/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/RepositoryPanel.java (96%) rename runelite-client/src/main/resources/{com/openosrs => net/runelite}/client/plugins/openosrs/externalmanager_icon.png (100%) rename runelite-client/src/main/resources/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/add_icon.png (100%) rename runelite-client/src/main/resources/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/add_raw_icon.png (100%) rename runelite-client/src/main/resources/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/delete_icon.png (100%) rename runelite-client/src/main/resources/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/discord_icon.png (100%) rename runelite-client/src/main/resources/{com/openosrs => net/runelite}/client/plugins/openosrs/externals/gh_icon.png (100%) diff --git a/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java b/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java index 80a11edc30..2cd15c48f9 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java +++ b/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java @@ -24,10 +24,8 @@ */ package net.runelite.api.events; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import net.runelite.api.MenuEntry; +import lombok.Data; +import net.runelite.api.MenuAction; /** * An event where a menu option has been clicked. @@ -40,22 +38,35 @@ import net.runelite.api.MenuEntry; * By default, when there is no action performed when left-clicking, * it seems that this event still triggers with the "Cancel" action. */ -@Getter -public class MenuOptionClicked extends MenuEntry +@Data +public class MenuOptionClicked { - public MenuOptionClicked(String option, String target, int identifier, int opcode, int param0, int param1, boolean forceLeftClick) - { - super(option, target, identifier, opcode, param0, param1, forceLeftClick); - authentic = true; - } - - public MenuOptionClicked(String option, String target, int identifier, int opcode, int param0, int param1, boolean forceLeftClick, boolean authentic, int mouseButton) - { - super(option, target, identifier, opcode, param0, param1, forceLeftClick); - this.authentic = authentic; - this.mouseButton = mouseButton; - } - + /** + * The action parameter used in the click. + */ + private int actionParam; + /** + * The option text added to the menu. + */ + private String menuOption; + /** + * The target of the action. + */ + private String menuTarget; + /** + * The action performed. + */ + private MenuAction menuAction; + /** + * The ID of the object, actor, or item that the interaction targets. + */ + private int id; + /** + * The ID of the widget where the menu was clicked. + * + * @see net.runelite.api.widgets.WidgetID + */ + private int widgetId; /** * The selected item index at the time of the option click. */ @@ -65,11 +76,6 @@ public class MenuOptionClicked extends MenuEntry */ private boolean consumed; - /** - * The mouse button will be 1 if a non draggable widget was clicked, - */ - private int mouseButton; - /** * Marks the event as having been consumed. *

@@ -81,36 +87,4 @@ public class MenuOptionClicked extends MenuEntry { this.consumed = true; } - - /** - * Whether or not the event is authentic. - */ - @Setter(AccessLevel.NONE) - private final boolean authentic; - - public void setMenuEntry(MenuEntry e) - { - setOption(e.getOption()); - setTarget(e.getTarget()); - setIdentifier(e.getIdentifier()); - setOpcode(e.getOpcode()); - setActionParam(e.getActionParam()); - setActionParam1(e.getActionParam1()); - setForceLeftClick(e.isForceLeftClick()); - } - - public int getWidgetId() - { - return getActionParam1(); - } - - public String getMenuTarget() - { - return getTarget(); - } - - public String getMenuOption() - { - return getOption(); - } } diff --git a/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java b/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java index 0f46afb1f8..058c575793 100644 --- a/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java +++ b/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java @@ -28,11 +28,10 @@ public class SoundManager this.runeliteConfig = runeLiteConfig; } - public void playSound(final Sound sound) + public void play(final Sound sound) { new Thread(new Runnable() { - @Override public void run() { diff --git a/runelite-client/src/main/java/com/openosrs/client/game/WorldLocation.java b/runelite-client/src/main/java/com/openosrs/client/game/WorldLocation.java index 5a65f8ec98..ce347a1b26 100644 --- a/runelite-client/src/main/java/com/openosrs/client/game/WorldLocation.java +++ b/runelite-client/src/main/java/com/openosrs/client/game/WorldLocation.java @@ -1,13 +1,28 @@ -/******************************************************************************* - * Copyright (c) 2019 openosrs - * Redistributions and modifications of this software are permitted as long as this notice remains in its original unmodified state at the top of this file. - * If there are any questions comments, or feedback about this software, please direct all inquiries directly to the file authors: - * ST0NEWALL#9112 - * Macweese#1169 UID 159941566994186240, macweese@pm.me - * openosrs Discord: https://discord.gg/Q7wFtCe - * openosrs website: https://openosrs.com - ******************************************************************************/ - +/* + * Copyright (c) 2019, ST0NEWALL + * Copyright (c) 2020, Macweese + * 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 com.openosrs.client.game; import com.google.common.collect.ImmutableMap; diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/components/InfoPanel.java b/runelite-client/src/main/java/com/openosrs/client/ui/components/InfoPanel.java index 7d5043bd55..f0d0fa1b2f 100644 --- a/runelite-client/src/main/java/com/openosrs/client/ui/components/InfoPanel.java +++ b/runelite-client/src/main/java/com/openosrs/client/ui/components/InfoPanel.java @@ -47,7 +47,7 @@ import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import com.openosrs.client.ui.OpenOSRSSplashScreen; import net.runelite.client.util.ImageUtil; -import com.openosrs.client.util.LinkBrowser; +import net.runelite.client.util.LinkBrowser; @Slf4j public class InfoPanel extends JPanel diff --git a/runelite-client/src/main/java/com/openosrs/client/util/LinkBrowser.java b/runelite-client/src/main/java/com/openosrs/client/util/LinkBrowser.java deleted file mode 100644 index 64df5e1127..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/util/LinkBrowser.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.openosrs.client.util; - -import java.awt.Desktop; -import java.awt.Toolkit; -import java.awt.datatransfer.StringSelection; -import java.io.File; -import java.io.IOException; -import javax.swing.JOptionPane; -import javax.swing.SwingUtilities; -import lombok.extern.slf4j.Slf4j; - -@Slf4j -public class LinkBrowser extends net.runelite.client.util.LinkBrowser -{ - /** - * Tries to open the specified {@code File} with the systems default text editor. If operation fails - * an error message is displayed with the option to copy the absolute file path to clipboard. - * - * @param file the File instance of the log file - * @return did the file open successfully? - */ - public static boolean openLocalFile(final File file) - { - if (file == null || !file.exists()) - { - return false; - } - - if (attemptOpenLocalFile(file)) - { - log.debug("Opened log file through Desktop#edit to {}", file); - return true; - } - - showMessageBox("Unable to open log file. Press 'OK' and the file path will be copied to your clipboard", file.getAbsolutePath()); - return false; - } - - private static boolean attemptOpenLocalFile(final File file) - { - if (!Desktop.isDesktopSupported()) - { - return false; - } - - final Desktop desktop = Desktop.getDesktop(); - - if (!desktop.isSupported(Desktop.Action.OPEN)) - { - return false; - } - - try - { - desktop.open(file); - return true; - } - catch (IOException ex) - { - log.warn("Failed to open Desktop#edit {}", file, ex); - return false; - } - } - - /** - * Open swing message box with specified message and copy data to clipboard - * @param message message to show - */ - private static void showMessageBox(final String message, final String data) - { - SwingUtilities.invokeLater(() -> - { - final int result = JOptionPane.showConfirmDialog(null, message, "Message", - JOptionPane.OK_CANCEL_OPTION); - - if (result == JOptionPane.OK_OPTION) - { - final StringSelection stringSelection = new StringSelection(data); - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null); - } - }); - } -} diff --git a/runelite-client/src/main/java/com/openosrs/client/util/MiscUtils.java b/runelite-client/src/main/java/com/openosrs/client/util/MiscUtils.java deleted file mode 100644 index d5bc562121..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/util/MiscUtils.java +++ /dev/null @@ -1,202 +0,0 @@ -package com.openosrs.client.util; - -import java.awt.Polygon; -import java.net.URL; -import java.net.URLEncoder; -import java.nio.charset.StandardCharsets; -import java.time.Duration; -import java.time.temporal.ChronoUnit; -import java.util.stream.Collectors; -import java.util.stream.Stream; -import net.runelite.api.Client; -import net.runelite.api.Player; -import net.runelite.api.WorldType; -import net.runelite.api.coords.WorldPoint; - -public class MiscUtils -{ - private static final int[] abovePointsX = {2944, 3392, 3392, 2944}; - private static final int[] abovePointsY = {3523, 3523, 3971, 3971}; - private static final int[] belowPointsX = {2944, 2944, 3264, 3264}; - private static final int[] belowPointsY = {9918, 10360, 10360, 9918}; - - private static final Polygon abovePoly = new Polygon(abovePointsX, abovePointsY, abovePointsX.length); - private static final Polygon belowPoly = new Polygon(belowPointsX, belowPointsY, belowPointsX.length); - - private static final ChronoUnit[] ORDERED_CHRONOS = new ChronoUnit[] - { - ChronoUnit.YEARS, - ChronoUnit.MONTHS, - ChronoUnit.WEEKS, - ChronoUnit.DAYS, - ChronoUnit.HOURS, - ChronoUnit.MINUTES, - ChronoUnit.SECONDS - }; - - //test replacement so private for now - private static boolean inWildy(WorldPoint point) - { - if (point == null) - { - return false; - } - - return abovePoly.contains(point.getX(), point.getY()) || belowPoly.contains(point.getX(), point.getY()); - } - - public static int getWildernessLevelFrom(Client client, WorldPoint point) - { - if (client == null) - { - return 0; - } - - if (point == null) - { - return 0; - } - - int x = point.getX(); - - if (point.getPlane() == 0 && (x < 2940 || x > 3391)) - { - return 0; - } - - int y = point.getY(); - //v underground //v above ground - int wildernessLevel = clamp(y > 6400 ? ((y - 9920) / 8) + 1 : ((y - 3520) / 8) + 1, 0, 56); - - if (point.getPlane() > 0 && y < 9920) - { - wildernessLevel = 0; - } - - if (client.getWorldType().stream().anyMatch(worldType -> worldType == WorldType.PVP || worldType == WorldType.HIGH_RISK)) - { - wildernessLevel += 15; - } - - return Math.max(0, wildernessLevel); - } - - public static int clamp(int val, int min, int max) - { - return Math.max(min, Math.min(max, val)); - } - - public static float clamp(float val, float min, float max) - { - return Math.max(min, Math.min(max, val)); - } - - public static boolean inWilderness(Client client) - { - Player localPlayer = client.getLocalPlayer(); - - if (localPlayer == null) - { - return false; - } - - return inWildy(localPlayer.getWorldLocation()); - - //return getWildernessLevelFrom(client, localPlayer.getWorldLocation()) > 0; - } - - public static String formatTimeAgo(Duration dur) - { - long dA = 0, dB = 0, rm; - ChronoUnit cA = null, cB = null; - for (int i = 0; i < ORDERED_CHRONOS.length; i++) - { - cA = ORDERED_CHRONOS[i]; - dA = dur.getSeconds() / cA.getDuration().getSeconds(); - rm = dur.getSeconds() % cA.getDuration().getSeconds(); - if (dA <= 0) - { - cA = null; - continue; - } - - if (i + 1 < ORDERED_CHRONOS.length) - { - cB = ORDERED_CHRONOS[i + 1]; - dB = rm / cB.getDuration().getSeconds(); - - if (dB <= 0) - { - cB = null; - } - } - - break; - } - - if (cA == null) - { - return "just now."; - } - - String str = formatUnit(cA, dA); - - if (cB != null) - { - str += " and " + formatUnit(cB, dB); - } - - return str + " ago."; - } - - private static String formatUnit(ChronoUnit chrono, long val) - { - boolean multiple = val != 1; - String str; - if (multiple) - { - str = val + " "; - } - else - { - str = "a" + (chrono == ChronoUnit.HOURS ? "n " : " "); - } - str += chrono.name().toLowerCase(); - if (!multiple) - { - if (str.charAt(str.length() - 1) == 's') - { - str = str.substring(0, str.length() - 1); - } - } - else if (str.charAt(str.length() - 1) != 's') - { - str += "s"; - } - return str; - } - - /** - * Mostly stolen from {@link java.net.URLStreamHandler#toExternalForm(URL)} - * - * @param url URL to encode - * @return URL, with path, query and ref encoded - */ - public static String urlToStringEncoded(URL url) - { - String s; - String path = url.getPath() != null ? Stream.of(url.getPath().split("/")) - .map(s2 -> URLEncoder.encode(s2, StandardCharsets.UTF_8)).collect(Collectors.joining("/")) : ""; - return url.getProtocol() - + ':' - + (((s = url.getAuthority()) != null && s.length() > 0) ? "//" + s : "") - + (path) - + (((s = url.getQuery()) != null) ? '?' + urlEncode(s) : "") - + (((s = url.getRef()) != null) ? '#' + urlEncode(s) : ""); - } - - private static String urlEncode(String s) - { - return URLEncoder.encode(s, StandardCharsets.UTF_8); - } -} diff --git a/runelite-client/src/main/java/com/openosrs/client/util/PvPUtil.java b/runelite-client/src/main/java/com/openosrs/client/util/PvPUtil.java deleted file mode 100644 index 1f0120c75a..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/util/PvPUtil.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (c) 2019. PKLite - All Rights Reserved - * Unauthorized modification, distribution, or possession of this source file, via any medium is strictly prohibited. - * Proprietary and confidential. Refer to PKLite License file for more information on - * full terms of this copyright and to determine what constitutes authorized use. - * Written by PKLite(ST0NEWALL, others) , 2019 - * - */ -package com.openosrs.client.util; - -import net.runelite.api.Client; -import net.runelite.api.InventoryID; -import net.runelite.api.Item; -import net.runelite.api.ItemComposition; -import net.runelite.api.Player; -import net.runelite.api.Varbits; -import net.runelite.api.WorldType; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.geometry.Cuboid; -import net.runelite.client.game.ItemManager; -import net.runelite.client.util.QuantityFormatter; -import org.apache.commons.lang3.ArrayUtils; -import java.awt.Polygon; -import java.util.Comparator; -import java.util.Objects; -import java.util.TreeMap; - -public class PvPUtil -{ - private static final Polygon NOT_WILDERNESS_BLACK_KNIGHTS = new Polygon( // this is black knights castle - new int[]{2994, 2995, 2996, 2996, 2994, 2994, 2997, 2998, 2998, 2999, 3000, 3001, 3002, 3003, 3004, 3005, 3005, - 3005, 3019, 3020, 3022, 3023, 3024, 3025, 3026, 3026, 3027, 3027, 3028, 3028, 3029, 3029, 3030, 3030, 3031, - 3031, 3032, 3033, 3034, 3035, 3036, 3037, 3037}, - new int[]{3525, 3526, 3527, 3529, 3529, 3534, 3534, 3535, 3536, 3537, 3538, 3539, 3540, 3541, 3542, 3543, 3544, - 3545, 3545, 3546, 3546, 3545, 3544, 3543, 3543, 3542, 3541, 3540, 3539, 3537, 3536, 3535, 3534, 3533, 3532, - 3531, 3530, 3529, 3528, 3527, 3526, 3526, 3525}, - 43 - ); - private static final Cuboid MAIN_WILDERNESS_CUBOID = new Cuboid(2944, 3525, 0, 3391, 4351, 3); - private static final Cuboid GOD_WARS_WILDERNESS_CUBOID = new Cuboid(3008, 10112, 0, 3071, 10175, 3); - private static final Cuboid WILDERNESS_UNDERGROUND_CUBOID = new Cuboid(2944, 9920, 0, 3391, 10879, 3); - - /** - * Gets the wilderness level based on a world point - * Java reimplementation of clientscript 384 [proc,wilderness_level] - * - * @param point the point in the world to get the wilderness level for - * @return the int representing the wilderness level - */ - public static int getWildernessLevelFrom(WorldPoint point) - { - if (MAIN_WILDERNESS_CUBOID.contains(point)) - { - if (NOT_WILDERNESS_BLACK_KNIGHTS.contains(point.getX(), point.getY())) - { - return 0; - } - - return ((point.getY() - 3520) / 8) + 1; // calc(((coordz(coord) - (55 * 64)) / 8) + 1) - } - else if (GOD_WARS_WILDERNESS_CUBOID.contains(point)) - { - return ((point.getY() - 9920) / 8) - 1; // calc(((coordz(coord) - (155 * 64)) / 8) - 1) - } - else if (WILDERNESS_UNDERGROUND_CUBOID.contains(point)) - { - return ((point.getY() - 9920) / 8) + 1; // calc(((coordz(coord) - (155 * 64)) / 8) + 1) - } - return 0; - } - - /** - * Determines if another player is attackable based off of wilderness level and combat levels - * - * @param client The client of the local player - * @param player the player to determine attackability - * @return returns true if the player is attackable, false otherwise - */ - public static boolean isAttackable(Client client, Player player) - { - int wildernessLevel = 0; - - if (WorldType.isDeadmanWorld(client.getWorldType())) - { - return true; - } - if (WorldType.isPvpWorld(client.getWorldType())) - { - wildernessLevel += 15; - } - if (client.getVar(Varbits.IN_WILDERNESS) == 1) - { - wildernessLevel += getWildernessLevelFrom(client.getLocalPlayer().getWorldLocation()); - } - return wildernessLevel != 0 && Math.abs(client.getLocalPlayer().getCombatLevel() - player.getCombatLevel()) <= wildernessLevel; - } - - public static int calculateRisk(Client client, ItemManager itemManager) - { - if (client.getItemContainer(InventoryID.EQUIPMENT) == null) - { - return 0; - } - if (client.getItemContainer(InventoryID.INVENTORY).getItems() == null) - { - return 0; - } - Item[] items = ArrayUtils.addAll(Objects.requireNonNull(client.getItemContainer(InventoryID.EQUIPMENT)).getItems(), - Objects.requireNonNull(client.getItemContainer(InventoryID.INVENTORY)).getItems()); - TreeMap priceMap = new TreeMap<>(Comparator.comparingInt(Integer::intValue)); - int wealth = 0; - for (Item i : items) - { - int value = (itemManager.getItemPrice(i.getId()) * i.getQuantity()); - - final ItemComposition itemComposition = itemManager.getItemComposition(i.getId()); - if (!itemComposition.isTradeable() && value == 0) - { - value = itemComposition.getPrice() * i.getQuantity(); - priceMap.put(value, i); - } - else - { - value = itemManager.getItemPrice(i.getId()) * i.getQuantity(); - if (i.getId() > 0 && value > 0) - { - priceMap.put(value, i); - } - } - wealth += value; - } - return Integer.parseInt(QuantityFormatter.quantityToRSDecimalStack(priceMap.keySet().stream().mapToInt(Integer::intValue).sum())); - - } -} \ No newline at end of file diff --git a/runelite-client/src/main/java/com/openosrs/client/util/SwingUtil.java b/runelite-client/src/main/java/com/openosrs/client/util/SwingUtil.java deleted file mode 100644 index 799b67bfa4..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/util/SwingUtil.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.openosrs.client.util; - -import java.awt.EventQueue; -import java.lang.reflect.InvocationTargetException; - -public class SwingUtil extends net.runelite.client.util.SwingUtil -{ - public static void syncExec(final Runnable r) throws InvocationTargetException, InterruptedException - { - if (EventQueue.isDispatchThread()) - { - r.run(); - } - else - { - EventQueue.invokeAndWait(r); - } - } -} diff --git a/runelite-client/src/main/java/com/openosrs/client/util/NonScheduledExecutorServiceExceptionLogger.java b/runelite-client/src/main/java/net/runelite/client/NonScheduledExecutorServiceExceptionLogger.java similarity index 65% rename from runelite-client/src/main/java/com/openosrs/client/util/NonScheduledExecutorServiceExceptionLogger.java rename to runelite-client/src/main/java/net/runelite/client/NonScheduledExecutorServiceExceptionLogger.java index 08436116a5..219781b278 100644 --- a/runelite-client/src/main/java/com/openosrs/client/util/NonScheduledExecutorServiceExceptionLogger.java +++ b/runelite-client/src/main/java/net/runelite/client/NonScheduledExecutorServiceExceptionLogger.java @@ -1,4 +1,29 @@ -package com.openosrs.client.util; +/* + * Copyright (c) 2021, ThatGamerBlue + * 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; import java.util.Collection; import java.util.List; diff --git a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java index f673cee903..19dd5c0c65 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLiteModule.java @@ -30,7 +30,6 @@ import com.google.inject.AbstractModule; import com.google.inject.Provides; import com.google.inject.name.Names; import com.openosrs.client.config.OpenOSRSConfig; -import com.openosrs.client.util.NonScheduledExecutorServiceExceptionLogger; import java.applet.Applet; import java.io.File; import java.util.Properties; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java index 79ee6f1f53..7304a5a723 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java @@ -40,11 +40,11 @@ import com.openosrs.client.events.ExternalPluginChanged; import com.openosrs.client.events.ExternalRepositoryChanged; import com.openosrs.client.ui.OpenOSRSSplashScreen; import com.openosrs.client.util.Groups; -import com.openosrs.client.util.MiscUtils; -import com.openosrs.client.util.SwingUtil; import java.lang.reflect.InvocationTargetException; import java.net.MalformedURLException; import java.net.URL; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; import java.nio.file.Path; import java.util.ArrayList; import java.util.HashMap; @@ -76,6 +76,7 @@ import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; import net.runelite.client.events.ConfigChanged; import net.runelite.client.ui.ClientUI; +import net.runelite.client.util.SwingUtil; import org.jgroups.Message; import org.pf4j.DefaultPluginManager; import org.pf4j.DependencyResolver; @@ -343,7 +344,7 @@ public class OPRSExternalPluginManager { config.append(repository.getId()); config.append("|"); - config.append(MiscUtils.urlToStringEncoded(repository.getUrl())); + config.append(urlToStringEncoded(repository.getUrl())); config.append(";"); } config.deleteCharAt(config.lastIndexOf(";")); @@ -1066,4 +1067,27 @@ public class OPRSExternalPluginManager } } + /** + * Mostly stolen from {@link java.net.URLStreamHandler#toExternalForm(URL)} + * + * @param url URL to encode + * @return URL, with path, query and ref encoded + */ + private static String urlToStringEncoded(URL url) + { + String s; + String path = url.getPath() != null ? Stream.of(url.getPath().split("/")) + .map(s2 -> URLEncoder.encode(s2, StandardCharsets.UTF_8)).collect(Collectors.joining("/")) : ""; + return url.getProtocol() + + ':' + + (((s = url.getAuthority()) != null && s.length() > 0) ? "//" + s : "") + + (path) + + (((s = url.getQuery()) != null) ? '?' + urlEncode(s) : "") + + (((s = url.getRef()) != null) ? '#' + urlEncode(s) : ""); + } + + private static String urlEncode(String s) + { + return URLEncoder.encode(s, StandardCharsets.UTF_8); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index b4d5934f2f..292f7b7414 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -87,7 +87,6 @@ public class PluginManager * Base package where the core plugins are */ private static final String PLUGIN_PACKAGE = "net.runelite.client.plugins"; - private static final String OPENOSRS_PACKAGE = "com.openosrs.client.plugins"; private final boolean developerMode; private final boolean safeMode; @@ -291,10 +290,6 @@ public class PluginManager .map(ClassInfo::load) .collect(Collectors.toList()); - plugins.addAll(classPath.getTopLevelClassesRecursive(OPENOSRS_PACKAGE).stream() - .map(ClassInfo::load) - .collect(Collectors.toList())); - loadPlugins(plugins, (loaded, total) -> SplashScreen.stage(.60, .70, null, "Loading Plugins", loaded, total, false)); } diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/OpenOSRSPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java similarity index 95% rename from runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/OpenOSRSPlugin.java rename to runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java index 54e540d0f6..c991795cce 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/OpenOSRSPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java @@ -24,17 +24,16 @@ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * */ -package com.openosrs.client.plugins.openosrs; +package net.runelite.client.plugins.openosrs; import ch.qos.logback.classic.Logger; import com.openosrs.client.config.OpenOSRSConfig; -import com.openosrs.client.plugins.openosrs.externals.ExternalPluginManagerPanel; +import net.runelite.client.plugins.openosrs.externals.ExternalPluginManagerPanel; import java.awt.image.BufferedImage; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; -import net.runelite.client.callback.ClientThread; import net.runelite.client.config.Keybind; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; @@ -65,9 +64,6 @@ public class OpenOSRSPlugin extends Plugin @Inject private Client client; - @Inject - private ClientThread clientThread; - @Inject private ClientToolbar clientToolbar; diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalBox.java similarity index 98% rename from runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalBox.java rename to runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalBox.java index cdaef1c8cd..51c9e94b90 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalBox.java @@ -1,4 +1,4 @@ -package com.openosrs.client.plugins.openosrs.externals; +package net.runelite.client.plugins.openosrs.externals; import java.awt.BorderLayout; import java.awt.Color; diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java similarity index 99% rename from runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java rename to runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java index 57ba0b7bd1..c4c4ee8390 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java @@ -1,4 +1,4 @@ -package com.openosrs.client.plugins.openosrs.externals; +package net.runelite.client.plugins.openosrs.externals; import net.runelite.client.plugins.OPRSExternalPluginManager; import java.awt.BorderLayout; diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java similarity index 99% rename from runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java rename to runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java index f8c7377c63..99fde9cc1f 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/PluginsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java @@ -1,4 +1,4 @@ -package com.openosrs.client.plugins.openosrs.externals; +package net.runelite.client.plugins.openosrs.externals; import net.runelite.client.plugins.OPRSExternalPluginManager; import com.google.gson.JsonSyntaxException; diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java similarity index 98% rename from runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java rename to runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java index cffc8d989f..8a1aec5eaf 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java @@ -1,4 +1,4 @@ -package com.openosrs.client.plugins.openosrs.externals; +package net.runelite.client.plugins.openosrs.externals; import net.runelite.client.plugins.OPRSExternalPluginManager; import com.openosrs.client.ui.JMultilineLabel; diff --git a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryPanel.java similarity index 96% rename from runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryPanel.java rename to runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryPanel.java index 6bd5181a51..ef3d7d5724 100644 --- a/runelite-client/src/main/java/com/openosrs/client/plugins/openosrs/externals/RepositoryPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryPanel.java @@ -1,4 +1,4 @@ -package com.openosrs.client.plugins.openosrs.externals; +package net.runelite.client.plugins.openosrs.externals; import net.runelite.client.plugins.OPRSExternalPluginManager; import com.openosrs.client.events.ExternalRepositoryChanged; 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 92e3f8c773..219e0e2e48 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 @@ -577,4 +577,134 @@ public class ImageUtil } return image; } + + /** + * Draw fg centered on top of bg + */ + public static SpritePixels mergeSprites(final Client client, final SpritePixels bg, final SpritePixels fg) + { + assert fg.getHeight() <= bg.getHeight() && fg.getWidth() <= bg.getWidth() : "Background has to be larger than foreground"; + + final int[] canvas = Arrays.copyOf(bg.getPixels(), bg.getWidth() * bg.getHeight()); + final SpritePixels result = client.createSpritePixels(canvas, bg.getWidth(), bg.getHeight()); + + final int bgWid = bg.getWidth(); + final int fgHgt = fg.getHeight(); + final int fgWid = fg.getWidth(); + + final int xOffset = (bgWid - fgWid) / 2; + final int yOffset = (bg.getHeight() - fgHgt) / 2; + + final int[] fgPixels = fg.getPixels(); + + for (int y1 = yOffset, y2 = 0; y2 < fgHgt; y1++, y2++) + { + int i1 = y1 * bgWid + xOffset; + int i2 = y2 * fgWid; + + for (int x = 0; x < fgWid; x++, i1++, i2++) + { + if (fgPixels[i2] > 0) + { + canvas[i1] = fgPixels[i2]; + } + } + } + + return result; + } + + /** + * Resize Sprite sprite to given width (newW) and height (newH) + */ + public static SpritePixels resizeSprite(final Client client, final SpritePixels sprite, int newW, int newH) + { + assert newW > 0 && newH > 0; + + final int oldW = sprite.getWidth(); + final int oldH = sprite.getHeight(); + + if (oldW == newW && oldH == newH) + { + return sprite; + } + + final int[] canvas = new int[newW * newH]; + final int[] pixels = sprite.getPixels(); + + final SpritePixels result = client.createSpritePixels(canvas, newW, newH); + + int pixelX = 0; + int pixelY = 0; + + final int oldMaxW = sprite.getMaxWidth(); + final int oldMaxH = sprite.getMaxHeight(); + + final int pixelW = (oldMaxW << 16) / newW; + final int pixelH = (oldMaxH << 16) / newH; + + int xOffset = 0; + int yOffset = 0; + + int canvasIdx; + if (sprite.getOffsetX() > 0) + { + canvasIdx = (pixelW + (sprite.getOffsetX() << 16) - 1) / pixelW; + xOffset += canvasIdx; + pixelX += canvasIdx * pixelW - (sprite.getOffsetX() << 16); + } + + if (sprite.getOffsetY() > 0) + { + canvasIdx = (pixelH + (sprite.getOffsetY() << 16) - 1) / pixelH; + yOffset += canvasIdx; + pixelY += canvasIdx * pixelH - (sprite.getOffsetY() << 16); + } + + if (oldW < oldMaxW) + { + newW = (pixelW + ((oldW << 16) - pixelX) - 1) / pixelW; + } + + if (oldH < oldMaxH) + { + newH = (pixelH + ((oldH << 16) - pixelY) - 1) / pixelH; + } + + canvasIdx = xOffset + yOffset * newW; + int canvasOffset = 0; + if (yOffset + newH > newH) + { + newH -= yOffset + newH - newH; + } + + int tmp; + if (yOffset < 0) + { + tmp = -yOffset; + newH -= tmp; + canvasIdx += tmp * newW; + pixelY += pixelH * tmp; + } + + if (newW + xOffset > newW) + { + tmp = newW + xOffset - newW; + newW -= tmp; + canvasOffset += tmp; + } + + if (xOffset < 0) + { + tmp = -xOffset; + newW -= tmp; + canvasIdx += tmp; + pixelX += pixelW * tmp; + canvasOffset += tmp; + } + + client.scaleSprite(canvas, pixels, 0, pixelX, pixelY, canvasIdx, canvasOffset, newW, newH, pixelW, pixelH, oldW); + + return result; + } } 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 e8965029c1..24828baa51 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 @@ -207,4 +207,54 @@ public class LinkBrowser } }); } + + /** + * Tries to open the specified {@code File} with the systems default text editor. If operation fails + * an error message is displayed with the option to copy the absolute file path to clipboard. + * + * @param file the File instance of the log file + * @return did the file open successfully? + */ + public static boolean openLocalFile(final File file) + { + if (file == null || !file.exists()) + { + return false; + } + + if (attemptOpenLocalFile(file)) + { + log.debug("Opened log file through Desktop#open to {}", file); + return true; + } + + showMessageBox("Unable to open file. Press 'OK' and the file path will be copied to your clipboard", file.getAbsolutePath()); + return false; + } + + private static boolean attemptOpenLocalFile(final File file) + { + if (!Desktop.isDesktopSupported()) + { + return false; + } + + final Desktop desktop = Desktop.getDesktop(); + + if (!desktop.isSupported(Desktop.Action.OPEN)) + { + return false; + } + + try + { + desktop.open(file); + return true; + } + catch (IOException ex) + { + log.warn("Failed to open Desktop#open {}", file, ex); + return false; + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java b/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java index 877c98b3d2..3380cf5a8f 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/PvPUtil.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2021, ThatGamerBlue + * Copyright (c) 2019, PKLite + * Copyright (c) 2020, ThatGamerBlue * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java index 5f226a40c9..1f33ce9638 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/util/SwingUtil.java @@ -40,6 +40,7 @@ import java.awt.TrayIcon; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; +import java.lang.reflect.InvocationTargetException; import java.util.Enumeration; import java.util.function.BiConsumer; import javax.annotation.Nonnull; @@ -302,4 +303,19 @@ public class SwingUtil l.enter(); } } + + /** + * Executes a runnable on the EDT, blocking until it finishes. + */ + public static void syncExec(final Runnable r) throws InvocationTargetException, InterruptedException + { + if (EventQueue.isDispatchThread()) + { + r.run(); + } + else + { + EventQueue.invokeAndWait(r); + } + } } diff --git a/runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externalmanager_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externalmanager_icon.png similarity index 100% rename from runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externalmanager_icon.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externalmanager_icon.png diff --git a/runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/add_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/add_icon.png similarity index 100% rename from runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/add_icon.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/add_icon.png diff --git a/runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/add_raw_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/add_raw_icon.png similarity index 100% rename from runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/add_raw_icon.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/add_raw_icon.png diff --git a/runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/delete_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/delete_icon.png similarity index 100% rename from runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/delete_icon.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/delete_icon.png diff --git a/runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/discord_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/discord_icon.png similarity index 100% rename from runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/discord_icon.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/discord_icon.png diff --git a/runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/gh_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/gh_icon.png similarity index 100% rename from runelite-client/src/main/resources/com/openosrs/client/plugins/openosrs/externals/gh_icon.png rename to runelite-client/src/main/resources/net/runelite/client/plugins/openosrs/externals/gh_icon.png 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 644a1346d3..cc74465b83 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -1388,13 +1388,6 @@ public abstract class RSClientMixin implements RSClient @Replace("menuAction") static void copy$menuAction(int param0, int param1, int opcode, int id, String option, String target, int canvasX, int canvasY) { - boolean authentic = true; - if (target != null && target.startsWith("!AUTHENTIC")) - { - authentic = false; - target = target.substring(10); - } - /* Along the way, the RuneScape client may change a menuAction by incrementing it with 2000. * I have no idea why, but it does. Their code contains the same conditional statement. */ @@ -1403,17 +1396,13 @@ public abstract class RSClientMixin implements RSClient opcode -= 2000; } - final MenuOptionClicked menuOptionClicked = new MenuOptionClicked( - option, - target, - id, - opcode, - param0, - param1, - false, - authentic, - client.getMouseCurrentButton() - ); + final MenuOptionClicked menuOptionClicked = new MenuOptionClicked(); + menuOptionClicked.setActionParam(param0); + menuOptionClicked.setMenuOption(option); + menuOptionClicked.setMenuTarget(target); + menuOptionClicked.setMenuAction(MenuAction.of(opcode)); + menuOptionClicked.setId(id); + menuOptionClicked.setWidgetId(param1); client.getCallbacks().post(menuOptionClicked); @@ -1425,15 +1414,15 @@ public abstract class RSClientMixin implements RSClient if (printMenuActions) { client.getLogger().info( - "|MenuAction|: MenuOption={} MenuTarget={} Id={} Opcode={} Param0={} Param1={} CanvasX={} CanvasY={} Authentic={}", - menuOptionClicked.getOption(), menuOptionClicked.getTarget(), menuOptionClicked.getIdentifier(), - menuOptionClicked.getOpcode(), menuOptionClicked.getActionParam(), menuOptionClicked.getActionParam1(), - canvasX, canvasY, authentic + "|MenuAction|: MenuOption={} MenuTarget={} Id={} Opcode={} Param0={} Param1={} CanvasX={} CanvasY={}", + menuOptionClicked.getMenuOption(), menuOptionClicked.getMenuTarget(), menuOptionClicked.getId(), + menuOptionClicked.getMenuAction(), menuOptionClicked.getActionParam(), menuOptionClicked.getWidgetId(), + canvasX, canvasY ); } - copy$menuAction(menuOptionClicked.getActionParam(), menuOptionClicked.getActionParam1(), menuOptionClicked.getOpcode(), - menuOptionClicked.getIdentifier(), menuOptionClicked.getOption(), menuOptionClicked.getTarget(), canvasX, canvasY); + copy$menuAction(menuOptionClicked.getActionParam(), menuOptionClicked.getWidgetId(), menuOptionClicked.getMenuAction().getId(), + menuOptionClicked.getId(), menuOptionClicked.getMenuOption(), menuOptionClicked.getMenuTarget(), canvasX, canvasY); } @Override @@ -1442,7 +1431,7 @@ public abstract class RSClientMixin implements RSClient { assert isClientThread(); - client.sendMenuAction(param0, param1, opcode, identifier, option, "!AUTHENTIC" + target, 658, 384); + client.sendMenuAction(param0, param1, opcode, identifier, option, target, 658, 384); } @FieldHook("Login_username") From 023a80cf69c087ca9b7be808af893747f4b2b093 Mon Sep 17 00:00:00 2001 From: zeruth Date: Thu, 4 Feb 2021 02:30:24 -0500 Subject: [PATCH 024/133] externals: move injector assurance to Plugin class --- .../net/runelite/client/plugins/Plugin.java | 13 +++++++ .../client/plugins/PluginManager.java | 35 ++----------------- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java index a36a1f40e6..eca34f0488 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java @@ -27,6 +27,7 @@ package net.runelite.client.plugins; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.Module; +import net.runelite.client.RuneLite; public abstract class Plugin implements Module { @@ -49,8 +50,20 @@ public abstract class Plugin implements Module { } + // This should never be null when we are using it public final Injector getInjector() { + if (injector == null) + { + Module pluginModule = (Binder binder) -> + { + binder.bind((Class) this.getClass()).toInstance(this); + binder.install(this); + }; + Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule); + pluginInjector.injectMembers(this); + injector = pluginInjector; + } return injector; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index 01c2d9ba7d..155b524c80 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -165,20 +165,7 @@ public class PluginManager try { Injector injector = plugin.getInjector(); - if (injector == null) - { - // Create injector for the module - Module pluginModule = (Binder binder) -> - { - // Since the plugin itself is a module, it won't bind itself, so we'll bind it here - binder.bind((Class) plugin.getClass()).toInstance(plugin); - binder.install(plugin); - }; - Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule); - pluginInjector.injectMembers(plugin); - plugin.injector = pluginInjector; - injector = pluginInjector; - } + for (Key key : injector.getBindings().keySet()) { Class type = key.getTypeLiteral().getRawType(); @@ -208,25 +195,7 @@ public class PluginManager plugins = getPlugins(); } plugins.forEach(pl -> - { - //TODO: Not sure why this is necessary but it is. The Injector isn't null when its handed off from our ExternalPluginManager. - // Hopefully we can figure out the root cause of the underlying issue. - if (pl.injector == null) - { - // Create injector for the module - Module pluginModule = (Binder binder) -> - { - // Since the plugin itself is a module, it won't bind itself, so we'll bind it here - binder.bind((Class) pl.getClass()).toInstance(pl); - binder.install(pl); - }; - Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule); - pluginInjector.injectMembers(pl); - pl.injector = pluginInjector; - } - - injectors.add(pl.getInjector()); - }); + injectors.add(pl.getInjector())); List list = new ArrayList<>(); for (Injector injector : injectors) From 1ae07707059df298da4b17303a986d783879cacd Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 3 Feb 2021 12:49:34 -0500 Subject: [PATCH 025/133] bank tags: allow plugins to register their own tag names This also moves the clue tab logic into the clue plugin and inverts the bank tags -> clue plugin dependency --- .../client/plugins/banktags/BankTag.java | 31 ++++++++++ .../plugins/banktags/BankTagsPlugin.java | 3 - .../client/plugins/banktags/TagManager.java | 59 ++++--------------- .../plugins/cluescrolls/ClueScrollPlugin.java | 45 ++++++++++++++ 4 files changed, 89 insertions(+), 49 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTag.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTag.java b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTag.java new file mode 100644 index 0000000000..74926bc292 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/banktags/BankTag.java @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2021, 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 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.banktags; + +public interface BankTag +{ + boolean contains(int itemId); +} 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 1cffe75379..4e56358252 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 @@ -75,13 +75,11 @@ import net.runelite.client.game.chatbox.ChatboxPanelManager; import net.runelite.client.input.MouseManager; import net.runelite.client.input.MouseWheelListener; import net.runelite.client.plugins.Plugin; -import net.runelite.client.plugins.PluginDependency; import net.runelite.client.plugins.PluginDescriptor; import net.runelite.client.plugins.banktags.tabs.TabInterface; import static net.runelite.client.plugins.banktags.tabs.TabInterface.FILTERED_CHARS; import net.runelite.client.plugins.banktags.tabs.TabSprites; import net.runelite.client.plugins.banktags.tabs.TagTab; -import net.runelite.client.plugins.cluescrolls.ClueScrollPlugin; import net.runelite.client.util.Text; @PluginDescriptor( @@ -89,7 +87,6 @@ import net.runelite.client.util.Text; description = "Enable tagging of bank items and searching of bank tags", tags = {"searching", "tagging"} ) -@PluginDependency(ClueScrollPlugin.class) public class BankTagsPlugin extends Plugin implements MouseWheelListener { public static final String CONFIG_GROUP = "banktags"; 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 fe697034d0..b03028e3d8 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 @@ -27,24 +27,17 @@ package net.runelite.client.plugins.banktags; import com.google.common.base.Strings; import java.util.Collection; +import java.util.HashMap; import java.util.LinkedHashSet; import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import javax.inject.Inject; import javax.inject.Singleton; -import net.runelite.api.ItemID; import net.runelite.client.config.ConfigManager; import net.runelite.client.game.ItemManager; import net.runelite.client.game.ItemVariationMapping; import static net.runelite.client.plugins.banktags.BankTagsPlugin.CONFIG_GROUP; -import net.runelite.client.plugins.cluescrolls.ClueScrollService; -import net.runelite.client.plugins.cluescrolls.clues.ClueScroll; -import net.runelite.client.plugins.cluescrolls.clues.CoordinateClue; -import net.runelite.client.plugins.cluescrolls.clues.EmoteClue; -import net.runelite.client.plugins.cluescrolls.clues.FairyRingClue; -import net.runelite.client.plugins.cluescrolls.clues.HotColdClue; -import net.runelite.client.plugins.cluescrolls.clues.MapClue; -import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement; import net.runelite.client.util.Text; @Singleton @@ -53,17 +46,15 @@ public class TagManager static final String ITEM_KEY_PREFIX = "item_"; private final ConfigManager configManager; private final ItemManager itemManager; - private final ClueScrollService clueScrollService; + private final Map customTags = new HashMap<>(); @Inject private TagManager( final ItemManager itemManager, - final ConfigManager configManager, - final ClueScrollService clueScrollService) + final ConfigManager configManager) { this.itemManager = itemManager; this.configManager = configManager; - this.clueScrollService = clueScrollService; } String getTagString(int itemId, boolean variation) @@ -123,9 +114,10 @@ public class TagManager boolean findTag(int itemId, String search) { - if (search.equals("clue") && testClue(itemId)) + BankTag bankTag = customTags.get(search); + if (bankTag != null) { - return true; + return bankTag.contains(itemId); } Collection tags = getTags(itemId, false); @@ -194,38 +186,13 @@ public class TagManager return itemId; } - private boolean testClue(int itemId) + public void registerTag(String name, BankTag tag) { - ClueScroll c = clueScrollService.getClue(); + customTags.put(name, tag); + } - if (c == null) - { - return false; - } - - if (c instanceof EmoteClue) - { - EmoteClue emote = (EmoteClue) c; - - for (ItemRequirement ir : emote.getItemRequirements()) - { - if (ir.fulfilledBy(itemId)) - { - return true; - } - } - } - else if (c instanceof CoordinateClue || c instanceof HotColdClue || c instanceof FairyRingClue) - { - return itemId == ItemID.SPADE; - } - else if (c instanceof MapClue) - { - MapClue mapClue = (MapClue) c; - - return mapClue.getObjectId() == -1 && itemId == ItemID.SPADE; - } - - return false; + public void unregisterTag(String name) + { + customTags.remove(name); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java index 06dee6142a..da3a95a301 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java @@ -96,7 +96,10 @@ import net.runelite.client.events.ConfigChanged; import net.runelite.client.events.OverlayMenuClicked; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDependency; import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.banktags.BankTagsPlugin; +import net.runelite.client.plugins.banktags.TagManager; import net.runelite.client.plugins.cluescrolls.clues.AnagramClue; import net.runelite.client.plugins.cluescrolls.clues.BeginnerMapClue; import net.runelite.client.plugins.cluescrolls.clues.CipherClue; @@ -117,6 +120,7 @@ import net.runelite.client.plugins.cluescrolls.clues.ObjectClueScroll; import net.runelite.client.plugins.cluescrolls.clues.SkillChallengeClue; import net.runelite.client.plugins.cluescrolls.clues.TextClueScroll; import net.runelite.client.plugins.cluescrolls.clues.ThreeStepCrypticClue; +import net.runelite.client.plugins.cluescrolls.clues.item.ItemRequirement; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.OverlayMenuEntry; import net.runelite.client.ui.overlay.OverlayUtil; @@ -131,6 +135,7 @@ import org.apache.commons.lang3.ArrayUtils; description = "Show answers to clue scroll riddles, anagrams, ciphers, and cryptic clues", tags = {"arrow", "hints", "world", "map", "coordinates", "emotes"} ) +@PluginDependency(BankTagsPlugin.class) @Slf4j public class ClueScrollPlugin extends Plugin { @@ -144,6 +149,7 @@ public class ClueScrollPlugin extends Plugin 13150, 9011, 13151, 9012 }; + private static final String CLUE_TAG_NAME = "clue"; @Getter private ClueScroll clue; @@ -191,6 +197,9 @@ public class ClueScrollPlugin extends Plugin @Inject private WorldMapPointManager worldMapPointManager; + @Inject + private TagManager tagManager; + @Inject @Named("developerMode") boolean developerMode; @@ -227,11 +236,13 @@ public class ClueScrollPlugin extends Plugin overlayManager.add(clueScrollEmoteOverlay); overlayManager.add(clueScrollWorldOverlay); overlayManager.add(clueScrollMusicOverlay); + tagManager.registerTag(CLUE_TAG_NAME, this::testClueTag); } @Override protected void shutDown() throws Exception { + tagManager.unregisterTag(CLUE_TAG_NAME); overlayManager.remove(clueScrollOverlay); overlayManager.remove(clueScrollEmoteOverlay); overlayManager.remove(clueScrollWorldOverlay); @@ -1109,4 +1120,38 @@ public class ClueScrollPlugin extends Plugin } return worldPoint; } + + private boolean testClueTag(int itemId) + { + ClueScroll c = clue; + if (c == null) + { + return false; + } + + if (c instanceof EmoteClue) + { + EmoteClue emote = (EmoteClue) c; + + for (ItemRequirement ir : emote.getItemRequirements()) + { + if (ir.fulfilledBy(itemId)) + { + return true; + } + } + } + else if (c instanceof CoordinateClue || c instanceof HotColdClue || c instanceof FairyRingClue) + { + return itemId == ItemID.SPADE; + } + else if (c instanceof MapClue) + { + MapClue mapClue = (MapClue) c; + + return mapClue.getObjectId() == -1 && itemId == ItemID.SPADE; + } + + return false; + } } From 7b5fb1fb7664015d44c455e6f0a2cc78fa1fb10a Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Feb 2021 13:41:32 -0500 Subject: [PATCH 026/133] clue scroll plugin: fix test --- .../client/plugins/cluescrolls/ClueScrollPluginTest.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/ClueScrollPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/ClueScrollPluginTest.java index 3719dce654..1c3cd1ecd4 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/ClueScrollPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cluescrolls/ClueScrollPluginTest.java @@ -40,6 +40,7 @@ import net.runelite.api.events.GameTick; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.banktags.TagManager; import net.runelite.client.plugins.cluescrolls.clues.hotcold.HotColdLocation; import net.runelite.client.ui.overlay.OverlayManager; import static org.junit.Assert.assertEquals; @@ -81,6 +82,10 @@ public class ClueScrollPluginTest @Bind ItemManager itemManager; + @Mock + @Bind + TagManager tagManager; + @Before public void before() { From fc8360f6cc24c8a6eebae1000bef3fd9a7818abf Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Feb 2021 16:18:18 -0500 Subject: [PATCH 027/133] config manager: require config group and key when setting configuration --- .../main/java/net/runelite/client/config/ConfigManager.java | 5 +++++ 1 file changed, 5 insertions(+) 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 a2a4eef25c..a1cc3ee8b5 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 @@ -507,6 +507,11 @@ public class ConfigManager public void setConfiguration(String groupName, String profile, String key, String value) { + if (Strings.isNullOrEmpty(groupName) || Strings.isNullOrEmpty(key)) + { + throw new IllegalArgumentException(); + } + assert !key.startsWith(RSPROFILE_GROUP + "."); String wholeKey = getWholeKey(groupName, profile, key); String oldValue = (String) properties.setProperty(wholeKey, value); From 29747b1ea7479e3fe377cd00a17966d36cce7b21 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Feb 2021 17:54:21 -0500 Subject: [PATCH 028/133] api: add an addChatMessage method overload to allow skipping events --- .../src/main/java/net/runelite/api/Client.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) 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 c08be0edbe..c665139862 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -120,8 +120,21 @@ public interface Client extends GameEngine * @param name the name of the player that sent the message * @param message the message contents * @param sender the sender/channel name + * @return the message node for the message */ - void addChatMessage(ChatMessageType type, String name, String message, String sender); + MessageNode addChatMessage(ChatMessageType type, String name, String message, String sender); + + /** + * Adds a new chat message to the chatbox. + * + * @param type the type of message + * @param name the name of the player that sent the message + * @param message the message contents + * @param sender the sender/channel name + * @param postEvent whether to post the chat message event + * @return the message node for the message + */ + MessageNode addChatMessage(ChatMessageType type, String name, String message, String sender, boolean postEvent); /** * Gets the current game state. From 236c23b25e014e5eab8ea70680a6356ae6d47885 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Feb 2021 17:55:44 -0500 Subject: [PATCH 029/133] chat command manager: remove unnecessary null tests --- .../runelite/client/chat/ChatCommandManager.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/chat/ChatCommandManager.java b/runelite-client/src/main/java/net/runelite/client/chat/ChatCommandManager.java index 03bc5960b6..0c3a0bfcbc 100644 --- a/runelite-client/src/main/java/net/runelite/client/chat/ChatCommandManager.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/ChatCommandManager.java @@ -106,11 +106,6 @@ public class ChatCommandManager implements ChatboxInputListener String message = chatMessage.getMessage(); String command = extractCommand(message); - if (command == null) - { - return; - } - ChatCommand chatCommand = commands.get(command.toLowerCase()); if (chatCommand == null) { @@ -137,11 +132,6 @@ public class ChatCommandManager implements ChatboxInputListener } String command = extractCommand(message); - if (command == null) - { - return false; - } - ChatCommand chatCommand = commands.get(command.toLowerCase()); if (chatCommand == null) { @@ -163,11 +153,6 @@ public class ChatCommandManager implements ChatboxInputListener final String message = privateMessageInput.getMessage(); String command = extractCommand(message); - if (command == null) - { - return false; - } - ChatCommand chatCommand = commands.get(command.toLowerCase()); if (chatCommand == null) { From a2b2d049ccaa2253fccd79b16325c229d9cf854f Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Feb 2021 17:57:33 -0500 Subject: [PATCH 030/133] chat message manager: format runelite messages prior to adding This allows plugins listening for the chat message event to get the runelite formatted message --- .../client/chat/ChatMessageManager.java | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java index 7faa7ac564..b1795db6ac 100644 --- a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java @@ -39,20 +39,19 @@ import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.atomic.AtomicReference; import javax.inject.Inject; import javax.inject.Singleton; -import net.runelite.api.ChatLineBuffer; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.MessageNode; import net.runelite.api.Player; import net.runelite.api.Varbits; import net.runelite.api.events.ChatMessage; -import net.runelite.client.events.ConfigChanged; import net.runelite.api.events.ResizeableChanged; import net.runelite.api.events.ScriptCallbackEvent; import net.runelite.api.events.VarbitChanged; import net.runelite.client.callback.ClientThread; import net.runelite.client.config.ChatColorConfig; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; import net.runelite.client.ui.JagexColors; import net.runelite.client.util.ColorUtil; @@ -571,18 +570,15 @@ public class ChatMessageManager return; } + final String formattedMessage = formatRuneLiteMessage(message.getRuneLiteFormattedMessage(), message.getType()); + // this updates chat cycle - client.addChatMessage( + final MessageNode line = client.addChatMessage( message.getType(), MoreObjects.firstNonNull(message.getName(), ""), - MoreObjects.firstNonNull(message.getValue(), message.getRuneLiteFormattedMessage()), + MoreObjects.firstNonNull(formattedMessage, message.getValue()), message.getSender()); - // Get last message from line buffer (the one we just added) - final ChatLineBuffer chatLineBuffer = client.getChatLineMap().get(message.getType().getType()); - final MessageNode[] lines = chatLineBuffer.getLines(); - final MessageNode line = lines[0]; - // Update the message with RuneLite additions line.setRuneLiteFormatMessage(message.getRuneLiteFormattedMessage()); @@ -590,34 +586,38 @@ public class ChatMessageManager { line.setTimestamp(message.getTimestamp()); } - - update(line); } - public void update(final MessageNode target) + /** + * Rebuild the message node message from the RuneLite format message + * + * @param messageNode message node + */ + public void update(final MessageNode messageNode) { - if (Strings.isNullOrEmpty(target.getRuneLiteFormatMessage())) + String message = formatRuneLiteMessage(messageNode.getRuneLiteFormatMessage(), messageNode.getType()); + if (message != null) { - return; + messageNode.setValue(message); + } + } + + private String formatRuneLiteMessage(String runeLiteFormatMessage, ChatMessageType type) + { + if (Strings.isNullOrEmpty(runeLiteFormatMessage)) + { + return null; } final boolean transparent = client.isResized() && transparencyVarbit != 0; - final Collection chatColors = colorCache.get(target.getType()); + final Collection chatColors = colorCache.get(type); - // If we do not have any colors cached, simply set clean message if (chatColors == null || chatColors.isEmpty()) { - target.setValue(target.getRuneLiteFormatMessage()); - return; + return runeLiteFormatMessage; } - target.setValue(recolorMessage(transparent, target.getRuneLiteFormatMessage(), target.getType())); - } - - private String recolorMessage(boolean transparent, String message, ChatMessageType messageType) - { - final Collection chatColors = colorCache.get(messageType); - final AtomicReference resultMessage = new AtomicReference<>(message); + final AtomicReference resultMessage = new AtomicReference<>(runeLiteFormatMessage); // Replace custom formatting with actual colors chatColors.stream() From e71b18b02b124bff69cc347238551d2e0d3bf29b Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Feb 2021 18:03:37 -0500 Subject: [PATCH 031/133] chat history: fix replaying runelite-formatted messages This stores the underlying message node, which may be updated much later with a new message value if a command is used, and replays that. Previously this was storing and replaying the raw RL formatted message, which is why colors would be stripped off of RL-formatted messages on replay. This also no longer triggers chat message events from history replay, which fixes commands incorrectly refiring from replay (and thus the nbsp hack), and fixes the chat notifier sending notifications for replayed messages. --- .../chathistory/ChatHistoryPlugin.java | 52 +++++-------------- 1 file changed, 14 insertions(+), 38 deletions(-) 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 8305c40bde..cc94cfc801 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 @@ -56,8 +56,6 @@ import net.runelite.api.widgets.WidgetInfo; import static net.runelite.api.widgets.WidgetInfo.TO_CHILD; import static net.runelite.api.widgets.WidgetInfo.TO_GROUP; 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; @@ -82,7 +80,7 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener private static final int CYCLE_HOTKEY = KeyEvent.VK_TAB; private static final int FRIENDS_MAX_SIZE = 5; - private Queue messageQueue; + private Queue messageQueue; private Deque friends; private String currentMessage = null; @@ -99,9 +97,6 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener @Inject private KeyManager keyManager; - @Inject - private ChatMessageManager chatMessageManager; - @Provides ChatHistoryConfig getConfig(ConfigManager configManager) { @@ -111,6 +106,9 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener @Override protected void startUp() { + // The client reuses MessageNodes after 100 chat messages of + // the same type, so this must be 100 (or maybe a map of + // size 100 evicting queues) messageQueue = EvictingQueue.create(100); friends = new ArrayDeque<>(FRIENDS_MAX_SIZE + 1); keyManager.registerKeyListener(this); @@ -140,11 +138,16 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener return; } - QueuedMessage queuedMessage; - - while ((queuedMessage = messageQueue.poll()) != null) + for (MessageNode queuedMessage : messageQueue) { - chatMessageManager.queue(queuedMessage); + final MessageNode node = client.addChatMessage( + queuedMessage.getType(), + queuedMessage.getName(), + queuedMessage.getValue(), + queuedMessage.getSender(), + false); + node.setRuneLiteFormatMessage(queuedMessage.getRuneLiteFormatMessage()); + node.setTimestamp(queuedMessage.getTimestamp()); } return; @@ -171,19 +174,7 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener case MODCHAT: case FRIENDSCHAT: case CONSOLE: - final QueuedMessage queuedMessage = QueuedMessage.builder() - .type(chatMessageType) - .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); - } + messageQueue.offer(chatMessage.getMessageNode()); } } @@ -348,21 +339,6 @@ public class ChatHistoryPlugin extends Plugin implements KeyListener clearMessageQueue(tab); } - /** - * Small hack to prevent plugins checking for specific messages to match - * @param message message - * @return message with nbsp - */ - private static String nbsp(final String message) - { - if (message != null) - { - return message.replace(' ', '\u00A0'); - } - - return null; - } - @Override public void keyPressed(KeyEvent e) { From e941bb72401fa3764dc2943a69b7ca936c2121a1 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 4 Feb 2021 22:29:04 -0500 Subject: [PATCH 032/133] friends chat: use return value of addChatMessage --- .../client/plugins/friendschat/FriendsChatPlugin.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/friendschat/FriendsChatPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/friendschat/FriendsChatPlugin.java index 8413c225e6..6263518015 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/friendschat/FriendsChatPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/friendschat/FriendsChatPlugin.java @@ -411,11 +411,7 @@ public class FriendsChatPlugin extends Plugin .append(textColor, member.getName() + activityMessage); final String messageString = message.build(); - client.addChatMessage(ChatMessageType.FRIENDSCHATNOTIFICATION, "", messageString, ""); - - final ChatLineBuffer chatLineBuffer = client.getChatLineMap().get(ChatMessageType.FRIENDSCHATNOTIFICATION.getType()); - final MessageNode[] lines = chatLineBuffer.getLines(); - final MessageNode line = lines[0]; + final MessageNode line = client.addChatMessage(ChatMessageType.FRIENDSCHATNOTIFICATION, "", messageString, ""); MemberJoinMessage joinMessage = new MemberJoinMessage(line, line.getId(), client.getTickCount()); joinMessages.addLast(joinMessage); From bf69f263b02f4bb1a1f036feb6f774a296b95a14 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Wed, 3 Feb 2021 20:43:22 -0500 Subject: [PATCH 033/133] chatnotifications: add private message notification --- .../chatnotifications/ChatNotificationsConfig.java | 11 +++++++++++ .../chatnotifications/ChatNotificationsPlugin.java | 7 +++++++ 2 files changed, 18 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsConfig.java index 80044fc340..8c254a2c2a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsConfig.java @@ -108,4 +108,15 @@ public interface ChatNotificationsConfig extends Config { return false; } + + @ConfigItem( + position = 7, + keyName = "notifyOnPM", + name = "Notify on private message", + description = "Notifies you whenever you receive a private message" + ) + default boolean notifyOnPM() + { + return false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java index afe574d3c6..092b8cf376 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java @@ -165,6 +165,13 @@ public class ChatNotificationsPlugin extends Plugin notifier.notify(Text.removeFormattingTags(broadcast)); } break; + case PRIVATECHAT: + case MODPRIVATECHAT: + if (config.notifyOnPM()) + { + notifier.notify(Text.removeTags(chatMessage.getName()) + ": " + chatMessage.getMessage()); + } + break; case CONSOLE: // Don't notify for notification messages if (chatMessage.getName().equals(runeliteTitle)) From aa6ac7cbc0ab5f20cdd3b133e8c49f073c619afe Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Thu, 4 Feb 2021 08:54:39 -0800 Subject: [PATCH 034/133] clues: Add Isle of Souls coordinate clues --- .../client/plugins/cluescrolls/clues/CoordinateClue.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java index 8d9a462118..5918f65343 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java @@ -205,6 +205,8 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati .put(new WorldPoint(2484, 4016, 0), new CoordinateClueInfo("Northeast corner of the Island of Stone.", ARMADYLIAN_OR_BANDOSIAN_GUARD)) .put(new WorldPoint(2222, 3331, 0), new CoordinateClueInfo("Prifddinas, west of the Tower of Voices", ARMADYLIAN_OR_BANDOSIAN_GUARD)) .put(new WorldPoint(3560, 3987, 0), new CoordinateClueInfo("Lithkren. Digsite pendant teleport if unlocked, otherwise take rowboat from west of Mushroom Meadow Mushtree.", ARMADYLIAN_OR_BANDOSIAN_GUARD)) + .put(new WorldPoint(2318, 2954, 0), new CoordinateClueInfo("North-east corner of the Isle of Souls.", ARMADYLIAN_OR_BANDOSIAN_GUARD)) + .put(new WorldPoint(2094, 2889, 0), new CoordinateClueInfo("West side of the Isle of Souls.", ARMADYLIAN_OR_BANDOSIAN_GUARD)) // Master .put(new WorldPoint(2178, 3209, 0), new CoordinateClueInfo("South of Iorwerth Camp.", BRASSICAN_MAGE)) .put(new WorldPoint(2155, 3100, 0), new CoordinateClueInfo("South of Port Tyras (BJS if 76 Agility).", BRASSICAN_MAGE)) From f9a51a59e6517056e40511656e0e20ef595ae50e Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Wed, 3 Feb 2021 10:08:09 -0500 Subject: [PATCH 035/133] discord: Isle of Souls region additions --- .../runelite/client/plugins/discord/DiscordGameEventType.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java index 7e07a12ee4..ec98c9495b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java @@ -187,6 +187,7 @@ enum DiscordGameEventType DUNGEON_CRABCLAW_CAVES("Crabclaw Caves", DiscordAreaType.DUNGEONS, 6553, 6809), DUNGEON_CRANDOR("Crandor Dungeon", DiscordAreaType.DUNGEONS, 11414), DUNGEON_CRASH_SITE_CAVERN("Crash Site Cavern", DiscordAreaType.DUNGEONS, 8280, 8536), + DUNGEON_CRUMBLING_TOWER("Crumbling Tower", DiscordAreaType.DUNGEONS, 7827), DUNGEON_DAEYALT_ESSENCE_MINE("Daeyalt Essence Mine", DiscordAreaType.DUNGEONS, 14744), DUNGEON_DIGSITE("Digsite Dungeon", DiscordAreaType.DUNGEONS, 13464, 13465), DUNGEON_DORGESHKAAN("Dorgesh-Kaan South Dungeon", DiscordAreaType.DUNGEONS, 10833), @@ -210,6 +211,7 @@ enum DiscordGameEventType DUNGEON_HAM_STORE_ROOM("H.A.M. Store room", DiscordAreaType.DUNGEONS, 10321), DUNGEON_HEROES_GUILD("Heroes' Guild Mine", DiscordAreaType.DUNGEONS, 11674), DUNGEON_IORWERTH("Iorwerth Dungeon", DiscordAreaType.DUNGEONS, 12737, 12738, 12993, 12994), + DUNGEON_ISLE_OF_SOULS("Isle of Souls Dungeon", DiscordAreaType.DUNGEONS, 8593), DUNGEON_JATIZSO_MINES("Jatizso Mines", DiscordAreaType.DUNGEONS, 9631), DUNGEON_JIGGIG_BURIAL_TOMB("Jiggig Burial Tomb", DiscordAreaType.DUNGEONS, 9875, 9874), DUNGEON_JOGRE("Jogre Dungeon", DiscordAreaType.DUNGEONS, 11412), @@ -367,6 +369,7 @@ enum DiscordGameEventType REGION_ICYENE_GRAVEYARD("Icyene Graveyard", DiscordAreaType.REGIONS, 14641, 14897, 14898), REGION_ISAFDAR("Isafdar", DiscordAreaType.REGIONS, 8497, 8753, 8754, 9009, 9010), REGION_ISLAND_OF_STONE("Island of Stone", DiscordAreaType.REGIONS, 9790), + REGION_ISLE_OF_SOULS("Isle of Souls", DiscordAreaType.REGIONS, 8236, 8237, 8238, 8491, 8492, 8494, 8747, 8750, 9003, 9004, 9006, 9260, 9261, 9262), REGION_JIGGIG("Jiggig" , DiscordAreaType.REGIONS, 9775), REGION_KANDARIN("Kandarin", DiscordAreaType.REGIONS, 9014, 9263, 9264, 9519, 9524, 9527, 9776, 9783, 10037, 10290, 10294, 10546, 10551, 10805), REGION_KARAMJA("Karamja" , DiscordAreaType.REGIONS, 10801, 10802, 11054, 11311, 11312, 11313, 11566, 11567, 11568, 11569, 11822), From 26bbe8a9821c84f46aa7b62ff70560d4a3e6a1f2 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Wed, 3 Feb 2021 10:09:35 -0500 Subject: [PATCH 036/133] fishing: add Isle of Souls NPC IDs to enum --- .../java/net/runelite/client/game/FishingSpot.java | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/game/FishingSpot.java b/runelite-client/src/main/java/net/runelite/client/game/FishingSpot.java index 14d7b96316..a3de9412a2 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/FishingSpot.java +++ b/runelite-client/src/main/java/net/runelite/client/game/FishingSpot.java @@ -29,6 +29,9 @@ import java.util.Map; import lombok.Getter; import net.runelite.api.ItemID; import static net.runelite.api.NpcID.FISHING_SPOT; +import static net.runelite.api.NpcID.FISHING_SPOT_10513; +import static net.runelite.api.NpcID.FISHING_SPOT_10514; +import static net.runelite.api.NpcID.FISHING_SPOT_10515; import static net.runelite.api.NpcID.FISHING_SPOT_1497; import static net.runelite.api.NpcID.FISHING_SPOT_1498; import static net.runelite.api.NpcID.FISHING_SPOT_1499; @@ -132,13 +135,14 @@ public enum FishingSpot FISHING_SPOT_1525, FISHING_SPOT_1528, FISHING_SPOT_1530, FISHING_SPOT_1544, FISHING_SPOT_3913, FISHING_SPOT_7155, FISHING_SPOT_7459, FISHING_SPOT_7462, FISHING_SPOT_7467, - FISHING_SPOT_7469, FISHING_SPOT_7947 + FISHING_SPOT_7469, FISHING_SPOT_7947, FISHING_SPOT_10513 ), LOBSTER("Lobster, Swordfish, Tuna", "Lobster", ItemID.RAW_LOBSTER, FISHING_SPOT_1510, FISHING_SPOT_1519, FISHING_SPOT_1522, FISHING_SPOT_3914, FISHING_SPOT_5820, FISHING_SPOT_7199, FISHING_SPOT_7460, FISHING_SPOT_7465, FISHING_SPOT_7470, - FISHING_SPOT_7946, FISHING_SPOT_9173, FISHING_SPOT_9174 + FISHING_SPOT_7946, FISHING_SPOT_9173, FISHING_SPOT_9174, + FISHING_SPOT_10515 ), SHARK("Shark, Bass", "Shark", ItemID.RAW_SHARK, FISHING_SPOT_1511, FISHING_SPOT_1520, FISHING_SPOT_3419, @@ -146,7 +150,7 @@ public enum FishingSpot FISHING_SPOT_5233, FISHING_SPOT_5234, FISHING_SPOT_5821, FISHING_SPOT_7200, FISHING_SPOT_7461, FISHING_SPOT_7466, FISHING_SPOT_8525, FISHING_SPOT_8526, FISHING_SPOT_8527, - FISHING_SPOT_9171, FISHING_SPOT_9172 + FISHING_SPOT_9171, FISHING_SPOT_9172, FISHING_SPOT_10514 ), MONKFISH("Monkfish", ItemID.RAW_MONKFISH, FISHING_SPOT_4316 From 7395e221ce7e33f082bd696a0d3fe9563e248b23 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Wed, 3 Feb 2021 10:08:44 -0500 Subject: [PATCH 037/133] worldmap: add Isle of Souls locations --- .../client/plugins/worldmap/DungeonLocation.java | 2 ++ .../client/plugins/worldmap/FishingSpotLocation.java | 3 +++ .../client/plugins/worldmap/HunterAreaLocation.java | 3 +++ .../client/plugins/worldmap/MiningSiteLocation.java | 5 +++++ .../client/plugins/worldmap/RareTreeLocation.java | 10 ++++++++++ 5 files changed, 23 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java index 0df27cf60c..544cefd6c8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/DungeonLocation.java @@ -58,6 +58,7 @@ enum DungeonLocation CRABCLAW_CAVES_TUNNEL("Crabclaw Caves Tunnel (quest)", new WorldPoint(1671, 9800, 0)), CRANDOR("Crandor Dungeon", new WorldPoint(2833, 3256, 0)), CRASH_ISLAND("Crash Island Dungeon", new WorldPoint(2920, 2721, 0)), + CRUMBLING_TOWER("Crumbling Tower basement", new WorldPoint(2130, 2994, 0)), DEEP_WILDERNESS("Deep Wilderness Dungeon", new WorldPoint(3044, 3924, 0)), DRAYNOR_MANOR_E("Draynor Manor basement", new WorldPoint(3114, 3357, 0)), DRAYNOR_MANOR_W("Draynor Manor basement", new WorldPoint(3091, 3362, 0)), @@ -89,6 +90,7 @@ enum DungeonLocation ICE_QUEEN_W("Ice Queen's Lair", new WorldPoint(2822, 3510, 0)), ICE_TROLL_E("Ice Troll Caves", new WorldPoint(2400, 3889, 0)), ICE_TROLL_W("Ice Troll Caves", new WorldPoint(2315, 3894, 0)), + ISLE_OF_SOULS_DUNGEON("Isle of Souls Dungeon", new WorldPoint(2308, 2919, 0)), IORWERTH("Iorwerth Dungeon", new WorldPoint(3224, 6044, 0)), IORWERTH_CAMP_CAVE("Iorwerth Camp cave", new WorldPoint(2200, 3262, 0)), IORWERTH_CAMP_CAVE_PRIF("Iorwerth Camp cave", new WorldPoint(3224, 6014, 0)), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotLocation.java index 3180ca0973..4b04a4beea 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/FishingSpotLocation.java @@ -95,6 +95,9 @@ enum FishingSpotLocation IORWERTH_CAMP_OUTSIDE(FishingSpot.SALMON, new WorldPoint(2215, 3245, 0)), ISAFDAR_NORTH_EAST_INSIDE(FishingSpot.SALMON, new WorldPoint(3293, 6005, 0)), ISAFDAR_NORTH_EAST_OUTSIDE(FishingSpot.SALMON, new WorldPoint(2269, 3253, 0)), + ISLE_OF_SOULS_EAST(FishingSpot.SHARK, new WorldPoint(2281, 2841, 0)), + ISLE_OF_SOULS_NORTH(FishingSpot.LOBSTER, new WorldPoint(2280, 2975, 0)), + ISLE_OF_SOULS_SOUTH_WEST(FishingSpot.SHRIMP, new WorldPoint(2162, 2782, 0)), JATISZO(new FishingSpot[]{FishingSpot.SHARK, FishingSpot.LOBSTER}, new WorldPoint(2400, 3780, 0), new WorldPoint(2412, 3780, 0), new WorldPoint(2419, 3789, 0)), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java index 3f9ee919b8..5607170461 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/HunterAreaLocation.java @@ -49,6 +49,9 @@ enum HunterAreaLocation FOSSIL_ISLAND_UNDERWATER(new WorldPoint(3743, 10295, 0), HunterCreature.FISH_SHOAL), GWENITH_HUNTER_AREA_OUTSIDE(new WorldPoint(2269, 3408, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA), GWENITH_HUNTER_AREA_INSIDE(new WorldPoint(3293, 6160, 0), HunterCreature.CARNIVOROUS_CHINCHOMPA), + ISLE_OF_SOULS_NORTH(new WorldPoint(2207, 2964, 0), HunterCreature.COPPER_LONGTAIL), + ISLE_OF_SOULS_NORTH_WEST(new WorldPoint(2127, 2950, 0), HunterCreature.CHINCHOMPA), + ISLE_OF_SOULS_SOUTH_WEST(new WorldPoint(2158, 2822, 0), HunterCreature.CRIMSON_SWIFT), KARAMJA_HUNTER_AREA(new WorldPoint(2786, 3001, 0), HunterCreature.HORNED_GRAAHK), KEBOS_SWAMP(new WorldPoint(1184, 3595, 0), HunterCreature.CRIMSON_SWIFT), KOUREND_WOODLAND_CENTER(new WorldPoint(1512, 3478, 0), HunterCreature.RUBY_HARVEST), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSiteLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSiteLocation.java index bd647d2b1d..ef4db23372 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSiteLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/MiningSiteLocation.java @@ -117,6 +117,11 @@ enum MiningSiteLocation new Rock(10, Ore.CLAY), new Rock(11, Ore.COPPER), new Rock(4, Ore.TIN), new Rock(9, Ore.IRON), new Rock(2, Ore.SILVER)), ISAFDAR(new WorldPoint(2277, 3159, 0), new Rock(4, Ore.ADAMANTITE), new Rock(2, Ore.RUNITE)), + ISLE_OF_SOULS_DUNGEON_EAST(new WorldPoint(1831, 9109, 0), new Rock(1, Ore.RUNITE)), + ISLE_OF_SOULS_DUNGEON_WEST(new WorldPoint(1814, 9116, 0), new Rock(2, Ore.ADAMANTITE)), + ISLE_OF_SOULS_SOUTH(new WorldPoint(2195, 2793, 0), + new Rock(3, Ore.CLAY), new Rock(3, Ore.TIN), new Rock(3, Ore.COPPER), new Rock(10, Ore.IRON), + new Rock(3, Ore.SILVER), new Rock(6, Ore.COAL), new Rock(4, Ore.GOLD), new Rock(2, Ore.MITHRIL)), JATIZSO(new WorldPoint(2396, 3812, 0), new Rock(11, Ore.TIN), new Rock(7, Ore.IRON), new Rock(8, Ore.COAL), new Rock(15, Ore.MITHRIL), new Rock(11, Ore.ADAMANTITE)), 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 addcda5af7..f7751bf5dd 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 @@ -83,6 +83,9 @@ enum RareTreeLocation new WorldPoint(2748, 3466, 0), new WorldPoint(2710, 3570, 0), + // Isle of Souls + new WorldPoint(2254, 2808, 0), + // Prifddinas new WorldPoint(2209, 3427, 0), new WorldPoint(3233, 6179, 0)), @@ -111,6 +114,9 @@ enum RareTreeLocation // Mos Le'Harmless new WorldPoint(3810, 3058, 0), + // Isle of Souls + new WorldPoint(2194, 2991, 0), + // Karamja new WorldPoint(2821, 3084, 0)), @@ -180,6 +186,10 @@ enum RareTreeLocation new WorldPoint(3674, 3447, 0), new WorldPoint(3684, 3385, 0), + // Isle of Souls + new WorldPoint(2147, 2972, 0), + new WorldPoint(2165, 2863, 0), + // Zanaris new WorldPoint(2412, 4464, 0), new WorldPoint(2465, 4427, 0), From 28556cfae35f905ae41dbc6817afc9c3a8dec4ff Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Wed, 3 Feb 2021 11:54:36 -0500 Subject: [PATCH 038/133] slayer: add Isle of Souls task location --- .../src/main/java/net/runelite/client/plugins/slayer/Task.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java index 8e02556440..29867dc042 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/Task.java @@ -202,6 +202,7 @@ enum Task "Fremennik Slayer Dungeon", "God Wars Dungeon", "Iorwerth Dungeon", + "Isle of Souls", "Jormungand's Prison", "Kalphite Lair", "Karuulm Slayer Dungeon", From 5031df9fb2040f7d7e9ba5c42477d7ab7bd1d76b Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Fri, 5 Feb 2021 17:15:27 +0000 Subject: [PATCH 039/133] clues: add Isle of Souls Hot/Cold location --- .../plugins/cluescrolls/clues/hotcold/HotColdLocation.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java index 02ef88a083..d2433db61a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java @@ -96,6 +96,7 @@ public enum HotColdLocation FREMENNIK_PROVINCE_LUNAR_VILLAGE(new WorldPoint(2084, 3916, 0), FREMENNIK_PROVINCE, "Lunar Isle, inside the village.", ANCIENT_WIZARDS), FREMENNIK_PROVINCE_LUNAR_NORTH(new WorldPoint(2106, 3949, 0), FREMENNIK_PROVINCE, "Lunar Isle, north of the village.", ANCIENT_WIZARDS), ICE_MOUNTAIN(new WorldPoint(3007, 3475, 0), MISTHALIN, "Atop Ice Mountain"), + ISLE_OF_SOULS_MINE(new WorldPoint(2189, 2794, 0), KANDARIN, "Isle of Souls Mine, south of the Soul Wars lobby"), KANDARIN_SINCLAR_MANSION(new WorldPoint(2730, 3588, 0), KANDARIN, "North-west of the Sinclair Mansion, near the log balance shortcut.", BRASSICAN_MAGE), KANDARIN_CATHERBY(new WorldPoint(2774, 3436, 0), KANDARIN, "Catherby, between the bank and the beehives, near small rock formation.", BRASSICAN_MAGE), KANDARIN_GRAND_TREE(new WorldPoint(2448, 3503, 0), KANDARIN, "Grand Tree, just east of the terrorchick gnome enclosure.", BRASSICAN_MAGE), From f8612639fe2a16bbfbba26e9df9d01305180ab1a Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Sun, 17 Jan 2021 18:45:04 -0500 Subject: [PATCH 040/133] discord: Use regions instead of varbits for raiding activity --- .../runelite/client/plugins/discord/DiscordGameEventType.java | 4 ++-- .../net/runelite/client/plugins/discord/DiscordPlugin.java | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java index ec98c9495b..4f93882e58 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java @@ -309,8 +309,8 @@ enum DiscordGameEventType MG_VOLCANIC_MINE("Volcanic Mine", DiscordAreaType.MINIGAMES, 15263, 15262), // Raids - RAIDS_CHAMBERS_OF_XERIC("Chambers of Xeric", DiscordAreaType.RAIDS, Varbits.IN_RAID), - RAIDS_THEATRE_OF_BLOOD("Theatre of Blood", DiscordAreaType.RAIDS, Varbits.THEATRE_OF_BLOOD), + RAIDS_CHAMBERS_OF_XERIC("Chambers of Xeric", DiscordAreaType.RAIDS, 12889, 13136, 13137, 13138, 13139, 13140, 13141, 13145, 13393, 13394, 13395, 13396, 13397, 13401), + RAIDS_THEATRE_OF_BLOOD("Theatre of Blood", DiscordAreaType.RAIDS, 12611, 12612, 12613, 12867, 12869, 13122, 13123, 13125, 13379), // Other REGION_ABYSSAL_AREA("Abyssal Area", DiscordAreaType.REGIONS, 12108), 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 6f5ffbdcd1..2c114de643 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 @@ -447,6 +447,7 @@ public class DiscordPlugin extends Plugin case DUNGEONS: return config.showDungeonActivity(); case MINIGAMES: return config.showMinigameActivity(); case REGIONS: return config.showRegionsActivity(); + case RAIDS: return config.showRaidingActivity(); } return false; From dc99317b756888b08c820d4feac916bbccad64c4 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Sun, 17 Jan 2021 19:01:09 -0500 Subject: [PATCH 041/133] discord: Remove code for handling of varbits By changing DiscordAreaType.RAIDS to use regions instead of varbits, it is no longer necessary for the discord plugin to have any varbit-related code --- .../plugins/discord/DiscordGameEventType.java | 38 ------------------- .../client/plugins/discord/DiscordPlugin.java | 17 --------- 2 files changed, 55 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java index 4f93882e58..4bfe4652ed 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java @@ -26,16 +26,12 @@ */ package net.runelite.client.plugins.discord; -import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import java.util.List; import java.util.Map; import javax.annotation.Nullable; import lombok.AllArgsConstructor; import lombok.Getter; -import net.runelite.api.Client; import net.runelite.api.Skill; -import net.runelite.api.Varbits; @AllArgsConstructor @Getter @@ -433,20 +429,12 @@ enum DiscordGameEventType REGION_WRATH_ALTAR("Wrath Altar", DiscordAreaType.REGIONS, 9291); private static final Map FROM_REGION; - private static final List FROM_VARBITS; static { ImmutableMap.Builder regionMapBuilder = new ImmutableMap.Builder<>(); - ImmutableList.Builder fromVarbitsBuilder = ImmutableList.builder(); for (DiscordGameEventType discordGameEventType : DiscordGameEventType.values()) { - if (discordGameEventType.getVarbits() != null) - { - fromVarbitsBuilder.add(discordGameEventType); - continue; - } - if (discordGameEventType.getRegionIds() == null) { continue; @@ -458,7 +446,6 @@ enum DiscordGameEventType } } FROM_REGION = regionMapBuilder.build(); - FROM_VARBITS = fromVarbitsBuilder.build(); } @Nullable @@ -500,9 +487,6 @@ enum DiscordGameEventType @Nullable private DiscordAreaType discordAreaType; - @Nullable - private Varbits varbits; - @Nullable private int[] regionIds; @@ -544,15 +528,6 @@ enum DiscordGameEventType this(state, priority, true, false, false, true, false); } - DiscordGameEventType(String areaName, DiscordAreaType areaType, Varbits varbits) - { - this.state = exploring(areaType, areaName); - this.priority = -2; - this.discordAreaType = areaType; - this.varbits = varbits; - this.shouldClear = true; - } - private static String training(final Skill skill) { return training(skill.getName()); @@ -612,17 +587,4 @@ enum DiscordGameEventType { return FROM_REGION.get(regionId); } - - public static DiscordGameEventType fromVarbit(final Client client) - { - for (DiscordGameEventType fromVarbit : FROM_VARBITS) - { - if (client.getVar(fromVarbit.getVarbits()) != 0) - { - return fromVarbit; - } - } - - return null; - } } 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 2c114de643..cfc0aa80e9 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 @@ -46,7 +46,6 @@ import net.runelite.api.WorldType; import net.runelite.api.coords.WorldPoint; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.StatChanged; -import net.runelite.api.events.VarbitChanged; import net.runelite.client.config.ConfigManager; import net.runelite.client.discord.DiscordService; import net.runelite.client.discord.events.DiscordJoinGame; @@ -210,22 +209,6 @@ public class DiscordPlugin extends Plugin } } - @Subscribe - public void onVarbitChanged(VarbitChanged event) - { - if (!config.showRaidingActivity()) - { - return; - } - - final DiscordGameEventType discordGameEventType = DiscordGameEventType.fromVarbit(client); - - if (discordGameEventType != null) - { - discordState.triggerEvent(discordGameEventType); - } - } - @Subscribe public void onDiscordReady(DiscordReady event) { From 233a44061015641f6d0e7cf7ba7802eaa26264f7 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 5 Feb 2021 15:17:27 -0500 Subject: [PATCH 042/133] api: squash oprs varbits/animID, add ClipBoard --- .../java/com/openosrs/api/AnimationID.java | 239 -------------- .../main/java/com/openosrs/api/Varbits.java | 312 ------------------ .../java/net/runelite/api/AnimationID.java | 235 ++++++++++++- .../main/java/net/runelite/api/Varbits.java | 233 ++++++------- .../com/openosrs/client/util/Clipboard.java | 62 ++++ 5 files changed, 415 insertions(+), 666 deletions(-) delete mode 100644 runelite-api/src/main/java/com/openosrs/api/AnimationID.java delete mode 100644 runelite-api/src/main/java/com/openosrs/api/Varbits.java create mode 100644 runelite-client/src/main/java/com/openosrs/client/util/Clipboard.java diff --git a/runelite-api/src/main/java/com/openosrs/api/AnimationID.java b/runelite-api/src/main/java/com/openosrs/api/AnimationID.java deleted file mode 100644 index 8a3de35994..0000000000 --- a/runelite-api/src/main/java/com/openosrs/api/AnimationID.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.openosrs.api; - -//This is where Animation IDs should go that aren't found in RuneLite -public class AnimationID extends net.runelite.api.AnimationID -{ - - // NPC animations - public static final int TZTOK_JAD_MAGIC_ATTACK = 2656; - public static final int TZTOK_JAD_RANGE_ATTACK = 2652; - public static final int HELLHOUND_DEFENCE = 6566; - - public static final int FISHING_TRAILBLAZER_HARPOON_2 = 8785; - - public static final int CRYSTALLINE_RAT_DEATH = 8334; - public static final int CRYSTALLINE_BAT_DEATH = 4917; - public static final int CRYSTALLINE_WOLF_DEATH = 8335; - public static final int CRYSTALLINE_SPIDER_DEATH = 8338; - public static final int CRYSTALLINE_UNICORN_DEATH = 6377; - public static final int CRYSTALLINE_DRAGON_DEATH = 92; - public static final int CRYSTALLINE_BEAR_DEATH = 4929; - public static final int CRYSTALLINE_DARK_BEAST_DEATH = 2733; - public static final int CORRUPTED_SCORPION_DEATH = 6256; - - public static final int TABLET_TELEPORT = 4069; - - public static final int THIEVING_STALL = 832; - public static final int PICKPOCKET_SUCCESS = 881; - public static final int PULL_LEVER = 2140; - public static final int STANDARD_PURPLE_TELEPORT = 714; - public static final int ECTOPHIAL_TELEPORT = 878; - public static final int FAIRY_RING_TELEPORT = 3265; - public static final int SCROLL_TELEPORT = 3864; - public static final int XERICS_TALISMAN_TELEPORT = 3865; - public static final int WILDERNESS_OBELISK_TELEPORT = 3945; - public static final int SEED_POD_TELEPORT = 4544; - - //block animations for players and perhaps npcs as well? - public static final int BLOCK_DEFENDER = 4177; - public static final int BLOCK_NO_SHIELD = 420; - public static final int BLOCK_SHIELD = 1156; - public static final int BLOCK_SWORD = 388; - public static final int BLOCK_UNARMED = 424; // Same Animation as failed pickpocked - - public static final int NIGHTMARE_DEATH = 8612; - public static final int LOW_LEVEL_MAGIC_ATTACK = 1162; - public static final int HIGH_LEVEL_MAGIC_ATTACK = 1167; - public static final int BLOWPIPE_ATTACK = 5061; - - // NPC animations - public static final int BLACKJACK_KO = 838; - - // Fight Caves - public static final int TZTOK_JAD_MELEE_ATTACK = 2655; - public static final int TOK_XIL_RANGE_ATTACK = 2633; - public static final int TOK_XIL_MELEE_ATTACK = 2628; - public static final int KET_ZEK_MELEE_ATTACK = 2644; - public static final int KET_ZEK_MAGE_ATTACK = 2647; - public static final int MEJ_KOT_MELEE_ATTACK = 2637; - public static final int MEJ_KOT_HEAL_ATTACK = 2639; - - // Vorkath - public static final int VORKATH_WAKE_UP = 7950; - public static final int VORKATH_DEATH = 7949; - public static final int VORKATH_SLASH_ATTACK = 7951; - public static final int VORKATH_ATTACK = 7952; - public static final int VORKATH_FIRE_BOMB_OR_SPAWN_ATTACK = 7960; - public static final int VORKATH_ACID_ATTACK = 7957; - - // Tekton - public static final int TEKTON_ANVIL = 7475; - public static final int TEKTON_AUTO1 = 7482; - public static final int TEKTON_AUTO2 = 7483; - public static final int TEKTON_AUTO3 = 7484; - public static final int TEKTON_FAST_AUTO1 = 7478; - public static final int TEKTON_FAST_AUTO2 = 7488; - public static final int TEKTON_ENRAGE_AUTO1 = 7492; - public static final int TEKTON_ENRAGE_AUTO2 = 7493; - public static final int TEKTON_ENRAGE_AUTO3 = 7494; - - // Hydra - public static final int HYDRA_WALKING = 8232; - public static final int HYDRA_IDLE = 8233; - public static final int HYDRA_POISON_1 = 8234; - public static final int HYDRA_RANGED_1 = 8235; - public static final int HYDRA_MAGIC_1 = 8236; - public static final int HYDRA_1_1 = 8237; - public static final int HYDRA_1_2 = 8238; - public static final int HYDRA_LIGHTNING = 8241; - public static final int HYDRA_RANGED_2 = 8242; - public static final int HYDRA_MAGIC_2 = 8243; - public static final int HYDRA_2_1 = 8244; - public static final int HYDRA_2_2 = 8245; - public static final int HYDRA_FIRE = 8248; - public static final int HYDRA_RANGED_3 = 8249; - public static final int HYDRA_MAGIC_3 = 8250; - public static final int HYDRA_3_1 = 8251; - public static final int HYDRA_3_2 = 8252; - public static final int HYDRA_MAGIC_4 = 8254; - public static final int HYDRA_POISON_4 = 8254; - public static final int HYDRA_RANGED_4 = 8255; - public static final int HYDRA_RANGED_OR_POISON_ATTACK = 8256; - public static final int HYDRA_4_1 = 8257; - public static final int HYDRA_4_2 = 8258; - - // Inferno animations - public static final int JAL_NIB = 7574; - public static final int JAL_MEJRAH = 7578; - public static final int JAL_MEJRAH_STAND = 7577; - public static final int JAL_AK_RANGE_ATTACK = 7581; - public static final int JAL_AK_MELEE_ATTACK = 7582; - public static final int JAL_AK_MAGIC_ATTACK = 7583; - public static final int JAL_IMKOT = 7597; - public static final int JAL_XIL_MELEE_ATTACK = 7604; - public static final int JAL_XIL_RANGE_ATTACK = 7605; - public static final int JAL_ZEK_MAGE_ATTACK = 7610; - public static final int JAL_ZEK_MELEE_ATTACK = 7612; - public static final int JALTOK_JAD_MELEE_ATTACK = 7590; - public static final int JALTOK_JAD_MAGE_ATTACK = 7592; - public static final int JALTOK_JAD_RANGE_ATTACK = 7593; - public static final int TZKAL_ZUK = 7566; - public static final int JAL_MEJJAK = 2858; - - // General Graardor - public static final int MINION_AUTO1 = 6154; - public static final int MINION_AUTO2 = 6156; - public static final int MINION_AUTO3 = 7071; - public static final int MINION_AUTO4 = 7073; - public static final int GENERAL_AUTO1 = 7018; - public static final int GENERAL_AUTO2 = 7020; - public static final int GENERAL_AUTO3 = 7021; - - // Kr'il Tsutsaroth - public static final int ZAMMY_GENERIC_AUTO = 64; - public static final int KRIL_AUTO = 6948; - public static final int KRIL_SPEC = 6950; - public static final int ZAKL_AUTO = 7077; - public static final int BALFRUG_AUTO = 4630; - - // Commander Zilyana - public static final int ZILYANA_MELEE_AUTO = 6964; - public static final int ZILYANA_AUTO = 6967; - public static final int ZILYANA_SPEC = 6970; - public static final int STARLIGHT_AUTO = 6376; - public static final int BREE_AUTO = 7026; - public static final int GROWLER_AUTO = 7037; - - // Kree'arra - public static final int KREE_RANGED = 6978; - public static final int SKREE_AUTO = 6955; - public static final int GEERIN_AUTO = 6956; - public static final int GEERIN_FLINCH = 6958; - public static final int KILISA_AUTO = 6957; - - // Vetion - public static final int VETION_EARTHQUAKE = 5507; - - // Zulrah - public static final int ZULRAH_DEATH = 5804; - public static final int ZULRAH_PHASE = 5072; - - //Dagannoth Kings - public static final int DAG_REX = 2853; - public static final int DAG_PRIME = 2854; - public static final int DAG_SUPREME = 2855; - - // Lizardman shaman - public static final int LIZARDMAN_SHAMAN_SPAWN = 7157; - public static final int LIZARDMAN_SHAMAN_SPAWN_EXPLOSION = 7159; - - // Cerberus - public static final int CERBERUS_MAGIC_ATTACK = 4489; - public static final int CERBERUS_RANGED_ATTACK = 4490; - public static final int CERBERUS_MELEE_ATTACK = 4491; - public static final int CERBERUS_LAVA_ATTACK = 4493; - public static final int CERBERUS_SUMMON_GHOSTS = 4494; - - // Gauntlet Hunleff - public static final int HUNLEFF_TRAMPLE = 8420; - public static final int HUNLEFF_ATTACK = 8419; - public static final int HUNLEFF_TORNADO = 8418; - public static final int HUNLLEF_SWITCH_TO_MAGIC = 8754; - public static final int HUNLLEF_SWITCH_TO_RANGED = 8755; - - //Zalcano - public static final int ZALCANO_KNOCKED_DOWN = 8437; - public static final int ZALCANO_WAKEUP = 8439; - public static final int ZALCANO_ROCK_GLOWING = 8448; - - // Theatre of Blood - Sugadinti Maiden - public static final int SUGADINTI_MAIDEN_BLOOD_SPLAT_ATTACK = 8091; - public static final int SUGADINTI_MAIDEN_MAGIC_ATTACK = 8092; - public static final int SUGADINTI_MAIDEN_DEATH = 8094; - - // Theatre of Blood - Pestilent Bloat - public static final int BLOAT_SLEEP = 8082; - - // Theatre of Blood - Sotetseg - public static final int SOTETSEG_MELEE_ATTACK = 8138; - public static final int SOTETSEG_REGULAR_PROJECTILE_ATTACK = 8139; - - // Theatre of Blood - Verzik Vitur - public static final int VERZIK_PHASE_1_MAGIC_ATTACK = 8109; - public static final int VERZIK_PHASE_1_MAGIC_ATTACK_CHANNEL = 8110; - public static final int VERZIK_CHANGE_TO_PHASE_2 = 8111; - public static final int VERZIK_PHASE_2_MAGIC_ATTACK = 8114; - public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_1 = 8116; - public static final int VERZIK_PHASE_2_HEALING_CHANNEL = 8117; - public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_2 = 8118; - public static final int VERZIK_CHANGE_TO_PHASE_3 = 8119; - public static final int VERZIK_PHASE_3_MELEE_ATTACK = 8123; - public static final int VERZIK_PHASE_3_MAGIC_ATTACK = 8124; - public static final int VERZIK_PHASE_3_RANGED_ATTACK = 8125; - public static final int VERZIK_PHASE_3_GREEN_POOL_ATTACK = 8126; - public static final int VERZIK_PHASE_3_WEB_ATTACK = 8127; - public static final int VERZIK_DEATH_1 = 8128; - public static final int VERZIK_DEATH_2 = 8129; - - // The Nightmare of Ashihama - public static final int NIGHTMARE_SPAWN_SLEEPWALKERS = 8572; - public static final int NIGHTMARE_FLOATY = 8592; - public static final int NIGHTMARE_WALKING = 8592; - public static final int NIGHTMARE_IDLE = 8593; - public static final int NIGHTMARE_MELEE_ATTACK = 8594; - public static final int NIGHTMARE_MAGIC_ATTACK = 8595; - public static final int NIGHTMARE_RANGED_ATTACK = 8596; - public static final int NIGHTMARE_SURGE_ATTACK = 8597; - public static final int NIGHTMARE_GHOST_AOE_ATTACK = 8598; - public static final int NIGHTMARE_CURSE_PRAYERS_ATTACK = 8599; - public static final int NIGHTMARE_SPAWN_INFECTIOUS_SPORES = 8600; - public static final int NIGHTMARE_SPAWN_ROOM_SECTION_FLOWERS = 8601; - public static final int NIGHTMARE_CHANNEL_DEVASTATING_ATTACK = 8604; - public static final int NIGHTMARE_SWITCH_TO_DEVIL_PHASE = 8605; - public static final int NIGHTMARE_PARASITE_ATTACK = 8606; - public static final int NIGHTMARE_JUMP_DOWN = 8607; - public static final int NIGHTMARE_SINK_DOWN = 8608; - public static final int NIGHTMARE_JUMP_UP = 8609; - public static final int NIGHTMARE_JUMP_UP_2 = 8610; - public static final int NIGHTMARE_WAKE_UP = 8611; -} diff --git a/runelite-api/src/main/java/com/openosrs/api/Varbits.java b/runelite-api/src/main/java/com/openosrs/api/Varbits.java deleted file mode 100644 index bc98952741..0000000000 --- a/runelite-api/src/main/java/com/openosrs/api/Varbits.java +++ /dev/null @@ -1,312 +0,0 @@ -package com.openosrs.api; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public enum Varbits -{ - /* - * Kharedst's Memoirs Teleport Item - */ - KHAREDSTS_MEMOIRS_CHARGES(6035), - - /** - * Grand Exchange - */ - GRAND_EXCHANGE_PRICE_PER_ITEM(4398), - - - /** - * Locked Prayers - * 0-7 = Locked - * 8 = Unlocked - */ - CHIVPIETY_UNLOCKED(3909), - - /** - * Locked Prayers - * 0 = Locked - * 1 = Unlocked - */ - - RIGOUR_UNLOCKED(5451), - AUGURY_UNLOCKED(5452), - PRESERVE_UNLOCKED(5453), - - /** - * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating - */ - THEATRE_OF_BLOOD(6440), - BLOAT_DOOR(6447), - - /** - * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb - */ - THEATRE_OF_BLOOD_ORB_1(6442), - THEATRE_OF_BLOOD_ORB_2(6443), - THEATRE_OF_BLOOD_ORB_3(6444), - THEATRE_OF_BLOOD_ORB_4(6445), - THEATRE_OF_BLOOD_ORB_5(6446), - - /** - * Nightmare Zone - */ - NMZ_ABSORPTION(3956), - NMZ_POINTS(3949), - NMZ_OVERLOAD(3955), - - /** - * Pyramid plunder - */ - PYRAMID_PLUNDER_SARCO_OPEN(2362), - PYRAMID_PLUNDER_CHEST_OPEN(2363), - PYRAMID_PLUNDER_ROOM_LOCATION(2365), - PYRAMID_PLUNDER_TIMER(2375), - PYRAMID_PLUNDER_THIEVING_LEVEL(2376), - PYRAMID_PLUNDER_ROOM(2377), - - /** - * In the Wilderness - */ - IN_THE_WILDERNESS(5963), - - /** - * Kingdom Management - */ - KINGDOM_FAVOR(72), - KINGDOM_COFFER(74), - KINGDOM_WORKERS_WOOD(81), - KINGDOM_WORKERS_HERBS(82), - KINGDOM_WORKERS_FISHING(83), - KINGDOM_WORKERS_MINING(84), - KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked - KINGDOM_WORKERS_HARDWOOD(2131), - KINGDOM_WORKERS_FARM(2132), - KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both - KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax - - /** - * Varbit used for Slayer reward points - */ - SLAYER_REWARD_POINTS(4068), - - /** - * 0 = standard - * 1 = ancients - * 2 = lunars - * 3 = arrceus - **/ - SPELLBOOK(4070), - - /** - * Bank settings/flags - **/ - BANK_NOTE_FLAG(3958), - - - /** - * Spells being auto-casted - */ - AUTO_CAST_SPELL(276), - - /** - * Temple Trekking - */ - TREK_POINTS(1955), - TREK_STARTED(1956), - TREK_EVENT(1958), - TREK_STATUS(6719), - BLOAT_ENTERED_ROOM(6447), - - /** - * f2p Quest varbits, these don't hold the completion value. - */ - QUEST_DEMON_SLAYER(2561), - QUEST_GOBLIN_DIPLOMACY(2378), - QUEST_MISTHALIN_MYSTERY(3468), - QUEST_THE_CORSAIR_CURSE(6071), - QUEST_X_MARKS_THE_SPOT(8063), - QUEST_ERNEST_LEVER_A(1788), - QUEST_ERNEST_LEVER_B(1789), - QUEST_ERNEST_LEVER_C(1790), - QUEST_ERNEST_LEVER_D(1791), - QUEST_ERNEST_LEVER_E(1792), - QUEST_ERNEST_LEVER_F(1793), - - /** - * member Quest varbits, these don't hold the completion value. - */ - QUEST_ANIMAL_MAGNETISM(3185), - QUEST_BETWEEN_A_ROCK(299), - QUEST_CONTACT(3274), - QUEST_ZOGRE_FLESH_EATERS(487), - QUEST_DARKNESS_OF_HALLOWVALE(2573), - QUEST_DEATH_TO_THE_DORGESHUUN(2258), - QUEST_DESERT_TREASURE(358), - QUEST_DEVIOUS_MINDS(1465), - QUEST_EAGLES_PEAK(2780), - QUEST_ELEMENTAL_WORKSHOP_II(2639), - QUEST_ENAKHRAS_LAMENT(1560), - QUEST_ENLIGHTENED_JOURNEY(2866), - QUEST_THE_EYES_OF_GLOUPHRIE(2497), - QUEST_FAIRYTALE_I_GROWING_PAINS(1803), - QUEST_FAIRYTALE_II_CURE_A_QUEEN(2326), - QUEST_THE_FEUD(334), // 14 = able to pickpocket - QUEST_FORGETTABLE_TALE(822), - QUEST_GARDEN_OF_TRANQUILLITY(961), - QUEST_GHOSTS_AHOY(217), - QUEST_THE_GIANT_DWARF(571), - QUEST_THE_GOLEM(346), - QUEST_HORROR_FROM_THE_DEEP(34), - QUEST_ICTHLARINS_LITTLE_HELPER(418), - QUEST_IN_AID_OF_THE_MYREQUE(1990), - QUEST_THE_LOST_TRIBE(532), - QUEST_LUNAR_DIPLOMACY(2448), - QUEST_MAKING_HISTORY(1383), - QUEST_MOUNTAIN_DAUGHTER(260), - QUEST_MOURNINGS_END_PART_II(1103), - QUEST_MY_ARMS_BIG_ADVENTURE(2790), - QUEST_RATCATCHERS(1404), - QUEST_RECIPE_FOR_DISASTER(1850), - QUEST_RECRUITMENT_DRIVE(657), - QUEST_ROYAL_TROUBLE(2140), - QUEST_THE_SLUG_MENACE(2610), - QUEST_SHADOW_OF_THE_STORM(1372), - QUEST_A_SOULS_BANE(2011), - QUEST_SPIRITS_OF_THE_ELID(1444), - QUEST_SWAN_SONG(2098), - QUEST_A_TAIL_OF_TWO_CATS(1028), - QUEST_TEARS_OF_GUTHIX(451), - QUEST_WANTED(1051), - QUEST_COLD_WAR(3293), - QUEST_THE_FREMENNIK_ISLES(3311), - QUEST_TOWER_OF_LIFE(3337), - QUEST_WHAT_LIES_BELOW(3523), - QUEST_OLAFS_QUEST(3534), - QUEST_ANOTHER_SLICE_OF_HAM(3550), - QUEST_DREAM_MENTOR(3618), - QUEST_GRIM_TALES(2783), - QUEST_KINGS_RANSOM(3888), - QUEST_MONKEY_MADNESS_II(5027), - QUEST_CLIENT_OF_KOUREND(5619), - QUEST_BONE_VOYAGE(5795), - QUEST_THE_QUEEN_OF_THIEVES(6037), - QUEST_THE_DEPTHS_OF_DESPAIR(6027), - QUEST_DRAGON_SLAYER_II(6104), - QUEST_TALE_OF_THE_RIGHTEOUS(6358), - QUEST_A_TASTE_OF_HOPE(6396), - QUEST_MAKING_FRIENDS_WITH_MY_ARM(6528), - QUEST_THE_ASCENT_OF_ARCEUUS(7856), - QUEST_THE_FORSAKEN_TOWER(7796), - //TODO - QUEST_SONG_OF_THE_ELVES(7796), - - /** - * mini-quest varbits, these don't hold the completion value. - */ - QUEST_ARCHITECTURAL_ALLIANCE(4982), - QUEST_BEAR_YOUR_SOUL(5078), - QUEST_CURSE_OF_THE_EMPTY_LORD(821), - QUEST_ENCHANTED_KEY(1391), - QUEST_THE_GENERALS_SHADOW(3330), - QUEST_SKIPPY_AND_THE_MOGRES(1344), - QUEST_LAIR_OF_TARN_RAZORLOR(3290), - QUEST_FAMILY_PEST(5347), - QUEST_THE_MAGE_ARENA_II(6067), - //TODO - QUEST_IN_SEARCH_OF_KNOWLEDGE(6067), - - /** - * Spellbook filtering (1 = unfiltered, 0 = filtered) - */ - FILTER_SPELLBOOK(6718), - - /** - * POH Building mode (1 = yes, 0 = no) - */ - BUILDING_MODE(2176), - - /** - * 1 if in game, 0 if not - */ - LMS_IN_GAME(5314), - - /** - * Amount of pvp kills in current game - */ - LMS_KILLS(5315), - - /** - * The x coordinate of the final safespace (world coord) - */ - LMS_SAFE_X(5316), - - LMS_POISON_PROGRESS(5317), - - /** - * The y coordinate of the final safespace (world coord) - */ - LMS_SAFE_Y(5320), - - /** - * 1 is true, 0 is false. - */ - GAUNTLET_FINAL_ROOM_ENTERED(9177), - - /** - * 1 is true, 0 is false. - */ - GAUNTLET_ENTERED(9178), - - WITHDRAW_X_AMOUNT(3960), - - IN_PVP_AREA(8121), - - /** - * Value of hotkey varbits can be 0-13 - * 0 corresponds to no hotkey set - * 1-12 correspond to F1-F12 respectively - * 13 corresponds to escape - */ - COMBAT_TAB_HOTKEY(4675), - STATS_TAB_HOTKEY(4676), - QUESTS_TAB_HOTKEY(4677), - INVENTORY_TAB_HOTKEY(4678), - EQUIPMENT_TAB_HOTKEY(4679), - PRAYER_TAB_HOTKEY(4680), - SPELLBOOK_TAB_HOTKEY(4682), - FRIENDS_TAB_HOTKEY(4684), - ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), - LOGOUT_TAB_HOTKEY(4689), - OPTIONS_TAB_HOTKEY(4686), - EMOTES_TAB_HOTKEY(4687), - CLAN_TAB_HOTKEY(4683), - MUSIC_TAB_HOTKEY(4688), - - /** - * Chat Notifications settings - *
- * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false - * LOOT_DROP_NOTIFICATIONS_VALUE: gp value - * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false - * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered - * DROP_ITEM_WARNINGS: 1 is true, 0 is false - * DROP_ITEM_WARNINGS_VALUE: gp value - */ - LOOT_DROP_NOTIFICATIONS(5399), - LOOT_DROP_NOTIFICATIONS_VALUE(5400), - UNTRADEABLE_LOOT_NOTIFICATIONS(5402), - BOSS_KILL_COUNT_UPDATES(4930), - DROP_ITEM_WARNINGS(5411), - DROP_ITEM_WARNINGS_VALUE(5412), - - PARASITE(10151), - ; - - /** - * The raw varbit ID. - */ - private final int id; -} 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 45b2369f23..2b657032d6 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -30,7 +30,7 @@ package net.runelite.api; * Note: This class is not complete and may not contain a specific animation * required. */ -public class AnimationID +public final class AnimationID { public static final int IDLE = -1; public static final int HERBLORE_PESTLE_AND_MORTAR = 364; @@ -251,4 +251,235 @@ public class AnimationID // POH Animations public static final int INCENSE_BURNER = 3687; -} + + //OPENOSRS + + // NPC animations + public static final int FISHING_TRAILBLAZER_HARPOON_2 = 8785; + + public static final int CRYSTALLINE_RAT_DEATH = 8334; + public static final int CRYSTALLINE_BAT_DEATH = 4917; + public static final int CRYSTALLINE_WOLF_DEATH = 8335; + public static final int CRYSTALLINE_SPIDER_DEATH = 8338; + public static final int CRYSTALLINE_UNICORN_DEATH = 6377; + public static final int CRYSTALLINE_DRAGON_DEATH = 92; + public static final int CRYSTALLINE_BEAR_DEATH = 4929; + public static final int CRYSTALLINE_DARK_BEAST_DEATH = 2733; + public static final int CORRUPTED_SCORPION_DEATH = 6256; + + public static final int TABLET_TELEPORT = 4069; + + public static final int THIEVING_STALL = 832; + public static final int PICKPOCKET_SUCCESS = 881; + public static final int PULL_LEVER = 2140; + public static final int STANDARD_PURPLE_TELEPORT = 714; + public static final int ECTOPHIAL_TELEPORT = 878; + public static final int FAIRY_RING_TELEPORT = 3265; + public static final int SCROLL_TELEPORT = 3864; + public static final int XERICS_TALISMAN_TELEPORT = 3865; + public static final int WILDERNESS_OBELISK_TELEPORT = 3945; + public static final int SEED_POD_TELEPORT = 4544; + + //block animations for players and perhaps npcs as well? + public static final int BLOCK_DEFENDER = 4177; + public static final int BLOCK_NO_SHIELD = 420; + public static final int BLOCK_SHIELD = 1156; + public static final int BLOCK_SWORD = 388; + public static final int BLOCK_UNARMED = 424; // Same Animation as failed pickpocked + + public static final int NIGHTMARE_DEATH = 8612; + public static final int LOW_LEVEL_MAGIC_ATTACK = 1162; + public static final int HIGH_LEVEL_MAGIC_ATTACK = 1167; + public static final int BLOWPIPE_ATTACK = 5061; + + // NPC animations + public static final int BLACKJACK_KO = 838; + + // Fight Caves + public static final int TZTOK_JAD_MELEE_ATTACK = 2655; + public static final int TOK_XIL_RANGE_ATTACK = 2633; + public static final int TOK_XIL_MELEE_ATTACK = 2628; + public static final int KET_ZEK_MELEE_ATTACK = 2644; + public static final int KET_ZEK_MAGE_ATTACK = 2647; + public static final int MEJ_KOT_MELEE_ATTACK = 2637; + public static final int MEJ_KOT_HEAL_ATTACK = 2639; + + // Vorkath + public static final int VORKATH_WAKE_UP = 7950; + public static final int VORKATH_DEATH = 7949; + public static final int VORKATH_SLASH_ATTACK = 7951; + public static final int VORKATH_ATTACK = 7952; + public static final int VORKATH_FIRE_BOMB_OR_SPAWN_ATTACK = 7960; + public static final int VORKATH_ACID_ATTACK = 7957; + + // Tekton + public static final int TEKTON_ANVIL = 7475; + public static final int TEKTON_AUTO1 = 7482; + public static final int TEKTON_AUTO2 = 7483; + public static final int TEKTON_AUTO3 = 7484; + public static final int TEKTON_FAST_AUTO1 = 7478; + public static final int TEKTON_FAST_AUTO2 = 7488; + public static final int TEKTON_ENRAGE_AUTO1 = 7492; + public static final int TEKTON_ENRAGE_AUTO2 = 7493; + public static final int TEKTON_ENRAGE_AUTO3 = 7494; + + // Hydra + public static final int HYDRA_WALKING = 8232; + public static final int HYDRA_IDLE = 8233; + public static final int HYDRA_POISON_1 = 8234; + public static final int HYDRA_RANGED_1 = 8235; + public static final int HYDRA_MAGIC_1 = 8236; + public static final int HYDRA_1_1 = 8237; + public static final int HYDRA_1_2 = 8238; + public static final int HYDRA_LIGHTNING = 8241; + public static final int HYDRA_RANGED_2 = 8242; + public static final int HYDRA_MAGIC_2 = 8243; + public static final int HYDRA_2_1 = 8244; + public static final int HYDRA_2_2 = 8245; + public static final int HYDRA_FIRE = 8248; + public static final int HYDRA_RANGED_3 = 8249; + public static final int HYDRA_MAGIC_3 = 8250; + public static final int HYDRA_3_1 = 8251; + public static final int HYDRA_3_2 = 8252; + public static final int HYDRA_MAGIC_4 = 8254; + public static final int HYDRA_POISON_4 = 8254; + public static final int HYDRA_RANGED_4 = 8255; + public static final int HYDRA_RANGED_OR_POISON_ATTACK = 8256; + public static final int HYDRA_4_1 = 8257; + public static final int HYDRA_4_2 = 8258; + + // Inferno animations + public static final int JAL_NIB = 7574; + public static final int JAL_MEJRAH = 7578; + public static final int JAL_MEJRAH_STAND = 7577; + public static final int JAL_AK_RANGE_ATTACK = 7581; + public static final int JAL_AK_MELEE_ATTACK = 7582; + public static final int JAL_AK_MAGIC_ATTACK = 7583; + public static final int JAL_IMKOT = 7597; + public static final int JAL_XIL_MELEE_ATTACK = 7604; + public static final int JAL_XIL_RANGE_ATTACK = 7605; + public static final int JAL_ZEK_MAGE_ATTACK = 7610; + public static final int JAL_ZEK_MELEE_ATTACK = 7612; + public static final int JALTOK_JAD_MELEE_ATTACK = 7590; + public static final int JALTOK_JAD_MAGE_ATTACK = 7592; + public static final int JALTOK_JAD_RANGE_ATTACK = 7593; + public static final int TZKAL_ZUK = 7566; + public static final int JAL_MEJJAK = 2858; + + // General Graardor + public static final int MINION_AUTO1 = 6154; + public static final int MINION_AUTO2 = 6156; + public static final int MINION_AUTO3 = 7071; + public static final int MINION_AUTO4 = 7073; + public static final int GENERAL_AUTO1 = 7018; + public static final int GENERAL_AUTO2 = 7020; + public static final int GENERAL_AUTO3 = 7021; + + // Kr'il Tsutsaroth + public static final int ZAMMY_GENERIC_AUTO = 64; + public static final int KRIL_AUTO = 6948; + public static final int KRIL_SPEC = 6950; + public static final int ZAKL_AUTO = 7077; + public static final int BALFRUG_AUTO = 4630; + + // Commander Zilyana + public static final int ZILYANA_MELEE_AUTO = 6964; + public static final int ZILYANA_AUTO = 6967; + public static final int ZILYANA_SPEC = 6970; + public static final int STARLIGHT_AUTO = 6376; + public static final int BREE_AUTO = 7026; + public static final int GROWLER_AUTO = 7037; + + // Kree'arra + public static final int KREE_RANGED = 6978; + public static final int SKREE_AUTO = 6955; + public static final int GEERIN_AUTO = 6956; + public static final int GEERIN_FLINCH = 6958; + public static final int KILISA_AUTO = 6957; + + // Vetion + public static final int VETION_EARTHQUAKE = 5507; + + // Zulrah + public static final int ZULRAH_DEATH = 5804; + public static final int ZULRAH_PHASE = 5072; + + //Dagannoth Kings + public static final int DAG_REX = 2853; + public static final int DAG_PRIME = 2854; + public static final int DAG_SUPREME = 2855; + + // Lizardman shaman + public static final int LIZARDMAN_SHAMAN_SPAWN = 7157; + public static final int LIZARDMAN_SHAMAN_SPAWN_EXPLOSION = 7159; + + // Cerberus + public static final int CERBERUS_MAGIC_ATTACK = 4489; + public static final int CERBERUS_RANGED_ATTACK = 4490; + public static final int CERBERUS_MELEE_ATTACK = 4491; + public static final int CERBERUS_LAVA_ATTACK = 4493; + public static final int CERBERUS_SUMMON_GHOSTS = 4494; + + // Gauntlet Hunleff + public static final int HUNLEFF_TRAMPLE = 8420; + public static final int HUNLEFF_ATTACK = 8419; + public static final int HUNLEFF_TORNADO = 8418; + public static final int HUNLLEF_SWITCH_TO_MAGIC = 8754; + public static final int HUNLLEF_SWITCH_TO_RANGED = 8755; + + //Zalcano + public static final int ZALCANO_KNOCKED_DOWN = 8437; + public static final int ZALCANO_WAKEUP = 8439; + public static final int ZALCANO_ROCK_GLOWING = 8448; + + // Theatre of Blood - Sugadinti Maiden + public static final int SUGADINTI_MAIDEN_BLOOD_SPLAT_ATTACK = 8091; + public static final int SUGADINTI_MAIDEN_MAGIC_ATTACK = 8092; + public static final int SUGADINTI_MAIDEN_DEATH = 8094; + + // Theatre of Blood - Pestilent Bloat + public static final int BLOAT_SLEEP = 8082; + + // Theatre of Blood - Sotetseg + public static final int SOTETSEG_MELEE_ATTACK = 8138; + public static final int SOTETSEG_REGULAR_PROJECTILE_ATTACK = 8139; + + // Theatre of Blood - Verzik Vitur + public static final int VERZIK_PHASE_1_MAGIC_ATTACK = 8109; + public static final int VERZIK_PHASE_1_MAGIC_ATTACK_CHANNEL = 8110; + public static final int VERZIK_CHANGE_TO_PHASE_2 = 8111; + public static final int VERZIK_PHASE_2_MAGIC_ATTACK = 8114; + public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_1 = 8116; + public static final int VERZIK_PHASE_2_HEALING_CHANNEL = 8117; + public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_2 = 8118; + public static final int VERZIK_CHANGE_TO_PHASE_3 = 8119; + public static final int VERZIK_PHASE_3_MELEE_ATTACK = 8123; + public static final int VERZIK_PHASE_3_MAGIC_ATTACK = 8124; + public static final int VERZIK_PHASE_3_RANGED_ATTACK = 8125; + public static final int VERZIK_PHASE_3_GREEN_POOL_ATTACK = 8126; + public static final int VERZIK_PHASE_3_WEB_ATTACK = 8127; + public static final int VERZIK_DEATH_1 = 8128; + public static final int VERZIK_DEATH_2 = 8129; + + // The Nightmare of Ashihama + public static final int NIGHTMARE_SPAWN_SLEEPWALKERS = 8572; + public static final int NIGHTMARE_FLOATY = 8592; + public static final int NIGHTMARE_WALKING = 8592; + public static final int NIGHTMARE_IDLE = 8593; + public static final int NIGHTMARE_MELEE_ATTACK = 8594; + public static final int NIGHTMARE_MAGIC_ATTACK = 8595; + public static final int NIGHTMARE_RANGED_ATTACK = 8596; + public static final int NIGHTMARE_SURGE_ATTACK = 8597; + public static final int NIGHTMARE_GHOST_AOE_ATTACK = 8598; + public static final int NIGHTMARE_CURSE_PRAYERS_ATTACK = 8599; + public static final int NIGHTMARE_SPAWN_INFECTIOUS_SPORES = 8600; + public static final int NIGHTMARE_SPAWN_ROOM_SECTION_FLOWERS = 8601; + public static final int NIGHTMARE_CHANNEL_DEVASTATING_ATTACK = 8604; + public static final int NIGHTMARE_SWITCH_TO_DEVIL_PHASE = 8605; + public static final int NIGHTMARE_PARASITE_ATTACK = 8606; + public static final int NIGHTMARE_JUMP_DOWN = 8607; + public static final int NIGHTMARE_SINK_DOWN = 8608; + public static final int NIGHTMARE_JUMP_UP = 8609; + public static final int NIGHTMARE_JUMP_UP_2 = 8610; + public static final int NIGHTMARE_WAKE_UP = 8611; +} \ No newline at end of file diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index 32b000971e..a50b41db7f 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -560,7 +560,7 @@ public enum Varbits EXPLORER_RING_ALCHS(4554), EXPLORER_RING_RUNENERGY(4553), - + WINTERTODT_TIMER(7980), /** * League relics @@ -599,76 +599,102 @@ public enum Varbits PVP_SPEC_ORB(8121), //OPENOSRS + /* + * Kharedst's Memoirs Teleport Item + */ + KHAREDSTS_MEMOIRS_CHARGES(6035), /** * Grand Exchange */ GRAND_EXCHANGE_PRICE_PER_ITEM(4398), - /* - * Kharedst's Memoirs Teleport Item - */ - KHAREDSTS_MEMOIRS_CHARGES(6035), - - LMS_POISON_PROGRESS(5317), /** - * The y coordinate of the final safespace (world coord) + * Locked Prayers + * 0-7 = Locked + * 8 = Unlocked */ - LMS_SAFE_Y(5320), + CHIVPIETY_UNLOCKED(3909), /** - * 1 is true, 0 is false. + * Locked Prayers + * 0 = Locked + * 1 = Unlocked */ - GAUNTLET_FINAL_ROOM_ENTERED(9177), + + RIGOUR_UNLOCKED(5451), + AUGURY_UNLOCKED(5452), + PRESERVE_UNLOCKED(5453), /** - * 1 is true, 0 is false. + * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating */ - GAUNTLET_ENTERED(9178), - - WITHDRAW_X_AMOUNT(3960), - - IN_PVP_AREA(8121), + BLOAT_DOOR(6447), /** - * Value of hotkey varbits can be 0-13 - * 0 corresponds to no hotkey set - * 1-12 correspond to F1-F12 respectively - * 13 corresponds to escape + * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb */ - COMBAT_TAB_HOTKEY(4675), - STATS_TAB_HOTKEY(4676), - QUESTS_TAB_HOTKEY(4677), - INVENTORY_TAB_HOTKEY(4678), - EQUIPMENT_TAB_HOTKEY(4679), - PRAYER_TAB_HOTKEY(4680), - SPELLBOOK_TAB_HOTKEY(4682), - FRIENDS_TAB_HOTKEY(4684), - ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), - LOGOUT_TAB_HOTKEY(4689), - OPTIONS_TAB_HOTKEY(4686), - EMOTES_TAB_HOTKEY(4687), - CLAN_TAB_HOTKEY(4683), - MUSIC_TAB_HOTKEY(4688), + THEATRE_OF_BLOOD_ORB_1(6442), + THEATRE_OF_BLOOD_ORB_2(6443), + THEATRE_OF_BLOOD_ORB_3(6444), + THEATRE_OF_BLOOD_ORB_4(6445), + THEATRE_OF_BLOOD_ORB_5(6446), /** - * Chat Notifications settings - *
- * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false - * LOOT_DROP_NOTIFICATIONS_VALUE: gp value - * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false - * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered - * DROP_ITEM_WARNINGS: 1 is true, 0 is false - * DROP_ITEM_WARNINGS_VALUE: gp value + * Nightmare Zone */ - LOOT_DROP_NOTIFICATIONS(5399), - LOOT_DROP_NOTIFICATIONS_VALUE(5400), - UNTRADEABLE_LOOT_NOTIFICATIONS(5402), - BOSS_KILL_COUNT_UPDATES(4930), - DROP_ITEM_WARNINGS(5411), - DROP_ITEM_WARNINGS_VALUE(5412), - /** + NMZ_OVERLOAD(3955), + + /** + * Pyramid plunder + */ + PYRAMID_PLUNDER_SARCO_OPEN(2362), + PYRAMID_PLUNDER_CHEST_OPEN(2363), + + /** + * In the Wilderness + */ + IN_THE_WILDERNESS(5963), + + /** + * Kingdom Management + */ + KINGDOM_WORKERS_WOOD(81), + KINGDOM_WORKERS_HERBS(82), + KINGDOM_WORKERS_FISHING(83), + KINGDOM_WORKERS_MINING(84), + KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked + KINGDOM_WORKERS_HARDWOOD(2131), + KINGDOM_WORKERS_FARM(2132), + KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both + KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax + + /** + * Varbit used for Slayer reward points + */ + SLAYER_REWARD_POINTS(4068), + + /** + * 0 = standard + * 1 = ancients + * 2 = lunars + * 3 = arrceus + **/ + SPELLBOOK(4070), + + /** + * Bank settings/flags + **/ + BANK_NOTE_FLAG(3958), + + + /** + * Spells being auto-casted + */ + AUTO_CAST_SPELL(276), + + /** * Temple Trekking */ TREK_POINTS(1955), @@ -785,8 +811,6 @@ public enum Varbits */ BUILDING_MODE(2176), - WINTERTODT_TIMER(7980), - /** * 1 if in game, 0 if not */ @@ -802,81 +826,64 @@ public enum Varbits */ LMS_SAFE_X(5316), + LMS_POISON_PROGRESS(5317), + /** - * Locked Prayers - * 0-7 = Locked - * 8 = Unlocked + * The y coordinate of the final safespace (world coord) */ - CHIVPIETY_UNLOCKED(3909), + LMS_SAFE_Y(5320), /** - * Locked Prayers - * 0 = Locked - * 1 = Unlocked + * 1 is true, 0 is false. */ + GAUNTLET_FINAL_ROOM_ENTERED(9177), - RIGOUR_UNLOCKED(5451), - AUGURY_UNLOCKED(5452), - PRESERVE_UNLOCKED(5453), - /** - * Spells being auto-casted + * 1 is true, 0 is false. */ - AUTO_CAST_SPELL(276), + GAUNTLET_ENTERED(9178), + + WITHDRAW_X_AMOUNT(3960), + + IN_PVP_AREA(8121), /** - * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating + * Value of hotkey varbits can be 0-13 + * 0 corresponds to no hotkey set + * 1-12 correspond to F1-F12 respectively + * 13 corresponds to escape */ - BLOAT_DOOR(6447), + COMBAT_TAB_HOTKEY(4675), + STATS_TAB_HOTKEY(4676), + QUESTS_TAB_HOTKEY(4677), + INVENTORY_TAB_HOTKEY(4678), + EQUIPMENT_TAB_HOTKEY(4679), + PRAYER_TAB_HOTKEY(4680), + SPELLBOOK_TAB_HOTKEY(4682), + FRIENDS_TAB_HOTKEY(4684), + ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), + LOGOUT_TAB_HOTKEY(4689), + OPTIONS_TAB_HOTKEY(4686), + EMOTES_TAB_HOTKEY(4687), + CLAN_TAB_HOTKEY(4683), + MUSIC_TAB_HOTKEY(4688), /** - * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb + * Chat Notifications settings + *
+ * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false + * LOOT_DROP_NOTIFICATIONS_VALUE: gp value + * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false + * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered + * DROP_ITEM_WARNINGS: 1 is true, 0 is false + * DROP_ITEM_WARNINGS_VALUE: gp value */ - THEATRE_OF_BLOOD_ORB_1(6442), - THEATRE_OF_BLOOD_ORB_2(6443), - THEATRE_OF_BLOOD_ORB_3(6444), - THEATRE_OF_BLOOD_ORB_4(6445), - THEATRE_OF_BLOOD_ORB_5(6446), - - //NMZ - NMZ_OVERLOAD(3955), - - //Pyramid Plunder - PYRAMID_PLUNDER_SARCO_OPEN(2362), - PYRAMID_PLUNDER_CHEST_OPEN(2363), - - /** - * Varbit used for Slayer reward points - */ - SLAYER_REWARD_POINTS(4068), - - /** - * 0 = standard - * 1 = ancients - * 2 = lunars - * 3 = arrceus - **/ - SPELLBOOK(4070), - - /** - * Bank settings/flags - **/ - BANK_NOTE_FLAG(3958), - - KINGDOM_WORKERS_WOOD(81), - KINGDOM_WORKERS_HERBS(82), - KINGDOM_WORKERS_FISHING(83), - KINGDOM_WORKERS_MINING(84), - KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked - KINGDOM_WORKERS_HARDWOOD(2131), - KINGDOM_WORKERS_FARM(2132), - KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both - KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax - - /** - * In the Wilderness - */ - IN_THE_WILDERNESS(5963), + LOOT_DROP_NOTIFICATIONS(5399), + LOOT_DROP_NOTIFICATIONS_VALUE(5400), + UNTRADEABLE_LOOT_NOTIFICATIONS(5402), + BOSS_KILL_COUNT_UPDATES(4930), + DROP_ITEM_WARNINGS(5411), + DROP_ITEM_WARNINGS_VALUE(5412), ; /** diff --git a/runelite-client/src/main/java/com/openosrs/client/util/Clipboard.java b/runelite-client/src/main/java/com/openosrs/client/util/Clipboard.java new file mode 100644 index 0000000000..5f9f236c16 --- /dev/null +++ b/runelite-client/src/main/java/com/openosrs/client/util/Clipboard.java @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2018, Connor + * 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 com.openosrs.client.util; + +import java.awt.Toolkit; +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.StringSelection; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.io.IOException; + +public class Clipboard +{ + public static String retrieve() + { + Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); + + if (contents == null || !contents.isDataFlavorSupported(DataFlavor.stringFlavor)) + { + return null; + } + + try + { + return (String) contents.getTransferData(DataFlavor.stringFlavor); + } + catch (UnsupportedFlavorException | IOException ex) + { + return null; + } + } + + public static void store(String contents) + { + final StringSelection selection = new StringSelection(contents); + + Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); + } +} \ No newline at end of file From 19c912c67cbb8e7cb6a87ba897b78d89435af50d Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 5 Feb 2021 15:17:53 -0500 Subject: [PATCH 043/133] mes: allow swaps from outside plugin --- .../plugins/menuentryswapper/MenuEntrySwapperPlugin.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 b72c138391..6c9b6cd800 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 @@ -152,7 +152,7 @@ public class MenuEntrySwapperPlugin extends Plugin @Getter private boolean configuringShiftClick = false; - private final Multimap swaps = LinkedHashMultimap.create(); + private static final Multimap swaps = LinkedHashMultimap.create(); private final ArrayListMultimap optionIndexes = ArrayListMultimap.create(); @Provides @@ -361,7 +361,7 @@ public class MenuEntrySwapperPlugin extends Plugin swap("eat", "guzzle", config::swapRockCake); } - private void swap(String option, String swappedOption, Supplier enabled) + public static void swap(String option, String swappedOption, Supplier enabled) { swap(option, alwaysTrue(), swappedOption, enabled); } @@ -371,7 +371,7 @@ public class MenuEntrySwapperPlugin extends Plugin swap(option, equalTo(target), swappedOption, enabled); } - private void swap(String option, Predicate targetPredicate, String swappedOption, Supplier enabled) + private static void swap(String option, Predicate targetPredicate, String swappedOption, Supplier enabled) { swaps.put(option, new Swap(alwaysTrue(), targetPredicate, swappedOption, enabled, true)); } From 18826a713c827bedaa95bdeb6f8e15682becfa56 Mon Sep 17 00:00:00 2001 From: Broooklyn <54762282+Broooklyn@users.noreply.github.com> Date: Wed, 27 Jan 2021 21:03:23 -0500 Subject: [PATCH 044/133] ground items: add Nightmare instance to normal despawn timers --- .../client/plugins/grounditems/GroundItemsOverlay.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java index 6f0b96aff9..f565652b11 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java @@ -82,6 +82,7 @@ public class GroundItemsOverlay extends Overlay private static final int GRAARDOR_REGION = 11347; private static final int KRIL_TSUTSAROTH_REGION = 11603; private static final int KREEARRA_REGION = 11346; + private static final int NIGHTMARE_REGION = 15515; private final Client client; private final GroundItemsPlugin plugin; @@ -451,9 +452,9 @@ public class GroundItemsOverlay extends Overlay } } else if (playerRegionID == ZILYANA_REGION || playerRegionID == GRAARDOR_REGION || - playerRegionID == KRIL_TSUTSAROTH_REGION || playerRegionID == KREEARRA_REGION) + playerRegionID == KRIL_TSUTSAROTH_REGION || playerRegionID == KREEARRA_REGION || playerRegionID == NIGHTMARE_REGION) { - // GWD instances use the normal despawn timers + // GWD and Nightmare instances use the normal despawn timers despawnTime = spawnTime.plus(groundItem.getLootType() == LootType.DROPPED ? DESPAWN_TIME_DROP : DESPAWN_TIME_LOOT); From fce6f85d03e32c3411ecfc77c5c5f1381e39ab8f Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Fri, 5 Feb 2021 20:56:57 +0000 Subject: [PATCH 045/133] Release 1.6.39 --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index eaa5cc4bc1..ec55896861 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index e8ed1260ad..0240cc1987 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 88f64d466f..5898de6b8e 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 8ebea1f210..b7ab8bac9e 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 44f7340e3f..92085b5806 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 Web Service diff --git a/pom.xml b/pom.xml index e9c9fb5744..b72dc67652 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 pom RuneLite @@ -61,7 +61,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - HEAD + runelite-parent-1.6.39 diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 9fff909084..e841f1e01e 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 518749315a..ce6ad72802 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 47677f9a03..c64c47dbd2 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39-SNAPSHOT + 1.6.39 script-assembler-plugin From 7fba2eb095afe1238f6238533c7b7ac1d1547dbd Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Fri, 5 Feb 2021 20:57:06 +0000 Subject: [PATCH 046/133] Bump for 1.6.40-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index ec55896861..782e4408cb 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 0240cc1987..20d0eb865c 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 5898de6b8e..305b0e3b7f 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index b7ab8bac9e..e864273049 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 92085b5806..4adddc284a 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index b72dc67652..9ee7f55c10 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT pom RuneLite @@ -61,7 +61,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - runelite-parent-1.6.39 + HEAD diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index e841f1e01e..6cc0d185cc 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index ce6ad72802..10000e6125 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index c64c47dbd2..016b2d854f 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.39 + 1.6.40-SNAPSHOT script-assembler-plugin From cf1f5cf7ceb82117afda3bb8fc8d0843ac31b1d5 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 5 Feb 2021 16:09:19 -0500 Subject: [PATCH 047/133] api: re-implement WidgetHiddenChanged event --- .../api/events/WidgetHiddenChanged.java | 44 +++++++++++++++++++ .../net/runelite/mixins/RSWidgetMixin.java | 43 ++++++++++++++++++ .../java/net/runelite/rs/api/RSWidget.java | 2 + 3 files changed, 89 insertions(+) create mode 100644 runelite-api/src/main/java/net/runelite/api/events/WidgetHiddenChanged.java diff --git a/runelite-api/src/main/java/net/runelite/api/events/WidgetHiddenChanged.java b/runelite-api/src/main/java/net/runelite/api/events/WidgetHiddenChanged.java new file mode 100644 index 0000000000..b66c05267f --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/WidgetHiddenChanged.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2018, 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.events; + +import net.runelite.api.widgets.Widget; +import lombok.Data; + +/** + * An event where the hidden state of a {@link Widget} has been modified. + */ +@Data +public class WidgetHiddenChanged implements Event +{ + /** + * The affected widget. + */ + private Widget widget; + /** + * The new hidden state of the widget. + */ + private boolean hidden; +} \ No newline at end of file diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java index c940376ba4..3bc5fb52ef 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSWidgetMixin.java @@ -28,6 +28,7 @@ import net.runelite.api.HashTable; import net.runelite.api.Node; import net.runelite.api.Point; import net.runelite.api.WidgetNode; +import net.runelite.api.events.WidgetHiddenChanged; import net.runelite.api.events.WidgetPositioned; import net.runelite.api.mixins.Copy; import net.runelite.api.mixins.FieldHook; @@ -74,6 +75,48 @@ public abstract class RSWidgetMixin implements RSWidget rl$y = -1; } + @Inject + @Override + public void broadcastHidden(boolean hidden) + { + WidgetHiddenChanged event = new WidgetHiddenChanged(); + event.setWidget(this); + event.setHidden(hidden); + + client.getCallbacks().post(event); + + RSWidget[] children = getChildren(); + + if (children != null) + { + // recursive through children + for (RSWidget child : children) + { + // if the widget is hidden it will not magically unhide from its parent changing + if (child == null || child.isSelfHidden()) + { + continue; + } + + child.broadcastHidden(hidden); + } + } + + // make sure we iterate nested children as well + // cannot be null + Widget[] nestedChildren = getNestedChildren(); + + for (Widget nestedChild : nestedChildren) + { + if (nestedChild == null || nestedChild.isSelfHidden()) + { + continue; + } + + ((RSWidget) nestedChild).broadcastHidden(hidden); + } + } + @Inject @Override public void setRenderParentId(int parentId) 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 ace10f8461..57546ccfe4 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 @@ -588,4 +588,6 @@ public interface RSWidget extends Widget @Import("onVarTransmit") @Override void setOnVarTransmitListener(Object[] o); + + void broadcastHidden(boolean hidden); } \ No newline at end of file From a579bdb121286638c2a3d71294d25d243781af39 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 5 Feb 2021 22:04:27 -0500 Subject: [PATCH 048/133] api: add client/ui/overlay/components/table --- .../components/table/TableAlignment.java | 32 ++ .../components/table/TableComponent.java | 465 ++++++++++++++++++ .../components/table/TableElement.java | 38 ++ .../ui/overlay/components/table/TableRow.java | 41 ++ 4 files changed, 576 insertions(+) create mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableAlignment.java create mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableComponent.java create mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableElement.java create mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableRow.java diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableAlignment.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableAlignment.java new file mode 100644 index 0000000000..6061e639f8 --- /dev/null +++ b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableAlignment.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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 com.openosrs.client.ui.overlay.components.table; + +public enum TableAlignment +{ + LEFT, + CENTER, + RIGHT +} diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableComponent.java new file mode 100644 index 0000000000..6f96434e6d --- /dev/null +++ b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableComponent.java @@ -0,0 +1,465 @@ +/* + * Copyright (c) 2018, Jordan Atwood + * Copyright (c) 2019, TheStonedTurtle + * 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 com.openosrs.client.ui.overlay.components.table; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics2D; +import java.awt.Point; +import java.awt.Rectangle; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import lombok.Getter; +import lombok.NonNull; +import lombok.Setter; +import net.runelite.api.util.Text; +import net.runelite.client.ui.overlay.components.ComponentConstants; +import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; +import net.runelite.client.ui.overlay.components.TextComponent; + +@Setter +public class TableComponent implements LayoutableRenderableEntity +{ + private static final TableElement EMPTY_ELEMENT = TableElement.builder().build(); + + @Getter + private final List columns = new ArrayList<>(); + @Getter + private final List rows = new ArrayList<>(); + + @Getter + private final Rectangle bounds = new Rectangle(); + + private TableAlignment defaultAlignment = TableAlignment.LEFT; + private Color defaultColor = Color.WHITE; + private Dimension gutter = new Dimension(3, 0); + private Point preferredLocation = new Point(); + private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0); + + @Override + public Dimension render(final Graphics2D graphics) + { + final FontMetrics metrics = graphics.getFontMetrics(); + final TableRow colRow = TableRow.builder().elements(this.columns).build(); + final int[] columnWidths = getColumnWidths(metrics, colRow); + + graphics.translate(preferredLocation.x, preferredLocation.y); + + // Display the columns first + int height = displayRow(graphics, colRow, 0, columnWidths, metrics); + + for (TableRow row : this.rows) + { + height = displayRow(graphics, row, height, columnWidths, metrics); + } + + graphics.translate(-preferredLocation.x, -preferredLocation.y); + + final Dimension dimension = new Dimension(preferredSize.width, height); + bounds.setLocation(preferredLocation); + bounds.setSize(dimension); + + return dimension; + } + + private int displayRow(Graphics2D graphics, TableRow row, int height, int[] columnWidths, FontMetrics metrics) + { + int x = 0; + int startingRowHeight = height; + + final List elements = row.getElements(); + for (int i = 0; i < elements.size(); i++) + { + int y = startingRowHeight; + final TableElement cell = elements.get(i); + + final String content = cell.getContent(); + if (content == null) + { + continue; + } + + final String[] lines = lineBreakText(content, columnWidths[i], metrics); + final TableAlignment alignment = getCellAlignment(row, i); + final Color color = getCellColor(row, i); + + for (String line : lines) + { + final int alignmentOffset = getAlignedPosition(line, alignment, columnWidths[i], metrics); + final TextComponent leftLineComponent = new TextComponent(); + y += metrics.getHeight(); + + leftLineComponent.setPosition(new Point(x + alignmentOffset, y)); + leftLineComponent.setText(line); + leftLineComponent.setColor(color); + leftLineComponent.render(graphics); + } + height = Math.max(height, y); + x += columnWidths[i] + gutter.width; + } + + return height + gutter.height; + } + + /** + * Returns the width that each column should take up + * Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width + * + * @param metrics + * @return int[] of column width + */ + private int[] getColumnWidths(final FontMetrics metrics, final TableRow columnRow) + { + int numCols = columns.size(); + for (final TableRow r : rows) + { + numCols = Math.max(r.getElements().size(), numCols); + } + + int[] maxtextw = new int[numCols]; // max text width over all rows + int[] maxwordw = new int[numCols]; // max width of longest word + boolean[] flex = new boolean[numCols]; // is column flexible? + boolean[] wrap = new boolean[numCols]; // can column be wrapped? + int[] finalcolw = new int[numCols]; // final width of columns + + final List rows = new ArrayList<>(this.rows); + rows.add(columnRow); + + for (final TableRow r : rows) + { + final List elements = r.getElements(); + for (int col = 0; col < elements.size(); col++) + { + final TableElement ele = elements.get(col); + final String cell = ele.getContent(); + if (cell == null) + { + continue; + } + + final int cellWidth = getTextWidth(metrics, cell); + + maxtextw[col] = Math.max(maxtextw[col], cellWidth); + for (String word : cell.split(" ")) + { + maxwordw[col] = Math.max(maxwordw[col], getTextWidth(metrics, word)); + } + + if (maxtextw[col] == cellWidth) + { + wrap[col] = cell.contains(" "); + } + } + } + + int left = preferredSize.width - (numCols - 1) * gutter.width; + final double avg = left / numCols; + int nflex = 0; + + // Determine whether columns should be flexible and assign width of non-flexible cells + for (int col = 0; col < numCols; col++) + { + // This limit can be adjusted as needed + final double maxNonFlexLimit = 1.5 * avg; + + flex[col] = maxtextw[col] > maxNonFlexLimit; + if (flex[col]) + { + nflex++; + } + else + { + finalcolw[col] = maxtextw[col]; + left -= finalcolw[col]; + } + } + + // If there is not enough space, make columns that could be word-wrapped flexible too + if (left < nflex * avg) + { + for (int col = 0; col < numCols; col++) + { + if (!flex[col] && wrap[col]) + { + left += finalcolw[col]; + finalcolw[col] = 0; + flex[col] = true; + nflex++; + } + } + } + + // Calculate weights for flexible columns. The max width is capped at the table width to + // treat columns that have to be wrapped more or less equal + int tot = 0; + for (int col = 0; col < numCols; col++) + { + if (flex[col]) + { + maxtextw[col] = Math.min(maxtextw[col], preferredSize.width); + tot += maxtextw[col]; + } + } + + // Now assign the actual width for flexible columns. Make sure that it is at least as long + // as the longest word length + for (int col = 0; col < numCols; col++) + { + if (flex[col]) + { + finalcolw[col] = left * maxtextw[col] / tot; + finalcolw[col] = Math.max(finalcolw[col], maxwordw[col]); + left -= finalcolw[col]; + } + } + + // When the sum of column widths is less than the total space available, distribute the + // extra space equally across all columns + final int extraPerCol = left / numCols; + for (int col = 0; col < numCols; col++) + { + finalcolw[col] += extraPerCol; + left -= extraPerCol; + } + // Add any remainder to the right-most column + finalcolw[finalcolw.length - 1] += left; + + return finalcolw; + } + + private static int getTextWidth(final FontMetrics metrics, final String cell) + { + return metrics.stringWidth(Text.removeTags(cell)); + } + + private static String[] lineBreakText(final String text, final int maxWidth, final FontMetrics metrics) + { + final String[] words = text.split(" "); + + if (words.length == 0) + { + return new String[0]; + } + + final StringBuilder wrapped = new StringBuilder(words[0]); + int spaceLeft = maxWidth - getTextWidth(metrics, wrapped.toString()); + + for (int i = 1; i < words.length; i++) + { + final String word = words[i]; + final int wordLen = getTextWidth(metrics, word); + final int spaceWidth = metrics.stringWidth(" "); + + if (wordLen + spaceWidth > spaceLeft) + { + wrapped.append("\n").append(word); + spaceLeft = maxWidth - wordLen; + } + else + { + wrapped.append(" ").append(word); + spaceLeft -= spaceWidth + wordLen; + } + } + + return wrapped.toString().split("\n"); + } + + public boolean isEmpty() + { + return columns.size() == 0 || rows.size() == 0; + } + + private void ensureColumnSize(final int size) + { + while (size > columns.size()) + { + columns.add(TableElement.builder().build()); + } + } + + private static int getAlignedPosition(final String str, final TableAlignment alignment, final int columnWidth, final FontMetrics metrics) + { + final int stringWidth = getTextWidth(metrics, str); + int offset = 0; + + switch (alignment) + { + case LEFT: + break; + case CENTER: + offset = (columnWidth / 2) - (stringWidth / 2); + break; + case RIGHT: + offset = columnWidth - stringWidth; + break; + } + return offset; + } + + /** + * Returns the color for the specified table element. + * Priority order: cell->row->column->default + * + * @param row TableRow element + * @param colIndex column index + */ + private Color getCellColor(final TableRow row, final int colIndex) + { + final List rowElements = row.getElements(); + final TableElement cell = colIndex < rowElements.size() ? rowElements.get(colIndex) : EMPTY_ELEMENT; + final TableElement column = colIndex < columns.size() ? columns.get(colIndex) : EMPTY_ELEMENT; + + return firstNonNull( + cell.getColor(), + row.getRowColor(), + column.getColor(), + defaultColor); + } + + private void setColumnAlignment(final int col, final TableAlignment alignment) + { + assert columns.size() > col; + columns.get(col).setAlignment(alignment); + } + + public void setColumnAlignments(@Nonnull final TableAlignment... alignments) + { + ensureColumnSize(alignments.length); + for (int i = 0; i < alignments.length; i++) + { + setColumnAlignment(i, alignments[i]); + } + } + + /** + * Returns the alignment for the specified table element. + * Priority order: cell->row->column->default + * + * @param row TableRow element + * @param colIndex column index + */ + private TableAlignment getCellAlignment(final TableRow row, final int colIndex) + { + final List rowElements = row.getElements(); + final TableElement cell = colIndex < rowElements.size() ? rowElements.get(colIndex) : EMPTY_ELEMENT; + final TableElement column = colIndex < columns.size() ? columns.get(colIndex) : EMPTY_ELEMENT; + + return firstNonNull( + cell.getAlignment(), + row.getRowAlignment(), + column.getAlignment(), + defaultAlignment); + } + + @SafeVarargs + private static T firstNonNull(@Nullable T... elements) + { + if (elements == null || elements.length == 0) + { + return null; + } + + int i = 0; + T cur = elements[0]; + while (cur == null && i < elements.length) + { + cur = elements[i]; + i++; + } + + return cur; + } + + // Helper functions for cleaner overlay code + public void addRow(@Nonnull final String... cells) + { + final List elements = new ArrayList<>(); + for (final String cell : cells) + { + elements.add(TableElement.builder().content(cell).build()); + } + + final TableRow row = TableRow.builder().build(); + row.setElements(elements); + + this.rows.add(row); + } + + private void addRows(@Nonnull final String[]... rows) + { + for (String[] row : rows) + { + addRow(row); + } + } + + public void addRows(@NonNull final TableRow... rows) + { + this.rows.addAll(Arrays.asList(rows)); + } + + public void setRows(@Nonnull final String[]... elements) + { + this.rows.clear(); + addRows(elements); + } + + public void setRows(@Nonnull final TableRow... elements) + { + this.rows.clear(); + this.rows.addAll(Arrays.asList(elements)); + } + + private void addColumn(@Nonnull final String col) + { + this.columns.add(TableElement.builder().content(col).build()); + } + + public void addColumns(@NonNull final TableElement... columns) + { + this.columns.addAll(Arrays.asList(columns)); + } + + public void setColumns(@Nonnull final TableElement... elements) + { + this.columns.clear(); + this.columns.addAll(Arrays.asList(elements)); + } + + public void setColumns(@Nonnull final String... columns) + { + this.columns.clear(); + for (String col : columns) + { + addColumn(col); + } + } +} diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableElement.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableElement.java new file mode 100644 index 0000000000..daf7eb5790 --- /dev/null +++ b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableElement.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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 com.openosrs.client.ui.overlay.components.table; + +import java.awt.Color; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class TableElement +{ + TableAlignment alignment; + Color color; + String content; +} diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableRow.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableRow.java new file mode 100644 index 0000000000..9451bba547 --- /dev/null +++ b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableRow.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2019, TheStonedTurtle + * 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 com.openosrs.client.ui.overlay.components.table; + +import java.awt.Color; +import java.util.Collections; +import java.util.List; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class TableRow +{ + Color rowColor; + TableAlignment rowAlignment; + @Builder.Default + List elements = Collections.emptyList(); +} From e08e16fa48cfaa6e9024ed758448a8aaa996afc1 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 5 Feb 2021 22:18:06 -0500 Subject: [PATCH 049/133] npcstats: add static getter for attack speed --- .../src/main/java/com/openosrs/client/game/NPCStats.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/runelite-client/src/main/java/com/openosrs/client/game/NPCStats.java b/runelite-client/src/main/java/com/openosrs/client/game/NPCStats.java index 93071976a7..ccf623ba78 100644 --- a/runelite-client/src/main/java/com/openosrs/client/game/NPCStats.java +++ b/runelite-client/src/main/java/com/openosrs/client/game/NPCStats.java @@ -207,6 +207,14 @@ public class NPCStats } }; + public static int getAttackSpeed(int npcId) + { + if (statsMap != null) + if (statsMap.get(npcId) != null) + return statsMap.get(npcId).getAttackSpeed(); + return -1; + } + public static void loadStats() throws IOException { try (JsonReader reader = new JsonReader(new InputStreamReader(NPCStats.class.getResourceAsStream("npc_stats.json"), StandardCharsets.UTF_8))) From 681216c25dd0ca33bb6e8adfffda3d74701b9940 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 5 Feb 2021 22:28:50 -0500 Subject: [PATCH 050/133] bootstrapper: use pretty formatting --- buildSrc/src/main/kotlin/BootstrapTask.kt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/BootstrapTask.kt b/buildSrc/src/main/kotlin/BootstrapTask.kt index 3eaf7dcebb..0e141ee23d 100644 --- a/buildSrc/src/main/kotlin/BootstrapTask.kt +++ b/buildSrc/src/main/kotlin/BootstrapTask.kt @@ -1,3 +1,4 @@ +import groovy.json.JsonOutput import org.gradle.api.DefaultTask import org.gradle.api.file.RegularFileProperty import org.gradle.api.tasks.* @@ -117,11 +118,13 @@ open class BootstrapTask @Inject constructor(@Input val type: String) : DefaultT "artifacts" to getArtifacts() ).toString() + val prettyJson = JsonOutput.prettyPrint(json) + val bootstrapDir = File("${project.buildDir}/bootstrap") bootstrapDir.mkdirs() File(bootstrapDir, "bootstrap-${type}.json").printWriter().use { out -> - out.println(json) + out.println(prettyJson) } } } From 73d481302b9eb72dad538921469ae9d81f5c9cb1 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Feb 2021 23:23:19 -0500 Subject: [PATCH 051/133] icon text field: use only awt key listener This key listener wrapper is unnecssary and was using the RL key listener, which is meant for client keys, instead of the awt key listener --- .../skillcalculator/SkillCalculator.java | 11 +++++++- .../client/ui/components/IconTextField.java | 26 ------------------- 2 files changed, 10 insertions(+), 27 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java index 64422ee34c..bd688d3788 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java @@ -30,6 +30,8 @@ import java.awt.Color; import java.awt.Dimension; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; +import java.awt.event.KeyAdapter; +import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.text.DecimalFormat; @@ -95,7 +97,14 @@ class SkillCalculator extends JPanel searchBar.setBackground(ColorScheme.DARKER_GRAY_COLOR); searchBar.setHoverBackgroundColor(ColorScheme.DARK_GRAY_HOVER_COLOR); searchBar.addClearListener(this::onSearch); - searchBar.addKeyListener(e -> onSearch()); + searchBar.addKeyListener(new KeyAdapter() + { + @Override + public void keyTyped(KeyEvent e) + { + onSearch(); + } + }); setLayout(new DynamicGridLayout(0, 1, 0, 5)); diff --git a/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java b/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java index b4c2ff1127..05806ada73 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/components/IconTextField.java @@ -33,14 +33,12 @@ import java.awt.Font; import java.awt.event.ActionListener; import java.awt.event.FocusAdapter; import java.awt.event.FocusEvent; -import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.util.ArrayList; import java.util.List; -import java.util.function.Consumer; import javax.swing.DefaultListModel; import javax.swing.ImageIcon; import javax.swing.JButton; @@ -335,30 +333,6 @@ public class IconTextField extends JPanel clearListeners.add(clearListener); } - public void addKeyListener(Consumer keyEventConsumer) - { - addKeyListener(new net.runelite.client.input.KeyListener() - { - @Override - public void keyTyped(KeyEvent e) - { - keyEventConsumer.accept(e); - } - - @Override - public void keyPressed(KeyEvent e) - { - keyEventConsumer.accept(e); - } - - @Override - public void keyReleased(KeyEvent e) - { - keyEventConsumer.accept(e); - } - }); - } - @Override public void removeKeyListener(KeyListener keyListener) { From f13be14280d8fc7d6f12c05ea0f3807daabaddf4 Mon Sep 17 00:00:00 2001 From: Adam Date: Fri, 5 Feb 2021 23:25:34 -0500 Subject: [PATCH 052/133] bank tags: fix tag named clue coexisting with the special "clue" tab --- .../java/net/runelite/client/plugins/banktags/TagManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 b03028e3d8..3cfac27242 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 @@ -115,9 +115,9 @@ public class TagManager boolean findTag(int itemId, String search) { BankTag bankTag = customTags.get(search); - if (bankTag != null) + if (bankTag != null && bankTag.contains(itemId)) { - return bankTag.contains(itemId); + return true; } Collection tags = getTags(itemId, false); From ad6e26715197092b93f63e679c834a58f4d15e0c Mon Sep 17 00:00:00 2001 From: zeruth Date: Sat, 6 Feb 2021 01:17:47 -0500 Subject: [PATCH 053/133] api: expand animID / overlayUtil --- .../java/net/runelite/api/AnimationID.java | 22 +++++++++++++ .../client/ui/overlay/OverlayUtil.java | 33 +++++++++++++++++++ 2 files changed, 55 insertions(+) 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 2b657032d6..d86d33e89c 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -482,4 +482,26 @@ public final class AnimationID public static final int NIGHTMARE_JUMP_UP = 8609; public static final int NIGHTMARE_JUMP_UP_2 = 8610; public static final int NIGHTMARE_WAKE_UP = 8611; + + //Supplies Tracker + public static final int ONEHAND_SLASH_SWORD_ANIMATION = 390; + public static final int ONEHAND_STAB_SWORD_ANIMATION = 386; + public static final int SCYTHE_OF_VITUR_ANIMATION = 8056; + public static final int LOW_LEVEL_STANDARD_SPELLS = 711; + public static final int WAVE_SPELL_ANIMATION = 727; + public static final int SURGE_SPELL_ANIMATION = 7855; + public static final int HIGH_ALCH_ANIMATION = 713; + public static final int LUNAR_HUMIDIFY = 6294; + public static final int PRAY_AT_ALTAR = 645; + public static final int ENSOULED_HEADS_ANIMATION = 7198; + + // Weapon attack animations + public static final int ONEHAND_SLASH_AXE_ANIMATION = 395; + public static final int ONEHAND_CRUSH_PICKAXE_ANIMATION = 400; + public static final int ONEHAND_CRUSH_AXE_ANIMATION = 401; + public static final int UNARMED_PUNCH_ANIMATION = 422; + public static final int UNARMED_KICK_ANIMATION = 423; + public static final int BOW_ATTACK_ANIMATION = 426; + public static final int ONEHAND_STAB_HALBERD_ANIMATION = 428; + public static final int ONEHAND_SLASH_HALBERD_ANIMATION = 440; } \ No newline at end of file diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/OverlayUtil.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/OverlayUtil.java index 0c4f611be5..47a6d2e716 100644 --- a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/OverlayUtil.java +++ b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/OverlayUtil.java @@ -138,4 +138,37 @@ public class OverlayUtil extends net.runelite.client.ui.overlay.OverlayUtil graphics.fill(poly); graphics.setStroke(originalStroke); } + + public static void renderAreaTilePolygon(Graphics2D graphics, Shape poly, Color color) + { + graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 10)); + graphics.fill(poly); + } + + public static void renderFullLine(Graphics2D graphics, int[][] line, Color color) + { + graphics.setColor(color); + final Stroke originalStroke = graphics.getStroke(); + graphics.setStroke(new BasicStroke(2)); + graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]); + graphics.setStroke(originalStroke); + } + + public static void renderDashedLine(Graphics2D graphics, int[][] line, Color color) + { + graphics.setColor(color); + final Stroke originalStroke = graphics.getStroke(); + graphics.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0)); + graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]); + graphics.setStroke(originalStroke); + } + + public static void renderOutlinePolygon(Graphics2D graphics, Shape poly, Color color) + { + graphics.setColor(color); + final Stroke originalStroke = graphics.getStroke(); + graphics.setStroke(new BasicStroke(2)); + graphics.draw(poly); + graphics.setStroke(originalStroke); + } } From fe51dfb05b7b27f2f51418b15436c11dab783f81 Mon Sep 17 00:00:00 2001 From: loldudester Date: Thu, 14 Jan 2021 09:52:28 +0000 Subject: [PATCH 054/133] TimeTracking: Move birdhouse schedule to ScheduledExecutorService --- .../client/plugins/timetracking/TimeTrackingPlugin.java | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java index 310c2e4e4a..f43f4a2d99 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java @@ -29,7 +29,6 @@ import com.google.inject.Inject; import com.google.inject.Provides; import java.awt.image.BufferedImage; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; @@ -58,7 +57,6 @@ import net.runelite.client.plugins.timetracking.clocks.ClockManager; import net.runelite.client.plugins.timetracking.farming.FarmingContractManager; import net.runelite.client.plugins.timetracking.farming.FarmingTracker; import net.runelite.client.plugins.timetracking.hunter.BirdHouseTracker; -import net.runelite.client.task.Schedule; import net.runelite.client.ui.ClientToolbar; import net.runelite.client.ui.NavigationButton; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; @@ -102,6 +100,8 @@ public class TimeTrackingPlugin extends Plugin private ScheduledFuture panelUpdateFuture; + private ScheduledFuture notifierFuture; + private TimeTrackingPanel panel; private NavigationButton navButton; @@ -139,6 +139,7 @@ public class TimeTrackingPlugin extends Plugin clientToolbar.addNavigation(navButton); panelUpdateFuture = executorService.scheduleAtFixedRate(this::updatePanel, 200, 200, TimeUnit.MILLISECONDS); + notifierFuture = executorService.scheduleAtFixedRate(this::checkCompletion, 0, 10, TimeUnit.SECONDS); } @Override @@ -153,6 +154,7 @@ public class TimeTrackingPlugin extends Plugin panelUpdateFuture = null; } + notifierFuture.cancel(true); clientToolbar.removeNavigation(navButton); infoBoxManager.removeInfoBox(farmingContractManager.getInfoBox()); farmingContractManager.setInfoBox(null); @@ -260,8 +262,7 @@ public class TimeTrackingPlugin extends Plugin } } - @Schedule(period = 10, unit = ChronoUnit.SECONDS) - public void checkCompletion() + private void checkCompletion() { boolean birdHouseDataChanged = birdHouseTracker.checkCompletion(); From daf0d1c48b33db599162a3e0beafd9ceeee68614 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 6 Feb 2021 14:06:19 -0500 Subject: [PATCH 055/133] perspective: support rectangular tile areas --- .../java/net/runelite/api/Perspective.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/Perspective.java b/runelite-api/src/main/java/net/runelite/api/Perspective.java index eab5323d38..8c309ccf3d 100644 --- a/runelite-api/src/main/java/net/runelite/api/Perspective.java +++ b/runelite-api/src/main/java/net/runelite/api/Perspective.java @@ -375,7 +375,7 @@ public class Perspective */ public static Polygon getCanvasTilePoly(@Nonnull Client client, @Nonnull LocalPoint localLocation, int zOffset) { - return getCanvasTileAreaPoly(client, localLocation, 1, zOffset); + return getCanvasTileAreaPoly(client, localLocation, 1, 1, zOffset); } /** @@ -388,7 +388,7 @@ public class Perspective */ public static Polygon getCanvasTileAreaPoly(@Nonnull Client client, @Nonnull LocalPoint localLocation, int size) { - return getCanvasTileAreaPoly(client, localLocation, size, 0); + return getCanvasTileAreaPoly(client, localLocation, size, size, 0); } /** @@ -396,23 +396,25 @@ public class Perspective * * @param client the game client * @param localLocation the center location of the AoE - * @param size the size of the area (ie. 3x3 AoE evaluates to size 3) + * @param sizeX the size of the area in tiles on the x axis + * @param sizeY the size of the area in tiles on the y axis * @param zOffset offset from ground plane * @return a polygon representing the tiles in the area */ public static Polygon getCanvasTileAreaPoly( @Nonnull Client client, @Nonnull LocalPoint localLocation, - int size, + int sizeX, + int sizeY, int zOffset) { final int plane = client.getPlane(); - final int swX = localLocation.getX() - (size * LOCAL_TILE_SIZE / 2); - final int swY = localLocation.getY() - (size * LOCAL_TILE_SIZE / 2); + final int swX = localLocation.getX() - (sizeX * LOCAL_TILE_SIZE / 2); + final int swY = localLocation.getY() - (sizeY * LOCAL_TILE_SIZE / 2); - final int neX = localLocation.getX() + (size * LOCAL_TILE_SIZE / 2); - final int neY = localLocation.getY() + (size * LOCAL_TILE_SIZE / 2); + final int neX = localLocation.getX() + (sizeX * LOCAL_TILE_SIZE / 2); + final int neY = localLocation.getY() + (sizeY * LOCAL_TILE_SIZE / 2); final int seX = swX; final int seY = neY; From 089756189f0eea885bb5223458de972b194f9230 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 6 Feb 2021 14:07:32 -0500 Subject: [PATCH 056/133] devtools: fix drawing game object overlays multiple times This also removes the player mesh overlay and the gameobject convex hull overlay since they are unnecessary --- .../plugins/devtools/DevToolsOverlay.java | 64 +++---------------- 1 file changed, 10 insertions(+), 54 deletions(-) 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 b4a9f65337..208107e836 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 @@ -43,8 +43,6 @@ import net.runelite.api.Constants; import net.runelite.api.DecorativeObject; import net.runelite.api.GameObject; import net.runelite.api.GraphicsObject; -import net.runelite.api.TileItem; -import net.runelite.api.GroundObject; import net.runelite.api.ItemLayer; import net.runelite.api.NPC; import net.runelite.api.NPCComposition; @@ -55,7 +53,8 @@ import net.runelite.api.Point; import net.runelite.api.Projectile; import net.runelite.api.Scene; import net.runelite.api.Tile; -import net.runelite.api.WallObject; +import net.runelite.api.TileItem; +import net.runelite.api.TileObject; import net.runelite.api.coords.LocalPoint; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetInfo; @@ -154,7 +153,6 @@ class DevToolsOverlay extends Overlay String text = local.getName() + " (A: " + local.getAnimation() + ") (P: " + local.getPoseAnimation() + ") (G: " + local.getGraphic() + ")"; OverlayUtil.renderActorOverlay(graphics, local, text, CYAN); - renderPlayerWireframe(graphics, local, CYAN); } private void renderNpcs(Graphics2D graphics) @@ -214,7 +212,7 @@ class DevToolsOverlay extends Overlay if (plugin.getGroundObjects().isActive()) { - renderGroundObject(graphics, tile, player); + renderTileObject(graphics, tile.getGroundObject(), player, PURPLE); } if (plugin.getGameObjects().isActive()) @@ -224,7 +222,7 @@ class DevToolsOverlay extends Overlay if (plugin.getWalls().isActive()) { - renderWallObject(graphics, tile, player); + renderTileObject(graphics, tile.getWallObject(), player, GRAY); } if (plugin.getDecorations().isActive()) @@ -309,45 +307,21 @@ class DevToolsOverlay extends Overlay { for (GameObject gameObject : gameObjects) { - if (gameObject != null) + if (gameObject != null && gameObject.getSceneMinLocation().equals(tile.getSceneLocation())) { - if (player.getLocalLocation().distanceTo(gameObject.getLocalLocation()) <= MAX_DISTANCE) - { - OverlayUtil.renderTileOverlay(graphics, gameObject, "ID: " + gameObject.getId(), GREEN); - } - - // Draw a polygon around the convex hull - // of the model vertices - Shape p = gameObject.getConvexHull(); - if (p != null) - { - graphics.draw(p); - } + renderTileObject(graphics, gameObject, player, GREEN); } } } } - private void renderGroundObject(Graphics2D graphics, Tile tile, Player player) + private void renderTileObject(Graphics2D graphics, TileObject tileObject, Player player, Color color) { - GroundObject groundObject = tile.getGroundObject(); - if (groundObject != null) + if (tileObject != null) { - if (player.getLocalLocation().distanceTo(groundObject.getLocalLocation()) <= MAX_DISTANCE) + if (player.getLocalLocation().distanceTo(tileObject.getLocalLocation()) <= MAX_DISTANCE) { - OverlayUtil.renderTileOverlay(graphics, groundObject, "ID: " + groundObject.getId(), PURPLE); - } - } - } - - private void renderWallObject(Graphics2D graphics, Tile tile, Player player) - { - WallObject wallObject = tile.getWallObject(); - if (wallObject != null) - { - if (player.getLocalLocation().distanceTo(wallObject.getLocalLocation()) <= MAX_DISTANCE) - { - OverlayUtil.renderTileOverlay(graphics, wallObject, "ID: " + wallObject.getId(), GRAY); + OverlayUtil.renderTileOverlay(graphics, tileObject, "ID: " + tileObject.getId(), color); } } } @@ -447,22 +421,4 @@ class DevToolsOverlay extends Overlay } } } - - private void renderPlayerWireframe(Graphics2D graphics, Player player, Color color) - { - Polygon[] polys = player.getPolygons(); - - if (polys == null) - { - return; - } - - graphics.setColor(color); - - for (Polygon p : polys) - { - graphics.drawPolygon(p); - } - } - } From 19db6bf8caf9e621bb5ed960d405ba5e11ec3d02 Mon Sep 17 00:00:00 2001 From: loldudester Date: Thu, 14 Jan 2021 09:46:46 +0000 Subject: [PATCH 057/133] TimeTracking: Add farming notifications --- .../runelite/client/config/ConfigManager.java | 5 + .../timetracking/TimeTrackingConfig.java | 1 + .../timetracking/TimeTrackingPlugin.java | 4 +- .../plugins/timetracking/TimeablePanel.java | 30 ++- .../timetracking/farming/FarmingPatch.java | 13 +- .../timetracking/farming/FarmingRegion.java | 4 +- .../timetracking/farming/FarmingTabPanel.java | 49 +++-- .../timetracking/farming/FarmingTracker.java | 172 ++++++++++++++++-- .../timetracking/farming/FarmingWorld.java | 72 ++++---- .../timetracking/farming/ProfilePatch.java | 34 ++++ .../plugins/timetracking/notify_icon.png | Bin 0 -> 463 bytes .../timetracking/notify_selected_icon.png | Bin 0 -> 310 bytes 12 files changed, 319 insertions(+), 65 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/ProfilePatch.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_icon.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_selected_icon.png 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 a1cc3ee8b5..549ef0b123 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 @@ -152,6 +152,11 @@ public class ConfigManager scheduledExecutorService.scheduleWithFixedDelay(this::sendConfig, 30, 30, TimeUnit.SECONDS); } + public String getRSProfileKey() + { + return rsProfileKey; + } + public final void switchSession(AccountSession session) { // Ensure existing config is saved diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java index 28704ff3ce..aa284ee86c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java @@ -41,6 +41,7 @@ public interface TimeTrackingConfig extends Config String TIMERS = "timers"; String STOPWATCHES = "stopwatches"; String PREFER_SOONEST = "preferSoonest"; + String NOTIFY = "notify"; @ConfigItem( keyName = "timeFormatMode", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java index f43f4a2d99..7eee98df8c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingPlugin.java @@ -139,7 +139,7 @@ public class TimeTrackingPlugin extends Plugin clientToolbar.addNavigation(navButton); panelUpdateFuture = executorService.scheduleAtFixedRate(this::updatePanel, 200, 200, TimeUnit.MILLISECONDS); - notifierFuture = executorService.scheduleAtFixedRate(this::checkCompletion, 0, 10, TimeUnit.SECONDS); + notifierFuture = executorService.scheduleAtFixedRate(this::checkCompletion, 10, 10, TimeUnit.SECONDS); } @Override @@ -270,6 +270,8 @@ public class TimeTrackingPlugin extends Plugin { panel.update(); } + + farmingTracker.checkCompletion(); } private void updatePanel() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeablePanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeablePanel.java index e7584a6e27..9796146500 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeablePanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeablePanel.java @@ -29,22 +29,29 @@ import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.GridLayout; +import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JPanel; +import javax.swing.JToggleButton; import javax.swing.border.EmptyBorder; import lombok.Getter; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.Constants; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; import net.runelite.client.ui.components.ThinProgressBar; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.SwingUtil; +@Slf4j @Getter public class TimeablePanel extends JPanel { private final T timeable; private final JLabel icon = new JLabel(); private final JLabel farmingContractIcon = new JLabel(); + private final JToggleButton notifyButton = new JToggleButton(); private final JLabel estimate = new JLabel(); private final ThinProgressBar progress = new ThinProgressBar(); private final JLabel text; @@ -79,8 +86,29 @@ public class TimeablePanel extends JPanel infoPanel.add(text); infoPanel.add(estimate); + ImageIcon notifyIcon = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_icon.png")); + ImageIcon notifySelectedIcon = new ImageIcon(ImageUtil.loadImageResource(TimeTrackingPlugin.class, "notify_selected_icon.png")); + + notifyButton.setPreferredSize(new Dimension(30, 16)); + notifyButton.setBorder(new EmptyBorder(0, 0, 0, 10)); + notifyButton.setIcon(notifyIcon); + notifyButton.setSelectedIcon(notifySelectedIcon); + SwingUtil.removeButtonDecorations(notifyButton); + SwingUtil.addModalTooltip(notifyButton, "Disable notifications", "Enable notifications"); + + JPanel notifyPanel = new JPanel(); + notifyPanel.setLayout(new BorderLayout()); + notifyPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + notifyPanel.add(notifyButton, BorderLayout.CENTER); + + JPanel iconPanel = new JPanel(); + iconPanel.setLayout(new BorderLayout()); + iconPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + iconPanel.add(notifyPanel, BorderLayout.EAST); + iconPanel.add(farmingContractIcon, BorderLayout.WEST); + + topContainer.add(iconPanel, BorderLayout.EAST); topContainer.add(icon, BorderLayout.WEST); - topContainer.add(farmingContractIcon, BorderLayout.EAST); topContainer.add(infoPanel, BorderLayout.CENTER); progress.setValue(0); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingPatch.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingPatch.java index 8c24588917..25b29ba01f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingPatch.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingPatch.java @@ -29,6 +29,7 @@ import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.Setter; import net.runelite.api.Varbits; +import net.runelite.client.plugins.timetracking.TimeTrackingConfig; @RequiredArgsConstructor( access = AccessLevel.PACKAGE @@ -41,4 +42,14 @@ class FarmingPatch private final String name; private final Varbits varbit; private final PatchImplementation implementation; -} + + String configKey() + { + return region.getRegionID() + "." + varbit.getId(); + } + + String notifyConfigKey() + { + return TimeTrackingConfig.NOTIFY + "." + region.getRegionID() + "." + varbit.getId(); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingRegion.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingRegion.java index a759d2c31b..bb149af373 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingRegion.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingRegion.java @@ -33,13 +33,15 @@ public class FarmingRegion { private final String name; private final int regionID; + private final boolean definite; private final FarmingPatch[] patches; private final Varbits[] varbits; - FarmingRegion(String name, int regionID, FarmingPatch... patches) + FarmingRegion(String name, int regionID, boolean definite, FarmingPatch... patches) { this.name = name; this.regionID = regionID; + this.definite = definite; this.patches = patches; this.varbits = new Varbits[patches.length]; for (int i = 0; i < patches.length; i++) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java index 1c068790c8..18d525c1e0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTabPanel.java @@ -33,8 +33,11 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; import javax.swing.JLabel; +import javax.swing.JToggleButton; import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; import net.runelite.api.ItemID; +import net.runelite.client.config.ConfigManager; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.timetracking.TabContentPanel; import net.runelite.client.plugins.timetracking.TimeTrackingConfig; @@ -42,10 +45,12 @@ import net.runelite.client.plugins.timetracking.TimeablePanel; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; +@Slf4j public class FarmingTabPanel extends TabContentPanel { private final FarmingTracker farmingTracker; private final ItemManager itemManager; + private final ConfigManager configManager; private final TimeTrackingConfig config; private final List> patchPanels; private final FarmingContractManager farmingContractManager; @@ -53,6 +58,7 @@ public class FarmingTabPanel extends TabContentPanel FarmingTabPanel( FarmingTracker farmingTracker, ItemManager itemManager, + ConfigManager configManager, TimeTrackingConfig config, Set patches, FarmingContractManager farmingContractManager @@ -60,6 +66,7 @@ public class FarmingTabPanel extends TabContentPanel { this.farmingTracker = farmingTracker; this.itemManager = itemManager; + this.configManager = configManager; this.config = config; this.patchPanels = new ArrayList<>(); this.farmingContractManager = farmingContractManager; @@ -103,6 +110,18 @@ public class FarmingTabPanel extends TabContentPanel lastImpl = patch.getImplementation(); } + // Set toggle state of notification menu on icon click; + JToggleButton toggleNotify = p.getNotifyButton(); + String configKey = patch.notifyConfigKey(); + + toggleNotify.addActionListener(e -> + { + if (configManager.getRSProfileKey() != null) + { + configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, configKey, toggleNotify.isSelected()); + } + }); + patchPanels.add(p); add(p, c); c.gridy++; @@ -197,18 +216,26 @@ public class FarmingTabPanel extends TabContentPanel { panel.getProgress().setVisible(false); } - JLabel farmingContractIcon = panel.getFarmingContractIcon(); - if (farmingContractManager.shouldHighlightFarmingTabPanel(patch)) - { - itemManager.getImage(ItemID.SEED_PACK).addTo(farmingContractIcon); - farmingContractIcon.setToolTipText(farmingContractManager.getContract().getName()); - } - else - { - farmingContractIcon.setIcon(null); - farmingContractIcon.setToolTipText(""); - } } + + JLabel farmingContractIcon = panel.getFarmingContractIcon(); + if (farmingContractManager.shouldHighlightFarmingTabPanel(patch)) + { + itemManager.getImage(ItemID.SEED_PACK).addTo(farmingContractIcon); + farmingContractIcon.setToolTipText(farmingContractManager.getContract().getName()); + } + else + { + farmingContractIcon.setIcon(null); + farmingContractIcon.setToolTipText(""); + } + + String configKey = patch.notifyConfigKey(); + JToggleButton toggleNotify = panel.getNotifyButton(); + boolean notifyEnabled = Boolean.TRUE + .equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, configKey, Boolean.class)); + + toggleNotify.setSelected(notifyEnabled); } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java index a5c953f324..e01c451b8d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java @@ -29,20 +29,28 @@ import com.google.inject.Singleton; import java.time.Instant; import java.util.Collection; import java.util.EnumMap; +import java.util.HashMap; +import java.util.List; +import java.util.Locale; import java.util.Map; import java.util.Set; import javax.annotation.Nullable; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; +import net.runelite.api.GameState; import net.runelite.api.Varbits; import net.runelite.api.coords.WorldPoint; import net.runelite.api.vars.Autoweed; import net.runelite.api.widgets.WidgetModalMode; +import net.runelite.client.Notifier; import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneScapeProfile; +import net.runelite.client.config.RuneScapeProfileType; import net.runelite.client.game.ItemManager; import net.runelite.client.plugins.timetracking.SummaryState; import net.runelite.client.plugins.timetracking.Tab; import net.runelite.client.plugins.timetracking.TimeTrackingConfig; +import net.runelite.client.util.Text; @Slf4j @Singleton @@ -53,6 +61,7 @@ public class FarmingTracker private final ConfigManager configManager; private final TimeTrackingConfig config; private final FarmingWorld farmingWorld; + private final Notifier notifier; private final Map summaries = new EnumMap<>(Tab.class); @@ -61,23 +70,26 @@ public class FarmingTracker * or {@code -1} if we have no data about any patch of the given type. */ private final Map completionTimes = new EnumMap<>(Tab.class); + Map wasNotified = new HashMap<>(); private boolean newRegionLoaded; private Collection lastRegions; + private boolean firstNotifyCheck = true; @Inject - private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager, TimeTrackingConfig config, FarmingWorld farmingWorld) + private FarmingTracker(Client client, ItemManager itemManager, ConfigManager configManager, TimeTrackingConfig config, FarmingWorld farmingWorld, Notifier notifier) { this.client = client; this.itemManager = itemManager; this.configManager = configManager; this.config = config; this.farmingWorld = farmingWorld; + this.notifier = notifier; } public FarmingTabPanel createTabPanel(Tab tab, FarmingContractManager farmingContractManager) { - return new FarmingTabPanel(this, itemManager, config, farmingWorld.getTabs().get(tab), farmingContractManager); + return new FarmingTabPanel(this, itemManager, configManager, config, farmingWorld.getTabs().get(tab), farmingContractManager); } /** @@ -130,7 +142,7 @@ public class FarmingTracker { // Write the config value if it doesn't match what is current, or it is more than 5 minutes old Varbits varbit = patch.getVarbit(); - String key = region.getRegionID() + "." + varbit.getId(); + String key = patch.configKey(); String strVarbit = Integer.toString(client.getVar(varbit)); String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key); @@ -189,6 +201,12 @@ public class FarmingTracker configManager.setRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET, offsetMins); } } + if (currentPatchState.getTickRate() != 0 + // Don't set wasNotified to false if witnessing a check-health action + && !(previousPatchState.getCropState() == CropState.GROWING && currentPatchState.getCropState() == CropState.HARVESTABLE && currentPatchState.getProduce().getPatchImplementation().isHealthCheckRequired())) + { + wasNotified.put(new ProfilePatch(patch, configManager.getRSProfileKey()), false); + } } else { @@ -258,17 +276,23 @@ public class FarmingTracker @Nullable public PatchPrediction predictPatch(FarmingPatch patch) + { + return predictPatch(patch, configManager.getRSProfileKey()); + } + + @Nullable + public PatchPrediction predictPatch(FarmingPatch patch, String profile) { long unixNow = Instant.now().getEpochSecond(); boolean autoweed = Integer.toString(Autoweed.ON.ordinal()) - .equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.AUTOWEED)); + .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.AUTOWEED)); boolean botanist = Boolean.TRUE - .equals(configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.BOTANIST, Boolean.class)); + .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.BOTANIST, Boolean.class)); - String key = patch.getRegion().getRegionID() + "." + patch.getVarbit().getId(); - String storedValue = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, key); + String key = patch.configKey(); + String storedValue = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, key); if (storedValue == null) { @@ -323,11 +347,11 @@ public class FarmingTracker long doneEstimate = 0; if (tickrate > 0) { - long tickNow = getTickTime(tickrate, 0, unixNow); - long tickTime = getTickTime(tickrate, 0, unixTime); + long tickNow = getTickTime(tickrate, 0, unixNow, profile); + long tickTime = getTickTime(tickrate, 0, unixTime, profile); int delta = (int) (tickNow - tickTime) / (tickrate * 60); - doneEstimate = getTickTime(tickrate, stages - 1 - stage, tickTime); + doneEstimate = getTickTime(tickrate, stages - 1 - stage, tickTime, profile); stage += delta; if (stage >= stages) @@ -347,13 +371,13 @@ public class FarmingTracker public long getTickTime(int tickRate, int ticks) { - return getTickTime(tickRate, ticks, Instant.now().getEpochSecond()); + return getTickTime(tickRate, ticks, Instant.now().getEpochSecond(), configManager.getRSProfileKey()); } - public long getTickTime(int tickRate, int ticks, long requestedTime) + public long getTickTime(int tickRate, int ticks, long requestedTime, String profile) { - Integer offsetPrecisionMins = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class); - Integer offsetTimeMins = configManager.getRSProfileConfiguration(TimeTrackingConfig.CONFIG_GROUP, TimeTrackingConfig.FARM_TICK_OFFSET, int.class); + Integer offsetPrecisionMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class); + Integer offsetTimeMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile, TimeTrackingConfig.FARM_TICK_OFFSET, int.class); //All offsets are negative but are stored as positive long calculatedOffsetTime = 0L; @@ -465,4 +489,124 @@ public class FarmingTracker completionTimes.put(tab.getKey(), completionTime); } } + + public void checkCompletion() + { + List rsProfiles = configManager.getRSProfiles(); + long unixNow = Instant.now().getEpochSecond(); + + for (RuneScapeProfile profile : rsProfiles) + { + Integer offsetPrecisionMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class); + Integer offsetTimeMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), TimeTrackingConfig.FARM_TICK_OFFSET, int.class); + + RuneScapeProfileType profileType = profile.getType(); + + for (Map.Entry> tab : farmingWorld.getTabs().entrySet()) + { + for (FarmingPatch patch : tab.getValue()) + { + ProfilePatch profilePatch = new ProfilePatch(patch, profile.getKey()); + boolean patchNotified = (wasNotified.get(profilePatch) != null ? wasNotified.get(profilePatch) : false); + String configKey = patch.notifyConfigKey(); + boolean shouldNotify = Boolean.TRUE + .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), configKey, Boolean.class)); + PatchPrediction prediction = predictPatch(patch, profile.getKey()); + + if (prediction == null) + { + continue; + } + + int tickRate = prediction.getProduce().getTickrate(); + + if (offsetPrecisionMins == null || offsetTimeMins == null || (offsetPrecisionMins < tickRate && offsetPrecisionMins < 40) || prediction.getProduce() == Produce.WEEDS + || unixNow <= prediction.getDoneEstimate() || patchNotified || prediction.getCropState() == CropState.FILLING) + { + continue; + } + + wasNotified.put(profilePatch, true); + + if (!firstNotifyCheck && shouldNotify) + { + final StringBuilder notificationStringBuilder = new StringBuilder(); + // Same RS account + if (client.getGameState() == GameState.LOGGED_IN && profile.getDisplayName().equals(client.getLocalPlayer().getName())) + { + // Same RS account but different profile type + if (profileType != RuneScapeProfileType.getCurrent(client)) + { + notificationStringBuilder.append("(") + .append(Text.titleCase(profile.getType())) + .append(") "); + } + // Same RS account AND profile falls through here so no bracketed prefix is added + } + else + { + // Different RS account AND profile type + if (profileType != RuneScapeProfileType.getCurrent(client) || client.getGameState() == GameState.LOGIN_SCREEN) + { + //Don't print profile type when logged out if is STANDARD + if (client.getGameState() == GameState.LOGIN_SCREEN && profileType == RuneScapeProfileType.STANDARD) + { + notificationStringBuilder.append("(") + .append(profile.getDisplayName()) + .append(") "); + } + else + { + notificationStringBuilder.append("(") + .append(profile.getDisplayName()) + .append(" - ") + .append(Text.titleCase(profile.getType())) + .append(") "); + } + } + // Different RS account but same profile type + else + { + notificationStringBuilder.append("(") + .append(profile.getDisplayName()) + .append(") "); + } + } + + notificationStringBuilder + .append("Your ") + .append(prediction.getProduce().getName()); + + switch (prediction.getCropState()) + { + case HARVESTABLE: + case GROWING: + if (prediction.getProduce().getName().toLowerCase(Locale.ENGLISH).contains("compost")) + { + notificationStringBuilder.append(" is ready to collect in "); + } + else + { + notificationStringBuilder.append(" is ready to harvest in "); + } + break; + case DISEASED: + notificationStringBuilder.append(" has become diseased in "); + break; + case DEAD: + notificationStringBuilder.append(" has died in "); + break; + } + + notificationStringBuilder.append(patch.getRegion().isDefinite() ? "the " : "") + .append(patch.getRegion().getName()) + .append("."); + + notifier.notify(notificationStringBuilder.toString()); + } + } + } + } + firstNotifyCheck = false; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java index 7ca46e130c..5429240775 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingWorld.java @@ -64,14 +64,14 @@ class FarmingWorld { // Some of these patches get updated in multiple regions. // It may be worth it to add a specialization for these patches - add(new FarmingRegion("Al Kharid", 13106, + add(new FarmingRegion("Al Kharid", 13106, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CACTUS) ), 13362, 13105); - add(new FarmingRegion("Ardougne", 10290, + add(new FarmingRegion("Ardougne", 10290, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) ), 10546); - add(new FarmingRegion("Ardougne", 10548, + add(new FarmingRegion("Ardougne", 10548, false, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -79,12 +79,12 @@ class FarmingWorld new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) )); - add(new FarmingRegion("Brimhaven", 11058, + add(new FarmingRegion("Brimhaven", 11058, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) ), 11057); - add(new FarmingRegion("Catherby", 11062, + add(new FarmingRegion("Catherby", 11062, false, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -103,7 +103,7 @@ class FarmingWorld return true; } }, 11061, 11318, 11317); - add(new FarmingRegion("Catherby", 11317, + add(new FarmingRegion("Catherby", 11317, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) ) { @@ -115,27 +115,27 @@ class FarmingWorld } }); - add(new FarmingRegion("Champions' Guild", 12596, + add(new FarmingRegion("Champions' Guild", 12596, true, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) )); - add(new FarmingRegion("Draynor Manor", 12340, + add(new FarmingRegion("Draynor Manor", 12340, false, new FarmingPatch("Belladonna", Varbits.FARMING_4771, PatchImplementation.BELLADONNA) )); - add(new FarmingRegion("Entrana", 11060, + add(new FarmingRegion("Entrana", 11060, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) ), 11316); - add(new FarmingRegion("Etceteria", 10300, + add(new FarmingRegion("Etceteria", 10300, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.SPIRIT_TREE) )); - add(new FarmingRegion("Falador", 11828, + add(new FarmingRegion("Falador", 11828, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 12084); - add(new FarmingRegion("Falador", 12083, + add(new FarmingRegion("Falador", 12083, false, new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -151,7 +151,7 @@ class FarmingWorld } }); - add(new FarmingRegion("Fossil Island", 14651, + add(new FarmingRegion("Fossil Island", 14651, false, new FarmingPatch("East", Varbits.FARMING_4771, PatchImplementation.HARDWOOD_TREE), new FarmingPatch("Middle", Varbits.FARMING_4772, PatchImplementation.HARDWOOD_TREE), new FarmingPatch("West", Varbits.FARMING_4773, PatchImplementation.HARDWOOD_TREE) @@ -179,22 +179,22 @@ class FarmingWorld return loc.getPlane() == 0; } }, 14907, 14908, 15164, 14652, 14906, 14650, 15162, 15163); - add(new FarmingRegion("Seaweed", 15008, + add(new FarmingRegion("Seaweed", 15008, false, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.SEAWEED), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.SEAWEED) )); - add(new FarmingRegion("Gnome Stronghold", 9781, + add(new FarmingRegion("Gnome Stronghold", 9781, true, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.FRUIT_TREE) ), 9782, 9526, 9525); - add(new FarmingRegion("Harmony", 15148, + add(new FarmingRegion("Harmony", 15148, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.HERB) )); - add(new FarmingRegion("Kourend", 6967, + add(new FarmingRegion("Kourend", 6967, false, new FarmingPatch("North East", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South West", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -202,7 +202,7 @@ class FarmingWorld new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST), new FarmingPatch("", Varbits.FARMING_7904, PatchImplementation.SPIRIT_TREE) ), 6711); - add(new FarmingRegion("Kourend", 7223, + add(new FarmingRegion("Kourend", 7223, false, new FarmingPatch("East 1", Varbits.GRAPES_4953, PatchImplementation.GRAPES), new FarmingPatch("East 2", Varbits.GRAPES_4954, PatchImplementation.GRAPES), new FarmingPatch("East 3", Varbits.GRAPES_4955, PatchImplementation.GRAPES), @@ -217,21 +217,21 @@ class FarmingWorld new FarmingPatch("West 6", Varbits.GRAPES_4964, PatchImplementation.GRAPES) )); - add(new FarmingRegion("Lletya", 9265, + add(new FarmingRegion("Lletya", 9265, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) ), 11103); - add(new FarmingRegion("Lumbridge", 12851, + add(new FarmingRegion("Lumbridge", 12851, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) )); - add(new FarmingRegion("Lumbridge", 12594, + add(new FarmingRegion("Lumbridge", 12594, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 12850); - add(new FarmingRegion("Morytania", 13622, + add(new FarmingRegion("Morytania", 13622, false, new FarmingPatch("Mushroom", Varbits.FARMING_4771, PatchImplementation.MUSHROOM) ), 13878); - add(new FarmingRegion("Morytania", 14391, + add(new FarmingRegion("Morytania", 14391, false, new FarmingPatch("North West", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South East", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), @@ -239,7 +239,7 @@ class FarmingWorld new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) ), 14390); - add(new FarmingRegion("Port Sarim", 12082, + add(new FarmingRegion("Port Sarim", 12082, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.SPIRIT_TREE) ) { @@ -250,48 +250,48 @@ class FarmingWorld } }, 12083); - add(new FarmingRegion("Rimmington", 11570, + add(new FarmingRegion("Rimmington", 11570, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.BUSH) ), 11826); - add(new FarmingRegion("Seers' Village", 10551, + add(new FarmingRegion("Seers' Village", 10551, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) ), 10550); - add(new FarmingRegion("Tai Bwo Wannai", 11056, + add(new FarmingRegion("Tai Bwo Wannai", 11056, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.CALQUAT) )); - add(new FarmingRegion("Taverley", 11573, + add(new FarmingRegion("Taverley", 11573, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 11829); - add(new FarmingRegion("Tree Gnome Village", 9777, + add(new FarmingRegion("Tree Gnome Village", 9777, true, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.FRUIT_TREE) ), 10033); - add(new FarmingRegion("Troll Stronghold", 11321, + add(new FarmingRegion("Troll Stronghold", 11321, true, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB) )); - add(new FarmingRegion("Varrock", 12854, + add(new FarmingRegion("Varrock", 12854, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.TREE) ), 12853); - add(new FarmingRegion("Yanille", 10288, + add(new FarmingRegion("Yanille", 10288, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HOPS) )); - add(new FarmingRegion("Weiss", 11325, + add(new FarmingRegion("Weiss", 11325, false, new FarmingPatch("", Varbits.FARMING_4771, PatchImplementation.HERB) )); - add(new FarmingRegion("Farming Guild", 5021, + add(new FarmingRegion("Farming Guild", 5021, true, new FarmingPatch("Hespori", Varbits.FARMING_7908, PatchImplementation.HESPORI) )); //Full 3x3 region area centered on farming guild - add(farmingGuildRegion = new FarmingRegion("Farming Guild", 4922, + add(farmingGuildRegion = new FarmingRegion("Farming Guild", 4922, true, new FarmingPatch("", Varbits.FARMING_7905, PatchImplementation.TREE), new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.HERB), new FarmingPatch("", Varbits.FARMING_4772, PatchImplementation.BUSH), @@ -308,7 +308,7 @@ class FarmingWorld ), 5177, 5178, 5179, 4921, 4923, 4665, 4666, 4667); //All of Prifddinas, and all of Prifddinas Underground - add(new FarmingRegion("Prifddinas", 13151, + add(new FarmingRegion("Prifddinas", 13151, false, new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/ProfilePatch.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/ProfilePatch.java new file mode 100644 index 0000000000..d928c1f8e0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/ProfilePatch.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2021 Hannah Ryan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.timetracking.farming; + +import lombok.Value; + +@Value +class ProfilePatch +{ + FarmingPatch patch; + String rsProfileKey; +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/timetracking/notify_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..4a4667bedb0951701f740880f10c36ac304018c4 GIT binary patch literal 463 zcmV;=0WkiFP)VHnEt9wX6PnW9nfDX0>j2x>#9^H|2VB4{t_y2dxWi&w;1uq$KOnak zx#>2*GpuF0U&VPF;7JD7({WSaWbTX#T8g8o1&+t4(-JMv%n6d> zIlM(aU6WMcJ>aQmiYtK$@>=Tn20Jl-EW`uVk07*qo IM6N<$g56t(YybcN literal 0 HcmV?d00001 From 5de258310a3b89de62c172e4561a12d373b0dd12 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 6 Feb 2021 12:00:48 -0700 Subject: [PATCH 058/133] http-api: allow gsoning classes outside of net.runelite This broke plugin hub plugins using our gson, which is valid since you can reflect into any unnamed module. --- .../http/api/gson/IllegalReflectionExclusion.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/http-api/src/main/java/net/runelite/http/api/gson/IllegalReflectionExclusion.java b/http-api/src/main/java/net/runelite/http/api/gson/IllegalReflectionExclusion.java index d7611cf83f..a25ffb79f7 100644 --- a/http-api/src/main/java/net/runelite/http/api/gson/IllegalReflectionExclusion.java +++ b/http-api/src/main/java/net/runelite/http/api/gson/IllegalReflectionExclusion.java @@ -27,13 +27,26 @@ package net.runelite.http.api.gson; import com.google.gson.ExclusionStrategy; import com.google.gson.FieldAttributes; import java.lang.reflect.Modifier; +import java.util.HashSet; +import java.util.Set; public class IllegalReflectionExclusion implements ExclusionStrategy { + private static final Set PRIVATE_CLASSLOADERS = new HashSet<>(); + + static + { + for (ClassLoader cl = ClassLoader.getSystemClassLoader(); cl != null; ) + { + cl = cl.getParent(); + PRIVATE_CLASSLOADERS.add(cl); + } + } + @Override public boolean shouldSkipField(FieldAttributes f) { - if (f.getDeclaringClass().getName().startsWith("net.runelite")) + if (!PRIVATE_CLASSLOADERS.contains(f.getDeclaringClass().getClassLoader())) { return false; } From 37838ac836db6d8c98f2c776c1b2e537c83dcd85 Mon Sep 17 00:00:00 2001 From: Liam Schmidt Date: Mon, 1 Feb 2021 21:33:52 -0500 Subject: [PATCH 059/133] screenshot: Add valuable drop threshold config --- .../plugins/screenshot/ScreenshotConfig.java | 20 ++++++++++++--- .../plugins/screenshot/ScreenshotPlugin.java | 12 ++++++--- .../screenshot/ScreenshotPluginTest.java | 25 ++++++++++++++++++- 3 files changed, 48 insertions(+), 9 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java index 269eb8554a..44d9d58b51 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotConfig.java @@ -205,11 +205,23 @@ public interface ScreenshotConfig extends Config return false; } + @ConfigItem( + keyName = "valuableDropThreshold", + name = "Valuable Threshold", + description = "The minimum value to save screenshots of valuable drops.", + position = 14, + section = whatSection + ) + default int valuableDropThreshold() + { + return 0; + } + @ConfigItem( keyName = "untradeableDrop", name = "Screenshot Untradeable drops", description = "Configures whether or not screenshots are automatically taken when you receive an untradeable drop.", - position = 14, + position = 15, section = whatSection ) default boolean screenshotUntradeableDrop() @@ -221,7 +233,7 @@ public interface ScreenshotConfig extends Config keyName = "ccKick", name = "Screenshot Kicks from FC", description = "Take a screenshot when you kick a user from a friends chat.", - position = 15, + position = 16, section = whatSection ) default boolean screenshotKick() @@ -233,7 +245,7 @@ public interface ScreenshotConfig extends Config keyName = "baHighGamble", name = "Screenshot BA high gambles", description = "Take a screenshot of your reward from a high gamble at Barbarian Assault.", - position = 16, + position = 17, section = whatSection ) default boolean screenshotHighGamble() @@ -245,7 +257,7 @@ public interface ScreenshotConfig extends Config keyName = "hotkey", name = "Screenshot hotkey", description = "When you press this key a screenshot will be taken", - position = 17 + position = 18 ) default Keybind hotkey() { 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 b199248c39..0dada91ae6 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 @@ -99,7 +99,7 @@ public class ScreenshotPlugin extends Plugin private static final Pattern NUMBER_PATTERN = Pattern.compile("([0-9]+)"); private static final Pattern LEVEL_UP_PATTERN = Pattern.compile(".*Your ([a-zA-Z]+) (?:level is|are)? now (\\d+)\\."); private static final Pattern BOSSKILL_MESSAGE_PATTERN = Pattern.compile("Your (.+) kill count is: (\\d+)."); - private static final Pattern VALUABLE_DROP_PATTERN = Pattern.compile(".*Valuable drop: ([^<>]+)(?:)?"); + private static final Pattern VALUABLE_DROP_PATTERN = Pattern.compile(".*Valuable drop: ([^<>]+?\\(((?:\\d+,?)+) coins\\))(?:)?"); private static final Pattern UNTRADEABLE_DROP_PATTERN = Pattern.compile(".*Untradeable drop: ([^<>]+)(?:)?"); private static final Pattern DUEL_END_PATTERN = Pattern.compile("You have now (won|lost) ([0-9]+) duels?\\."); private static final Pattern QUEST_PATTERN_1 = Pattern.compile(".+?ve\\.*? (?been|rebuilt|.+?ed)? ?(?:the )?'?(?.+?)'?(?: [Qq]uest)?[!.]?$"); @@ -417,9 +417,13 @@ public class ScreenshotPlugin extends Plugin Matcher m = VALUABLE_DROP_PATTERN.matcher(chatMessage); if (m.matches()) { - String valuableDropName = m.group(1); - String fileName = "Valuable drop " + valuableDropName; - takeScreenshot(fileName, "Valuable Drops"); + int valuableDropValue = Integer.parseInt(m.group(2).replaceAll(",", "")); + if (valuableDropValue >= config.valuableDropThreshold()) + { + String valuableDropName = m.group(1); + String fileName = "Valuable drop " + valuableDropName; + takeScreenshot(fileName, "Valuable Drops"); + } } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java index 5386db8202..edd5101817 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java @@ -57,6 +57,7 @@ import org.mockito.Mock; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -67,7 +68,8 @@ public class ScreenshotPluginTest private static final String BARROWS_CHEST = "Your Barrows chest count is 310"; private static final String CHAMBERS_OF_XERIC_CHEST = "Your completed Chambers of Xeric count is: 489."; private static final String THEATRE_OF_BLOOD_CHEST = "Your completed Theatre of Blood count is: 73."; - private static final String VALUABLE_DROP = "Valuable drop: 6 x Bronze arrow (42 coins)"; + private static final String NOT_SO_VALUABLE_DROP = "Valuable drop: 6 x Bronze arrow (42 coins)"; + private static final String VALUABLE_DROP = "Valuable drop: Rune scimitar (25,600 coins)"; private static final String UNTRADEABLE_DROP = "Untradeable drop: Rusty sword"; private static final String BA_HIGH_GAMBLE_REWARD = "Raw shark (x 300)!
High level gamble count: 100"; private static final String HUNTER_LEVEL_2_TEXT = "Congratulations, you've just advanced a Hunter level.

Your Hunter level is now 2."; @@ -121,6 +123,7 @@ public class ScreenshotPluginTest Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); when(screenshotConfig.screenshotLevels()).thenReturn(true); when(screenshotConfig.screenshotValuableDrop()).thenReturn(true); + when(screenshotConfig.valuableDropThreshold()).thenReturn(1000); when(screenshotConfig.screenshotUntradeableDrop()).thenReturn(true); } @@ -161,10 +164,30 @@ public class ScreenshotPluginTest assertEquals(73, screenshotPlugin.gettheatreOfBloodNumber()); } + @Test + public void testNotSoValuableDrop() + { + ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", NOT_SO_VALUABLE_DROP, null, 0); + screenshotPlugin.onChatMessage(chatMessageEvent); + + verifyNoInteractions(drawManager); + + when(screenshotConfig.valuableDropThreshold()).thenReturn(0); + screenshotPlugin.onChatMessage(chatMessageEvent); + + verify(drawManager).requestNextFrameListener(any(Consumer.class)); + } + @Test public void testValuableDrop() { ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "", VALUABLE_DROP, null, 0); + when(screenshotConfig.valuableDropThreshold()).thenReturn(100_000); + screenshotPlugin.onChatMessage(chatMessageEvent); + + verifyNoInteractions(drawManager); + + when(screenshotConfig.valuableDropThreshold()).thenReturn(1000); screenshotPlugin.onChatMessage(chatMessageEvent); verify(drawManager).requestNextFrameListener(any(Consumer.class)); From 5a529568d66bb056801df1368ec6d46b17751887 Mon Sep 17 00:00:00 2001 From: Alexsuperfly Date: Sun, 24 Jan 2021 17:26:50 -0500 Subject: [PATCH 060/133] timers: Remove cannon timer when cannon is destroyed --- .../java/net/runelite/client/plugins/timers/TimersPlugin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java index fb8572a853..341c1c35a0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java @@ -89,6 +89,7 @@ public class TimersPlugin extends Plugin private static final String CANNON_FURNACE_MESSAGE = "You add the furnace."; private static final String CANNON_PICKUP_MESSAGE = "You pick up the cannon. It's really heavy."; private static final String CANNON_REPAIR_MESSAGE = "You repair your cannon, restoring it to working order."; + private static final String CANNON_DESTROYED_MESSAGE = "Your cannon has been destroyed!"; private static final String CHARGE_EXPIRED_MESSAGE = "Your magical charge fades away."; private static final String CHARGE_MESSAGE = "You feel charged with magic power."; private static final String EXTENDED_ANTIFIRE_DRINK_MESSAGE = "You drink some of your extended antifire potion."; @@ -517,7 +518,7 @@ public class TimersPlugin extends Plugin cannonTimer.setTooltip(cannonTimer.getTooltip() + " - World " + client.getWorld()); } - if (config.showCannon() && message.equals(CANNON_PICKUP_MESSAGE)) + if (config.showCannon() && (message.equals(CANNON_PICKUP_MESSAGE) || message.equals(CANNON_DESTROYED_MESSAGE))) { removeGameTimer(CANNON); } From ecb47ff50b5bdfe148e1051e7fccbf578c6e4021 Mon Sep 17 00:00:00 2001 From: Alexsuperfly Date: Sun, 24 Jan 2021 17:28:01 -0500 Subject: [PATCH 061/133] cannon: Remove cannon when it is destroyed --- .../java/net/runelite/client/plugins/cannon/CannonPlugin.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java index 4d426ae9b6..1f9aeeaa25 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java @@ -304,7 +304,8 @@ public class CannonPlugin extends Plugin } if (event.getMessage().contains("You pick up the cannon") - || event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!")) + || event.getMessage().contains("Your cannon has decayed. Speak to Nulodion to get a new one!") + || event.getMessage().contains("Your cannon has been destroyed!")) { cannonPlaced = false; cballsLeft = 0; From ccf1e8eb8b02a8206780d0eeac0dde3f0af45964 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 09:54:39 +0000 Subject: [PATCH 062/133] 3/n --- .../openosrs/client/game/SoundManager.java | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java b/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java index 058c575793..e6e7489570 100644 --- a/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java +++ b/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java @@ -19,15 +19,6 @@ import org.slf4j.LoggerFactory; @Singleton public class SoundManager { - private static final Logger log = LoggerFactory.getLogger(SoundManager.class); - private final RuneLiteConfig runeliteConfig; - - @Inject - private SoundManager(RuneLiteConfig runeLiteConfig) - { - this.runeliteConfig = runeLiteConfig; - } - public void play(final Sound sound) { new Thread(new Runnable() @@ -49,15 +40,8 @@ public class SoundManager int volume = 50; FloatControl gainControl = (FloatControl) line.getControl(FloatControl.Type.MASTER_GAIN); BooleanControl muteControl = (BooleanControl) line.getControl(BooleanControl.Type.MUTE); - if (volume == 0) - { - muteControl.setValue(true); - } - else - { - muteControl.setValue(false); - gainControl.setValue((float) (Math.log((double) volume / 100.0) / Math.log(10.0) * 20.0)); - } + muteControl.setValue(false); + gainControl.setValue((float) (Math.log((double) volume / 100.0) / Math.log(10.0) * 20.0)); } line.start(); SoundManager.this.stream(AudioSystem.getAudioInputStream(outFormat, in), line); From a208de4be0a1ed8f28a29e0928a20c19ec0d7a40 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 10:06:27 +0000 Subject: [PATCH 063/133] Revert "api: add client/ui/overlay/components/table" This reverts commit a579bdb121286638c2a3d71294d25d243781af39. --- .../components/table/TableAlignment.java | 32 -- .../components/table/TableComponent.java | 465 ------------------ .../components/table/TableElement.java | 38 -- .../ui/overlay/components/table/TableRow.java | 41 -- 4 files changed, 576 deletions(-) delete mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableAlignment.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableComponent.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableElement.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableRow.java diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableAlignment.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableAlignment.java deleted file mode 100644 index 6061e639f8..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableAlignment.java +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright (c) 2019, TheStonedTurtle - * 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 com.openosrs.client.ui.overlay.components.table; - -public enum TableAlignment -{ - LEFT, - CENTER, - RIGHT -} diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableComponent.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableComponent.java deleted file mode 100644 index 6f96434e6d..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableComponent.java +++ /dev/null @@ -1,465 +0,0 @@ -/* - * Copyright (c) 2018, Jordan Atwood - * Copyright (c) 2019, TheStonedTurtle - * 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 com.openosrs.client.ui.overlay.components.table; - -import java.awt.Color; -import java.awt.Dimension; -import java.awt.FontMetrics; -import java.awt.Graphics2D; -import java.awt.Point; -import java.awt.Rectangle; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; -import lombok.Getter; -import lombok.NonNull; -import lombok.Setter; -import net.runelite.api.util.Text; -import net.runelite.client.ui.overlay.components.ComponentConstants; -import net.runelite.client.ui.overlay.components.LayoutableRenderableEntity; -import net.runelite.client.ui.overlay.components.TextComponent; - -@Setter -public class TableComponent implements LayoutableRenderableEntity -{ - private static final TableElement EMPTY_ELEMENT = TableElement.builder().build(); - - @Getter - private final List columns = new ArrayList<>(); - @Getter - private final List rows = new ArrayList<>(); - - @Getter - private final Rectangle bounds = new Rectangle(); - - private TableAlignment defaultAlignment = TableAlignment.LEFT; - private Color defaultColor = Color.WHITE; - private Dimension gutter = new Dimension(3, 0); - private Point preferredLocation = new Point(); - private Dimension preferredSize = new Dimension(ComponentConstants.STANDARD_WIDTH, 0); - - @Override - public Dimension render(final Graphics2D graphics) - { - final FontMetrics metrics = graphics.getFontMetrics(); - final TableRow colRow = TableRow.builder().elements(this.columns).build(); - final int[] columnWidths = getColumnWidths(metrics, colRow); - - graphics.translate(preferredLocation.x, preferredLocation.y); - - // Display the columns first - int height = displayRow(graphics, colRow, 0, columnWidths, metrics); - - for (TableRow row : this.rows) - { - height = displayRow(graphics, row, height, columnWidths, metrics); - } - - graphics.translate(-preferredLocation.x, -preferredLocation.y); - - final Dimension dimension = new Dimension(preferredSize.width, height); - bounds.setLocation(preferredLocation); - bounds.setSize(dimension); - - return dimension; - } - - private int displayRow(Graphics2D graphics, TableRow row, int height, int[] columnWidths, FontMetrics metrics) - { - int x = 0; - int startingRowHeight = height; - - final List elements = row.getElements(); - for (int i = 0; i < elements.size(); i++) - { - int y = startingRowHeight; - final TableElement cell = elements.get(i); - - final String content = cell.getContent(); - if (content == null) - { - continue; - } - - final String[] lines = lineBreakText(content, columnWidths[i], metrics); - final TableAlignment alignment = getCellAlignment(row, i); - final Color color = getCellColor(row, i); - - for (String line : lines) - { - final int alignmentOffset = getAlignedPosition(line, alignment, columnWidths[i], metrics); - final TextComponent leftLineComponent = new TextComponent(); - y += metrics.getHeight(); - - leftLineComponent.setPosition(new Point(x + alignmentOffset, y)); - leftLineComponent.setText(line); - leftLineComponent.setColor(color); - leftLineComponent.render(graphics); - } - height = Math.max(height, y); - x += columnWidths[i] + gutter.width; - } - - return height + gutter.height; - } - - /** - * Returns the width that each column should take up - * Based on https://stackoverflow.com/questions/22206825/algorithm-for-calculating-variable-column-widths-for-set-table-width - * - * @param metrics - * @return int[] of column width - */ - private int[] getColumnWidths(final FontMetrics metrics, final TableRow columnRow) - { - int numCols = columns.size(); - for (final TableRow r : rows) - { - numCols = Math.max(r.getElements().size(), numCols); - } - - int[] maxtextw = new int[numCols]; // max text width over all rows - int[] maxwordw = new int[numCols]; // max width of longest word - boolean[] flex = new boolean[numCols]; // is column flexible? - boolean[] wrap = new boolean[numCols]; // can column be wrapped? - int[] finalcolw = new int[numCols]; // final width of columns - - final List rows = new ArrayList<>(this.rows); - rows.add(columnRow); - - for (final TableRow r : rows) - { - final List elements = r.getElements(); - for (int col = 0; col < elements.size(); col++) - { - final TableElement ele = elements.get(col); - final String cell = ele.getContent(); - if (cell == null) - { - continue; - } - - final int cellWidth = getTextWidth(metrics, cell); - - maxtextw[col] = Math.max(maxtextw[col], cellWidth); - for (String word : cell.split(" ")) - { - maxwordw[col] = Math.max(maxwordw[col], getTextWidth(metrics, word)); - } - - if (maxtextw[col] == cellWidth) - { - wrap[col] = cell.contains(" "); - } - } - } - - int left = preferredSize.width - (numCols - 1) * gutter.width; - final double avg = left / numCols; - int nflex = 0; - - // Determine whether columns should be flexible and assign width of non-flexible cells - for (int col = 0; col < numCols; col++) - { - // This limit can be adjusted as needed - final double maxNonFlexLimit = 1.5 * avg; - - flex[col] = maxtextw[col] > maxNonFlexLimit; - if (flex[col]) - { - nflex++; - } - else - { - finalcolw[col] = maxtextw[col]; - left -= finalcolw[col]; - } - } - - // If there is not enough space, make columns that could be word-wrapped flexible too - if (left < nflex * avg) - { - for (int col = 0; col < numCols; col++) - { - if (!flex[col] && wrap[col]) - { - left += finalcolw[col]; - finalcolw[col] = 0; - flex[col] = true; - nflex++; - } - } - } - - // Calculate weights for flexible columns. The max width is capped at the table width to - // treat columns that have to be wrapped more or less equal - int tot = 0; - for (int col = 0; col < numCols; col++) - { - if (flex[col]) - { - maxtextw[col] = Math.min(maxtextw[col], preferredSize.width); - tot += maxtextw[col]; - } - } - - // Now assign the actual width for flexible columns. Make sure that it is at least as long - // as the longest word length - for (int col = 0; col < numCols; col++) - { - if (flex[col]) - { - finalcolw[col] = left * maxtextw[col] / tot; - finalcolw[col] = Math.max(finalcolw[col], maxwordw[col]); - left -= finalcolw[col]; - } - } - - // When the sum of column widths is less than the total space available, distribute the - // extra space equally across all columns - final int extraPerCol = left / numCols; - for (int col = 0; col < numCols; col++) - { - finalcolw[col] += extraPerCol; - left -= extraPerCol; - } - // Add any remainder to the right-most column - finalcolw[finalcolw.length - 1] += left; - - return finalcolw; - } - - private static int getTextWidth(final FontMetrics metrics, final String cell) - { - return metrics.stringWidth(Text.removeTags(cell)); - } - - private static String[] lineBreakText(final String text, final int maxWidth, final FontMetrics metrics) - { - final String[] words = text.split(" "); - - if (words.length == 0) - { - return new String[0]; - } - - final StringBuilder wrapped = new StringBuilder(words[0]); - int spaceLeft = maxWidth - getTextWidth(metrics, wrapped.toString()); - - for (int i = 1; i < words.length; i++) - { - final String word = words[i]; - final int wordLen = getTextWidth(metrics, word); - final int spaceWidth = metrics.stringWidth(" "); - - if (wordLen + spaceWidth > spaceLeft) - { - wrapped.append("\n").append(word); - spaceLeft = maxWidth - wordLen; - } - else - { - wrapped.append(" ").append(word); - spaceLeft -= spaceWidth + wordLen; - } - } - - return wrapped.toString().split("\n"); - } - - public boolean isEmpty() - { - return columns.size() == 0 || rows.size() == 0; - } - - private void ensureColumnSize(final int size) - { - while (size > columns.size()) - { - columns.add(TableElement.builder().build()); - } - } - - private static int getAlignedPosition(final String str, final TableAlignment alignment, final int columnWidth, final FontMetrics metrics) - { - final int stringWidth = getTextWidth(metrics, str); - int offset = 0; - - switch (alignment) - { - case LEFT: - break; - case CENTER: - offset = (columnWidth / 2) - (stringWidth / 2); - break; - case RIGHT: - offset = columnWidth - stringWidth; - break; - } - return offset; - } - - /** - * Returns the color for the specified table element. - * Priority order: cell->row->column->default - * - * @param row TableRow element - * @param colIndex column index - */ - private Color getCellColor(final TableRow row, final int colIndex) - { - final List rowElements = row.getElements(); - final TableElement cell = colIndex < rowElements.size() ? rowElements.get(colIndex) : EMPTY_ELEMENT; - final TableElement column = colIndex < columns.size() ? columns.get(colIndex) : EMPTY_ELEMENT; - - return firstNonNull( - cell.getColor(), - row.getRowColor(), - column.getColor(), - defaultColor); - } - - private void setColumnAlignment(final int col, final TableAlignment alignment) - { - assert columns.size() > col; - columns.get(col).setAlignment(alignment); - } - - public void setColumnAlignments(@Nonnull final TableAlignment... alignments) - { - ensureColumnSize(alignments.length); - for (int i = 0; i < alignments.length; i++) - { - setColumnAlignment(i, alignments[i]); - } - } - - /** - * Returns the alignment for the specified table element. - * Priority order: cell->row->column->default - * - * @param row TableRow element - * @param colIndex column index - */ - private TableAlignment getCellAlignment(final TableRow row, final int colIndex) - { - final List rowElements = row.getElements(); - final TableElement cell = colIndex < rowElements.size() ? rowElements.get(colIndex) : EMPTY_ELEMENT; - final TableElement column = colIndex < columns.size() ? columns.get(colIndex) : EMPTY_ELEMENT; - - return firstNonNull( - cell.getAlignment(), - row.getRowAlignment(), - column.getAlignment(), - defaultAlignment); - } - - @SafeVarargs - private static T firstNonNull(@Nullable T... elements) - { - if (elements == null || elements.length == 0) - { - return null; - } - - int i = 0; - T cur = elements[0]; - while (cur == null && i < elements.length) - { - cur = elements[i]; - i++; - } - - return cur; - } - - // Helper functions for cleaner overlay code - public void addRow(@Nonnull final String... cells) - { - final List elements = new ArrayList<>(); - for (final String cell : cells) - { - elements.add(TableElement.builder().content(cell).build()); - } - - final TableRow row = TableRow.builder().build(); - row.setElements(elements); - - this.rows.add(row); - } - - private void addRows(@Nonnull final String[]... rows) - { - for (String[] row : rows) - { - addRow(row); - } - } - - public void addRows(@NonNull final TableRow... rows) - { - this.rows.addAll(Arrays.asList(rows)); - } - - public void setRows(@Nonnull final String[]... elements) - { - this.rows.clear(); - addRows(elements); - } - - public void setRows(@Nonnull final TableRow... elements) - { - this.rows.clear(); - this.rows.addAll(Arrays.asList(elements)); - } - - private void addColumn(@Nonnull final String col) - { - this.columns.add(TableElement.builder().content(col).build()); - } - - public void addColumns(@NonNull final TableElement... columns) - { - this.columns.addAll(Arrays.asList(columns)); - } - - public void setColumns(@Nonnull final TableElement... elements) - { - this.columns.clear(); - this.columns.addAll(Arrays.asList(elements)); - } - - public void setColumns(@Nonnull final String... columns) - { - this.columns.clear(); - for (String col : columns) - { - addColumn(col); - } - } -} diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableElement.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableElement.java deleted file mode 100644 index daf7eb5790..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableElement.java +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright (c) 2019, TheStonedTurtle - * 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 com.openosrs.client.ui.overlay.components.table; - -import java.awt.Color; -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class TableElement -{ - TableAlignment alignment; - Color color; - String content; -} diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableRow.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableRow.java deleted file mode 100644 index 9451bba547..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/components/table/TableRow.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2019, TheStonedTurtle - * 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 com.openosrs.client.ui.overlay.components.table; - -import java.awt.Color; -import java.util.Collections; -import java.util.List; -import lombok.Builder; -import lombok.Data; - -@Data -@Builder -public class TableRow -{ - Color rowColor; - TableAlignment rowAlignment; - @Builder.Default - List elements = Collections.emptyList(); -} From a3461a2625759ebe44293113ebaa07de8f9c7176 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 10:20:47 +0000 Subject: [PATCH 064/133] Revert "api: squash oprs varbits/animID, add ClipBoard" This reverts commit 233a44061015641f6d0e7cf7ba7802eaa26264f7. # Conflicts: # runelite-api/src/main/java/net/runelite/api/AnimationID.java --- .../java/com/openosrs/api/AnimationID.java | 239 ++++++++++++++ .../main/java/com/openosrs/api/Varbits.java | 312 ++++++++++++++++++ .../java/net/runelite/api/AnimationID.java | 257 +-------------- .../main/java/net/runelite/api/Varbits.java | 225 ++++++------- .../com/openosrs/client/util/Clipboard.java | 62 ---- 5 files changed, 662 insertions(+), 433 deletions(-) create mode 100644 runelite-api/src/main/java/com/openosrs/api/AnimationID.java create mode 100644 runelite-api/src/main/java/com/openosrs/api/Varbits.java delete mode 100644 runelite-client/src/main/java/com/openosrs/client/util/Clipboard.java diff --git a/runelite-api/src/main/java/com/openosrs/api/AnimationID.java b/runelite-api/src/main/java/com/openosrs/api/AnimationID.java new file mode 100644 index 0000000000..8a3de35994 --- /dev/null +++ b/runelite-api/src/main/java/com/openosrs/api/AnimationID.java @@ -0,0 +1,239 @@ +package com.openosrs.api; + +//This is where Animation IDs should go that aren't found in RuneLite +public class AnimationID extends net.runelite.api.AnimationID +{ + + // NPC animations + public static final int TZTOK_JAD_MAGIC_ATTACK = 2656; + public static final int TZTOK_JAD_RANGE_ATTACK = 2652; + public static final int HELLHOUND_DEFENCE = 6566; + + public static final int FISHING_TRAILBLAZER_HARPOON_2 = 8785; + + public static final int CRYSTALLINE_RAT_DEATH = 8334; + public static final int CRYSTALLINE_BAT_DEATH = 4917; + public static final int CRYSTALLINE_WOLF_DEATH = 8335; + public static final int CRYSTALLINE_SPIDER_DEATH = 8338; + public static final int CRYSTALLINE_UNICORN_DEATH = 6377; + public static final int CRYSTALLINE_DRAGON_DEATH = 92; + public static final int CRYSTALLINE_BEAR_DEATH = 4929; + public static final int CRYSTALLINE_DARK_BEAST_DEATH = 2733; + public static final int CORRUPTED_SCORPION_DEATH = 6256; + + public static final int TABLET_TELEPORT = 4069; + + public static final int THIEVING_STALL = 832; + public static final int PICKPOCKET_SUCCESS = 881; + public static final int PULL_LEVER = 2140; + public static final int STANDARD_PURPLE_TELEPORT = 714; + public static final int ECTOPHIAL_TELEPORT = 878; + public static final int FAIRY_RING_TELEPORT = 3265; + public static final int SCROLL_TELEPORT = 3864; + public static final int XERICS_TALISMAN_TELEPORT = 3865; + public static final int WILDERNESS_OBELISK_TELEPORT = 3945; + public static final int SEED_POD_TELEPORT = 4544; + + //block animations for players and perhaps npcs as well? + public static final int BLOCK_DEFENDER = 4177; + public static final int BLOCK_NO_SHIELD = 420; + public static final int BLOCK_SHIELD = 1156; + public static final int BLOCK_SWORD = 388; + public static final int BLOCK_UNARMED = 424; // Same Animation as failed pickpocked + + public static final int NIGHTMARE_DEATH = 8612; + public static final int LOW_LEVEL_MAGIC_ATTACK = 1162; + public static final int HIGH_LEVEL_MAGIC_ATTACK = 1167; + public static final int BLOWPIPE_ATTACK = 5061; + + // NPC animations + public static final int BLACKJACK_KO = 838; + + // Fight Caves + public static final int TZTOK_JAD_MELEE_ATTACK = 2655; + public static final int TOK_XIL_RANGE_ATTACK = 2633; + public static final int TOK_XIL_MELEE_ATTACK = 2628; + public static final int KET_ZEK_MELEE_ATTACK = 2644; + public static final int KET_ZEK_MAGE_ATTACK = 2647; + public static final int MEJ_KOT_MELEE_ATTACK = 2637; + public static final int MEJ_KOT_HEAL_ATTACK = 2639; + + // Vorkath + public static final int VORKATH_WAKE_UP = 7950; + public static final int VORKATH_DEATH = 7949; + public static final int VORKATH_SLASH_ATTACK = 7951; + public static final int VORKATH_ATTACK = 7952; + public static final int VORKATH_FIRE_BOMB_OR_SPAWN_ATTACK = 7960; + public static final int VORKATH_ACID_ATTACK = 7957; + + // Tekton + public static final int TEKTON_ANVIL = 7475; + public static final int TEKTON_AUTO1 = 7482; + public static final int TEKTON_AUTO2 = 7483; + public static final int TEKTON_AUTO3 = 7484; + public static final int TEKTON_FAST_AUTO1 = 7478; + public static final int TEKTON_FAST_AUTO2 = 7488; + public static final int TEKTON_ENRAGE_AUTO1 = 7492; + public static final int TEKTON_ENRAGE_AUTO2 = 7493; + public static final int TEKTON_ENRAGE_AUTO3 = 7494; + + // Hydra + public static final int HYDRA_WALKING = 8232; + public static final int HYDRA_IDLE = 8233; + public static final int HYDRA_POISON_1 = 8234; + public static final int HYDRA_RANGED_1 = 8235; + public static final int HYDRA_MAGIC_1 = 8236; + public static final int HYDRA_1_1 = 8237; + public static final int HYDRA_1_2 = 8238; + public static final int HYDRA_LIGHTNING = 8241; + public static final int HYDRA_RANGED_2 = 8242; + public static final int HYDRA_MAGIC_2 = 8243; + public static final int HYDRA_2_1 = 8244; + public static final int HYDRA_2_2 = 8245; + public static final int HYDRA_FIRE = 8248; + public static final int HYDRA_RANGED_3 = 8249; + public static final int HYDRA_MAGIC_3 = 8250; + public static final int HYDRA_3_1 = 8251; + public static final int HYDRA_3_2 = 8252; + public static final int HYDRA_MAGIC_4 = 8254; + public static final int HYDRA_POISON_4 = 8254; + public static final int HYDRA_RANGED_4 = 8255; + public static final int HYDRA_RANGED_OR_POISON_ATTACK = 8256; + public static final int HYDRA_4_1 = 8257; + public static final int HYDRA_4_2 = 8258; + + // Inferno animations + public static final int JAL_NIB = 7574; + public static final int JAL_MEJRAH = 7578; + public static final int JAL_MEJRAH_STAND = 7577; + public static final int JAL_AK_RANGE_ATTACK = 7581; + public static final int JAL_AK_MELEE_ATTACK = 7582; + public static final int JAL_AK_MAGIC_ATTACK = 7583; + public static final int JAL_IMKOT = 7597; + public static final int JAL_XIL_MELEE_ATTACK = 7604; + public static final int JAL_XIL_RANGE_ATTACK = 7605; + public static final int JAL_ZEK_MAGE_ATTACK = 7610; + public static final int JAL_ZEK_MELEE_ATTACK = 7612; + public static final int JALTOK_JAD_MELEE_ATTACK = 7590; + public static final int JALTOK_JAD_MAGE_ATTACK = 7592; + public static final int JALTOK_JAD_RANGE_ATTACK = 7593; + public static final int TZKAL_ZUK = 7566; + public static final int JAL_MEJJAK = 2858; + + // General Graardor + public static final int MINION_AUTO1 = 6154; + public static final int MINION_AUTO2 = 6156; + public static final int MINION_AUTO3 = 7071; + public static final int MINION_AUTO4 = 7073; + public static final int GENERAL_AUTO1 = 7018; + public static final int GENERAL_AUTO2 = 7020; + public static final int GENERAL_AUTO3 = 7021; + + // Kr'il Tsutsaroth + public static final int ZAMMY_GENERIC_AUTO = 64; + public static final int KRIL_AUTO = 6948; + public static final int KRIL_SPEC = 6950; + public static final int ZAKL_AUTO = 7077; + public static final int BALFRUG_AUTO = 4630; + + // Commander Zilyana + public static final int ZILYANA_MELEE_AUTO = 6964; + public static final int ZILYANA_AUTO = 6967; + public static final int ZILYANA_SPEC = 6970; + public static final int STARLIGHT_AUTO = 6376; + public static final int BREE_AUTO = 7026; + public static final int GROWLER_AUTO = 7037; + + // Kree'arra + public static final int KREE_RANGED = 6978; + public static final int SKREE_AUTO = 6955; + public static final int GEERIN_AUTO = 6956; + public static final int GEERIN_FLINCH = 6958; + public static final int KILISA_AUTO = 6957; + + // Vetion + public static final int VETION_EARTHQUAKE = 5507; + + // Zulrah + public static final int ZULRAH_DEATH = 5804; + public static final int ZULRAH_PHASE = 5072; + + //Dagannoth Kings + public static final int DAG_REX = 2853; + public static final int DAG_PRIME = 2854; + public static final int DAG_SUPREME = 2855; + + // Lizardman shaman + public static final int LIZARDMAN_SHAMAN_SPAWN = 7157; + public static final int LIZARDMAN_SHAMAN_SPAWN_EXPLOSION = 7159; + + // Cerberus + public static final int CERBERUS_MAGIC_ATTACK = 4489; + public static final int CERBERUS_RANGED_ATTACK = 4490; + public static final int CERBERUS_MELEE_ATTACK = 4491; + public static final int CERBERUS_LAVA_ATTACK = 4493; + public static final int CERBERUS_SUMMON_GHOSTS = 4494; + + // Gauntlet Hunleff + public static final int HUNLEFF_TRAMPLE = 8420; + public static final int HUNLEFF_ATTACK = 8419; + public static final int HUNLEFF_TORNADO = 8418; + public static final int HUNLLEF_SWITCH_TO_MAGIC = 8754; + public static final int HUNLLEF_SWITCH_TO_RANGED = 8755; + + //Zalcano + public static final int ZALCANO_KNOCKED_DOWN = 8437; + public static final int ZALCANO_WAKEUP = 8439; + public static final int ZALCANO_ROCK_GLOWING = 8448; + + // Theatre of Blood - Sugadinti Maiden + public static final int SUGADINTI_MAIDEN_BLOOD_SPLAT_ATTACK = 8091; + public static final int SUGADINTI_MAIDEN_MAGIC_ATTACK = 8092; + public static final int SUGADINTI_MAIDEN_DEATH = 8094; + + // Theatre of Blood - Pestilent Bloat + public static final int BLOAT_SLEEP = 8082; + + // Theatre of Blood - Sotetseg + public static final int SOTETSEG_MELEE_ATTACK = 8138; + public static final int SOTETSEG_REGULAR_PROJECTILE_ATTACK = 8139; + + // Theatre of Blood - Verzik Vitur + public static final int VERZIK_PHASE_1_MAGIC_ATTACK = 8109; + public static final int VERZIK_PHASE_1_MAGIC_ATTACK_CHANNEL = 8110; + public static final int VERZIK_CHANGE_TO_PHASE_2 = 8111; + public static final int VERZIK_PHASE_2_MAGIC_ATTACK = 8114; + public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_1 = 8116; + public static final int VERZIK_PHASE_2_HEALING_CHANNEL = 8117; + public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_2 = 8118; + public static final int VERZIK_CHANGE_TO_PHASE_3 = 8119; + public static final int VERZIK_PHASE_3_MELEE_ATTACK = 8123; + public static final int VERZIK_PHASE_3_MAGIC_ATTACK = 8124; + public static final int VERZIK_PHASE_3_RANGED_ATTACK = 8125; + public static final int VERZIK_PHASE_3_GREEN_POOL_ATTACK = 8126; + public static final int VERZIK_PHASE_3_WEB_ATTACK = 8127; + public static final int VERZIK_DEATH_1 = 8128; + public static final int VERZIK_DEATH_2 = 8129; + + // The Nightmare of Ashihama + public static final int NIGHTMARE_SPAWN_SLEEPWALKERS = 8572; + public static final int NIGHTMARE_FLOATY = 8592; + public static final int NIGHTMARE_WALKING = 8592; + public static final int NIGHTMARE_IDLE = 8593; + public static final int NIGHTMARE_MELEE_ATTACK = 8594; + public static final int NIGHTMARE_MAGIC_ATTACK = 8595; + public static final int NIGHTMARE_RANGED_ATTACK = 8596; + public static final int NIGHTMARE_SURGE_ATTACK = 8597; + public static final int NIGHTMARE_GHOST_AOE_ATTACK = 8598; + public static final int NIGHTMARE_CURSE_PRAYERS_ATTACK = 8599; + public static final int NIGHTMARE_SPAWN_INFECTIOUS_SPORES = 8600; + public static final int NIGHTMARE_SPAWN_ROOM_SECTION_FLOWERS = 8601; + public static final int NIGHTMARE_CHANNEL_DEVASTATING_ATTACK = 8604; + public static final int NIGHTMARE_SWITCH_TO_DEVIL_PHASE = 8605; + public static final int NIGHTMARE_PARASITE_ATTACK = 8606; + public static final int NIGHTMARE_JUMP_DOWN = 8607; + public static final int NIGHTMARE_SINK_DOWN = 8608; + public static final int NIGHTMARE_JUMP_UP = 8609; + public static final int NIGHTMARE_JUMP_UP_2 = 8610; + public static final int NIGHTMARE_WAKE_UP = 8611; +} diff --git a/runelite-api/src/main/java/com/openosrs/api/Varbits.java b/runelite-api/src/main/java/com/openosrs/api/Varbits.java new file mode 100644 index 0000000000..bc98952741 --- /dev/null +++ b/runelite-api/src/main/java/com/openosrs/api/Varbits.java @@ -0,0 +1,312 @@ +package com.openosrs.api; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter +public enum Varbits +{ + /* + * Kharedst's Memoirs Teleport Item + */ + KHAREDSTS_MEMOIRS_CHARGES(6035), + + /** + * Grand Exchange + */ + GRAND_EXCHANGE_PRICE_PER_ITEM(4398), + + + /** + * Locked Prayers + * 0-7 = Locked + * 8 = Unlocked + */ + CHIVPIETY_UNLOCKED(3909), + + /** + * Locked Prayers + * 0 = Locked + * 1 = Unlocked + */ + + RIGOUR_UNLOCKED(5451), + AUGURY_UNLOCKED(5452), + PRESERVE_UNLOCKED(5453), + + /** + * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating + */ + THEATRE_OF_BLOOD(6440), + BLOAT_DOOR(6447), + + /** + * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb + */ + THEATRE_OF_BLOOD_ORB_1(6442), + THEATRE_OF_BLOOD_ORB_2(6443), + THEATRE_OF_BLOOD_ORB_3(6444), + THEATRE_OF_BLOOD_ORB_4(6445), + THEATRE_OF_BLOOD_ORB_5(6446), + + /** + * Nightmare Zone + */ + NMZ_ABSORPTION(3956), + NMZ_POINTS(3949), + NMZ_OVERLOAD(3955), + + /** + * Pyramid plunder + */ + PYRAMID_PLUNDER_SARCO_OPEN(2362), + PYRAMID_PLUNDER_CHEST_OPEN(2363), + PYRAMID_PLUNDER_ROOM_LOCATION(2365), + PYRAMID_PLUNDER_TIMER(2375), + PYRAMID_PLUNDER_THIEVING_LEVEL(2376), + PYRAMID_PLUNDER_ROOM(2377), + + /** + * In the Wilderness + */ + IN_THE_WILDERNESS(5963), + + /** + * Kingdom Management + */ + KINGDOM_FAVOR(72), + KINGDOM_COFFER(74), + KINGDOM_WORKERS_WOOD(81), + KINGDOM_WORKERS_HERBS(82), + KINGDOM_WORKERS_FISHING(83), + KINGDOM_WORKERS_MINING(84), + KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked + KINGDOM_WORKERS_HARDWOOD(2131), + KINGDOM_WORKERS_FARM(2132), + KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both + KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax + + /** + * Varbit used for Slayer reward points + */ + SLAYER_REWARD_POINTS(4068), + + /** + * 0 = standard + * 1 = ancients + * 2 = lunars + * 3 = arrceus + **/ + SPELLBOOK(4070), + + /** + * Bank settings/flags + **/ + BANK_NOTE_FLAG(3958), + + + /** + * Spells being auto-casted + */ + AUTO_CAST_SPELL(276), + + /** + * Temple Trekking + */ + TREK_POINTS(1955), + TREK_STARTED(1956), + TREK_EVENT(1958), + TREK_STATUS(6719), + BLOAT_ENTERED_ROOM(6447), + + /** + * f2p Quest varbits, these don't hold the completion value. + */ + QUEST_DEMON_SLAYER(2561), + QUEST_GOBLIN_DIPLOMACY(2378), + QUEST_MISTHALIN_MYSTERY(3468), + QUEST_THE_CORSAIR_CURSE(6071), + QUEST_X_MARKS_THE_SPOT(8063), + QUEST_ERNEST_LEVER_A(1788), + QUEST_ERNEST_LEVER_B(1789), + QUEST_ERNEST_LEVER_C(1790), + QUEST_ERNEST_LEVER_D(1791), + QUEST_ERNEST_LEVER_E(1792), + QUEST_ERNEST_LEVER_F(1793), + + /** + * member Quest varbits, these don't hold the completion value. + */ + QUEST_ANIMAL_MAGNETISM(3185), + QUEST_BETWEEN_A_ROCK(299), + QUEST_CONTACT(3274), + QUEST_ZOGRE_FLESH_EATERS(487), + QUEST_DARKNESS_OF_HALLOWVALE(2573), + QUEST_DEATH_TO_THE_DORGESHUUN(2258), + QUEST_DESERT_TREASURE(358), + QUEST_DEVIOUS_MINDS(1465), + QUEST_EAGLES_PEAK(2780), + QUEST_ELEMENTAL_WORKSHOP_II(2639), + QUEST_ENAKHRAS_LAMENT(1560), + QUEST_ENLIGHTENED_JOURNEY(2866), + QUEST_THE_EYES_OF_GLOUPHRIE(2497), + QUEST_FAIRYTALE_I_GROWING_PAINS(1803), + QUEST_FAIRYTALE_II_CURE_A_QUEEN(2326), + QUEST_THE_FEUD(334), // 14 = able to pickpocket + QUEST_FORGETTABLE_TALE(822), + QUEST_GARDEN_OF_TRANQUILLITY(961), + QUEST_GHOSTS_AHOY(217), + QUEST_THE_GIANT_DWARF(571), + QUEST_THE_GOLEM(346), + QUEST_HORROR_FROM_THE_DEEP(34), + QUEST_ICTHLARINS_LITTLE_HELPER(418), + QUEST_IN_AID_OF_THE_MYREQUE(1990), + QUEST_THE_LOST_TRIBE(532), + QUEST_LUNAR_DIPLOMACY(2448), + QUEST_MAKING_HISTORY(1383), + QUEST_MOUNTAIN_DAUGHTER(260), + QUEST_MOURNINGS_END_PART_II(1103), + QUEST_MY_ARMS_BIG_ADVENTURE(2790), + QUEST_RATCATCHERS(1404), + QUEST_RECIPE_FOR_DISASTER(1850), + QUEST_RECRUITMENT_DRIVE(657), + QUEST_ROYAL_TROUBLE(2140), + QUEST_THE_SLUG_MENACE(2610), + QUEST_SHADOW_OF_THE_STORM(1372), + QUEST_A_SOULS_BANE(2011), + QUEST_SPIRITS_OF_THE_ELID(1444), + QUEST_SWAN_SONG(2098), + QUEST_A_TAIL_OF_TWO_CATS(1028), + QUEST_TEARS_OF_GUTHIX(451), + QUEST_WANTED(1051), + QUEST_COLD_WAR(3293), + QUEST_THE_FREMENNIK_ISLES(3311), + QUEST_TOWER_OF_LIFE(3337), + QUEST_WHAT_LIES_BELOW(3523), + QUEST_OLAFS_QUEST(3534), + QUEST_ANOTHER_SLICE_OF_HAM(3550), + QUEST_DREAM_MENTOR(3618), + QUEST_GRIM_TALES(2783), + QUEST_KINGS_RANSOM(3888), + QUEST_MONKEY_MADNESS_II(5027), + QUEST_CLIENT_OF_KOUREND(5619), + QUEST_BONE_VOYAGE(5795), + QUEST_THE_QUEEN_OF_THIEVES(6037), + QUEST_THE_DEPTHS_OF_DESPAIR(6027), + QUEST_DRAGON_SLAYER_II(6104), + QUEST_TALE_OF_THE_RIGHTEOUS(6358), + QUEST_A_TASTE_OF_HOPE(6396), + QUEST_MAKING_FRIENDS_WITH_MY_ARM(6528), + QUEST_THE_ASCENT_OF_ARCEUUS(7856), + QUEST_THE_FORSAKEN_TOWER(7796), + //TODO + QUEST_SONG_OF_THE_ELVES(7796), + + /** + * mini-quest varbits, these don't hold the completion value. + */ + QUEST_ARCHITECTURAL_ALLIANCE(4982), + QUEST_BEAR_YOUR_SOUL(5078), + QUEST_CURSE_OF_THE_EMPTY_LORD(821), + QUEST_ENCHANTED_KEY(1391), + QUEST_THE_GENERALS_SHADOW(3330), + QUEST_SKIPPY_AND_THE_MOGRES(1344), + QUEST_LAIR_OF_TARN_RAZORLOR(3290), + QUEST_FAMILY_PEST(5347), + QUEST_THE_MAGE_ARENA_II(6067), + //TODO + QUEST_IN_SEARCH_OF_KNOWLEDGE(6067), + + /** + * Spellbook filtering (1 = unfiltered, 0 = filtered) + */ + FILTER_SPELLBOOK(6718), + + /** + * POH Building mode (1 = yes, 0 = no) + */ + BUILDING_MODE(2176), + + /** + * 1 if in game, 0 if not + */ + LMS_IN_GAME(5314), + + /** + * Amount of pvp kills in current game + */ + LMS_KILLS(5315), + + /** + * The x coordinate of the final safespace (world coord) + */ + LMS_SAFE_X(5316), + + LMS_POISON_PROGRESS(5317), + + /** + * The y coordinate of the final safespace (world coord) + */ + LMS_SAFE_Y(5320), + + /** + * 1 is true, 0 is false. + */ + GAUNTLET_FINAL_ROOM_ENTERED(9177), + + /** + * 1 is true, 0 is false. + */ + GAUNTLET_ENTERED(9178), + + WITHDRAW_X_AMOUNT(3960), + + IN_PVP_AREA(8121), + + /** + * Value of hotkey varbits can be 0-13 + * 0 corresponds to no hotkey set + * 1-12 correspond to F1-F12 respectively + * 13 corresponds to escape + */ + COMBAT_TAB_HOTKEY(4675), + STATS_TAB_HOTKEY(4676), + QUESTS_TAB_HOTKEY(4677), + INVENTORY_TAB_HOTKEY(4678), + EQUIPMENT_TAB_HOTKEY(4679), + PRAYER_TAB_HOTKEY(4680), + SPELLBOOK_TAB_HOTKEY(4682), + FRIENDS_TAB_HOTKEY(4684), + ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), + LOGOUT_TAB_HOTKEY(4689), + OPTIONS_TAB_HOTKEY(4686), + EMOTES_TAB_HOTKEY(4687), + CLAN_TAB_HOTKEY(4683), + MUSIC_TAB_HOTKEY(4688), + + /** + * Chat Notifications settings + *
+ * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false + * LOOT_DROP_NOTIFICATIONS_VALUE: gp value + * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false + * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered + * DROP_ITEM_WARNINGS: 1 is true, 0 is false + * DROP_ITEM_WARNINGS_VALUE: gp value + */ + LOOT_DROP_NOTIFICATIONS(5399), + LOOT_DROP_NOTIFICATIONS_VALUE(5400), + UNTRADEABLE_LOOT_NOTIFICATIONS(5402), + BOSS_KILL_COUNT_UPDATES(4930), + DROP_ITEM_WARNINGS(5411), + DROP_ITEM_WARNINGS_VALUE(5412), + + PARASITE(10151), + ; + + /** + * The raw varbit ID. + */ + private final int id; +} 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 d86d33e89c..45b2369f23 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -30,7 +30,7 @@ package net.runelite.api; * Note: This class is not complete and may not contain a specific animation * required. */ -public final class AnimationID +public class AnimationID { public static final int IDLE = -1; public static final int HERBLORE_PESTLE_AND_MORTAR = 364; @@ -251,257 +251,4 @@ public final class AnimationID // POH Animations public static final int INCENSE_BURNER = 3687; - - //OPENOSRS - - // NPC animations - public static final int FISHING_TRAILBLAZER_HARPOON_2 = 8785; - - public static final int CRYSTALLINE_RAT_DEATH = 8334; - public static final int CRYSTALLINE_BAT_DEATH = 4917; - public static final int CRYSTALLINE_WOLF_DEATH = 8335; - public static final int CRYSTALLINE_SPIDER_DEATH = 8338; - public static final int CRYSTALLINE_UNICORN_DEATH = 6377; - public static final int CRYSTALLINE_DRAGON_DEATH = 92; - public static final int CRYSTALLINE_BEAR_DEATH = 4929; - public static final int CRYSTALLINE_DARK_BEAST_DEATH = 2733; - public static final int CORRUPTED_SCORPION_DEATH = 6256; - - public static final int TABLET_TELEPORT = 4069; - - public static final int THIEVING_STALL = 832; - public static final int PICKPOCKET_SUCCESS = 881; - public static final int PULL_LEVER = 2140; - public static final int STANDARD_PURPLE_TELEPORT = 714; - public static final int ECTOPHIAL_TELEPORT = 878; - public static final int FAIRY_RING_TELEPORT = 3265; - public static final int SCROLL_TELEPORT = 3864; - public static final int XERICS_TALISMAN_TELEPORT = 3865; - public static final int WILDERNESS_OBELISK_TELEPORT = 3945; - public static final int SEED_POD_TELEPORT = 4544; - - //block animations for players and perhaps npcs as well? - public static final int BLOCK_DEFENDER = 4177; - public static final int BLOCK_NO_SHIELD = 420; - public static final int BLOCK_SHIELD = 1156; - public static final int BLOCK_SWORD = 388; - public static final int BLOCK_UNARMED = 424; // Same Animation as failed pickpocked - - public static final int NIGHTMARE_DEATH = 8612; - public static final int LOW_LEVEL_MAGIC_ATTACK = 1162; - public static final int HIGH_LEVEL_MAGIC_ATTACK = 1167; - public static final int BLOWPIPE_ATTACK = 5061; - - // NPC animations - public static final int BLACKJACK_KO = 838; - - // Fight Caves - public static final int TZTOK_JAD_MELEE_ATTACK = 2655; - public static final int TOK_XIL_RANGE_ATTACK = 2633; - public static final int TOK_XIL_MELEE_ATTACK = 2628; - public static final int KET_ZEK_MELEE_ATTACK = 2644; - public static final int KET_ZEK_MAGE_ATTACK = 2647; - public static final int MEJ_KOT_MELEE_ATTACK = 2637; - public static final int MEJ_KOT_HEAL_ATTACK = 2639; - - // Vorkath - public static final int VORKATH_WAKE_UP = 7950; - public static final int VORKATH_DEATH = 7949; - public static final int VORKATH_SLASH_ATTACK = 7951; - public static final int VORKATH_ATTACK = 7952; - public static final int VORKATH_FIRE_BOMB_OR_SPAWN_ATTACK = 7960; - public static final int VORKATH_ACID_ATTACK = 7957; - - // Tekton - public static final int TEKTON_ANVIL = 7475; - public static final int TEKTON_AUTO1 = 7482; - public static final int TEKTON_AUTO2 = 7483; - public static final int TEKTON_AUTO3 = 7484; - public static final int TEKTON_FAST_AUTO1 = 7478; - public static final int TEKTON_FAST_AUTO2 = 7488; - public static final int TEKTON_ENRAGE_AUTO1 = 7492; - public static final int TEKTON_ENRAGE_AUTO2 = 7493; - public static final int TEKTON_ENRAGE_AUTO3 = 7494; - - // Hydra - public static final int HYDRA_WALKING = 8232; - public static final int HYDRA_IDLE = 8233; - public static final int HYDRA_POISON_1 = 8234; - public static final int HYDRA_RANGED_1 = 8235; - public static final int HYDRA_MAGIC_1 = 8236; - public static final int HYDRA_1_1 = 8237; - public static final int HYDRA_1_2 = 8238; - public static final int HYDRA_LIGHTNING = 8241; - public static final int HYDRA_RANGED_2 = 8242; - public static final int HYDRA_MAGIC_2 = 8243; - public static final int HYDRA_2_1 = 8244; - public static final int HYDRA_2_2 = 8245; - public static final int HYDRA_FIRE = 8248; - public static final int HYDRA_RANGED_3 = 8249; - public static final int HYDRA_MAGIC_3 = 8250; - public static final int HYDRA_3_1 = 8251; - public static final int HYDRA_3_2 = 8252; - public static final int HYDRA_MAGIC_4 = 8254; - public static final int HYDRA_POISON_4 = 8254; - public static final int HYDRA_RANGED_4 = 8255; - public static final int HYDRA_RANGED_OR_POISON_ATTACK = 8256; - public static final int HYDRA_4_1 = 8257; - public static final int HYDRA_4_2 = 8258; - - // Inferno animations - public static final int JAL_NIB = 7574; - public static final int JAL_MEJRAH = 7578; - public static final int JAL_MEJRAH_STAND = 7577; - public static final int JAL_AK_RANGE_ATTACK = 7581; - public static final int JAL_AK_MELEE_ATTACK = 7582; - public static final int JAL_AK_MAGIC_ATTACK = 7583; - public static final int JAL_IMKOT = 7597; - public static final int JAL_XIL_MELEE_ATTACK = 7604; - public static final int JAL_XIL_RANGE_ATTACK = 7605; - public static final int JAL_ZEK_MAGE_ATTACK = 7610; - public static final int JAL_ZEK_MELEE_ATTACK = 7612; - public static final int JALTOK_JAD_MELEE_ATTACK = 7590; - public static final int JALTOK_JAD_MAGE_ATTACK = 7592; - public static final int JALTOK_JAD_RANGE_ATTACK = 7593; - public static final int TZKAL_ZUK = 7566; - public static final int JAL_MEJJAK = 2858; - - // General Graardor - public static final int MINION_AUTO1 = 6154; - public static final int MINION_AUTO2 = 6156; - public static final int MINION_AUTO3 = 7071; - public static final int MINION_AUTO4 = 7073; - public static final int GENERAL_AUTO1 = 7018; - public static final int GENERAL_AUTO2 = 7020; - public static final int GENERAL_AUTO3 = 7021; - - // Kr'il Tsutsaroth - public static final int ZAMMY_GENERIC_AUTO = 64; - public static final int KRIL_AUTO = 6948; - public static final int KRIL_SPEC = 6950; - public static final int ZAKL_AUTO = 7077; - public static final int BALFRUG_AUTO = 4630; - - // Commander Zilyana - public static final int ZILYANA_MELEE_AUTO = 6964; - public static final int ZILYANA_AUTO = 6967; - public static final int ZILYANA_SPEC = 6970; - public static final int STARLIGHT_AUTO = 6376; - public static final int BREE_AUTO = 7026; - public static final int GROWLER_AUTO = 7037; - - // Kree'arra - public static final int KREE_RANGED = 6978; - public static final int SKREE_AUTO = 6955; - public static final int GEERIN_AUTO = 6956; - public static final int GEERIN_FLINCH = 6958; - public static final int KILISA_AUTO = 6957; - - // Vetion - public static final int VETION_EARTHQUAKE = 5507; - - // Zulrah - public static final int ZULRAH_DEATH = 5804; - public static final int ZULRAH_PHASE = 5072; - - //Dagannoth Kings - public static final int DAG_REX = 2853; - public static final int DAG_PRIME = 2854; - public static final int DAG_SUPREME = 2855; - - // Lizardman shaman - public static final int LIZARDMAN_SHAMAN_SPAWN = 7157; - public static final int LIZARDMAN_SHAMAN_SPAWN_EXPLOSION = 7159; - - // Cerberus - public static final int CERBERUS_MAGIC_ATTACK = 4489; - public static final int CERBERUS_RANGED_ATTACK = 4490; - public static final int CERBERUS_MELEE_ATTACK = 4491; - public static final int CERBERUS_LAVA_ATTACK = 4493; - public static final int CERBERUS_SUMMON_GHOSTS = 4494; - - // Gauntlet Hunleff - public static final int HUNLEFF_TRAMPLE = 8420; - public static final int HUNLEFF_ATTACK = 8419; - public static final int HUNLEFF_TORNADO = 8418; - public static final int HUNLLEF_SWITCH_TO_MAGIC = 8754; - public static final int HUNLLEF_SWITCH_TO_RANGED = 8755; - - //Zalcano - public static final int ZALCANO_KNOCKED_DOWN = 8437; - public static final int ZALCANO_WAKEUP = 8439; - public static final int ZALCANO_ROCK_GLOWING = 8448; - - // Theatre of Blood - Sugadinti Maiden - public static final int SUGADINTI_MAIDEN_BLOOD_SPLAT_ATTACK = 8091; - public static final int SUGADINTI_MAIDEN_MAGIC_ATTACK = 8092; - public static final int SUGADINTI_MAIDEN_DEATH = 8094; - - // Theatre of Blood - Pestilent Bloat - public static final int BLOAT_SLEEP = 8082; - - // Theatre of Blood - Sotetseg - public static final int SOTETSEG_MELEE_ATTACK = 8138; - public static final int SOTETSEG_REGULAR_PROJECTILE_ATTACK = 8139; - - // Theatre of Blood - Verzik Vitur - public static final int VERZIK_PHASE_1_MAGIC_ATTACK = 8109; - public static final int VERZIK_PHASE_1_MAGIC_ATTACK_CHANNEL = 8110; - public static final int VERZIK_CHANGE_TO_PHASE_2 = 8111; - public static final int VERZIK_PHASE_2_MAGIC_ATTACK = 8114; - public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_1 = 8116; - public static final int VERZIK_PHASE_2_HEALING_CHANNEL = 8117; - public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_2 = 8118; - public static final int VERZIK_CHANGE_TO_PHASE_3 = 8119; - public static final int VERZIK_PHASE_3_MELEE_ATTACK = 8123; - public static final int VERZIK_PHASE_3_MAGIC_ATTACK = 8124; - public static final int VERZIK_PHASE_3_RANGED_ATTACK = 8125; - public static final int VERZIK_PHASE_3_GREEN_POOL_ATTACK = 8126; - public static final int VERZIK_PHASE_3_WEB_ATTACK = 8127; - public static final int VERZIK_DEATH_1 = 8128; - public static final int VERZIK_DEATH_2 = 8129; - - // The Nightmare of Ashihama - public static final int NIGHTMARE_SPAWN_SLEEPWALKERS = 8572; - public static final int NIGHTMARE_FLOATY = 8592; - public static final int NIGHTMARE_WALKING = 8592; - public static final int NIGHTMARE_IDLE = 8593; - public static final int NIGHTMARE_MELEE_ATTACK = 8594; - public static final int NIGHTMARE_MAGIC_ATTACK = 8595; - public static final int NIGHTMARE_RANGED_ATTACK = 8596; - public static final int NIGHTMARE_SURGE_ATTACK = 8597; - public static final int NIGHTMARE_GHOST_AOE_ATTACK = 8598; - public static final int NIGHTMARE_CURSE_PRAYERS_ATTACK = 8599; - public static final int NIGHTMARE_SPAWN_INFECTIOUS_SPORES = 8600; - public static final int NIGHTMARE_SPAWN_ROOM_SECTION_FLOWERS = 8601; - public static final int NIGHTMARE_CHANNEL_DEVASTATING_ATTACK = 8604; - public static final int NIGHTMARE_SWITCH_TO_DEVIL_PHASE = 8605; - public static final int NIGHTMARE_PARASITE_ATTACK = 8606; - public static final int NIGHTMARE_JUMP_DOWN = 8607; - public static final int NIGHTMARE_SINK_DOWN = 8608; - public static final int NIGHTMARE_JUMP_UP = 8609; - public static final int NIGHTMARE_JUMP_UP_2 = 8610; - public static final int NIGHTMARE_WAKE_UP = 8611; - - //Supplies Tracker - public static final int ONEHAND_SLASH_SWORD_ANIMATION = 390; - public static final int ONEHAND_STAB_SWORD_ANIMATION = 386; - public static final int SCYTHE_OF_VITUR_ANIMATION = 8056; - public static final int LOW_LEVEL_STANDARD_SPELLS = 711; - public static final int WAVE_SPELL_ANIMATION = 727; - public static final int SURGE_SPELL_ANIMATION = 7855; - public static final int HIGH_ALCH_ANIMATION = 713; - public static final int LUNAR_HUMIDIFY = 6294; - public static final int PRAY_AT_ALTAR = 645; - public static final int ENSOULED_HEADS_ANIMATION = 7198; - - // Weapon attack animations - public static final int ONEHAND_SLASH_AXE_ANIMATION = 395; - public static final int ONEHAND_CRUSH_PICKAXE_ANIMATION = 400; - public static final int ONEHAND_CRUSH_AXE_ANIMATION = 401; - public static final int UNARMED_PUNCH_ANIMATION = 422; - public static final int UNARMED_KICK_ANIMATION = 423; - public static final int BOW_ATTACK_ANIMATION = 426; - public static final int ONEHAND_STAB_HALBERD_ANIMATION = 428; - public static final int ONEHAND_SLASH_HALBERD_ANIMATION = 440; -} \ No newline at end of file +} diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index d9845f473d..f0901f473d 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -571,7 +571,7 @@ public enum Varbits EXPLORER_RING_ALCHS(4554), EXPLORER_RING_RUNENERGY(4553), - WINTERTODT_TIMER(7980), + /** * League relics @@ -610,102 +610,76 @@ public enum Varbits PVP_SPEC_ORB(8121), //OPENOSRS - /* - * Kharedst's Memoirs Teleport Item - */ - KHAREDSTS_MEMOIRS_CHARGES(6035), /** * Grand Exchange */ GRAND_EXCHANGE_PRICE_PER_ITEM(4398), - - /** - * Locked Prayers - * 0-7 = Locked - * 8 = Unlocked + /* + * Kharedst's Memoirs Teleport Item */ - CHIVPIETY_UNLOCKED(3909), + KHAREDSTS_MEMOIRS_CHARGES(6035), + + LMS_POISON_PROGRESS(5317), /** - * Locked Prayers - * 0 = Locked - * 1 = Unlocked + * The y coordinate of the final safespace (world coord) */ - - RIGOUR_UNLOCKED(5451), - AUGURY_UNLOCKED(5452), - PRESERVE_UNLOCKED(5453), + LMS_SAFE_Y(5320), /** - * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating + * 1 is true, 0 is false. */ - BLOAT_DOOR(6447), + GAUNTLET_FINAL_ROOM_ENTERED(9177), /** - * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb + * 1 is true, 0 is false. */ - THEATRE_OF_BLOOD_ORB_1(6442), - THEATRE_OF_BLOOD_ORB_2(6443), - THEATRE_OF_BLOOD_ORB_3(6444), - THEATRE_OF_BLOOD_ORB_4(6445), - THEATRE_OF_BLOOD_ORB_5(6446), + GAUNTLET_ENTERED(9178), + + WITHDRAW_X_AMOUNT(3960), + + IN_PVP_AREA(8121), /** - * Nightmare Zone + * Value of hotkey varbits can be 0-13 + * 0 corresponds to no hotkey set + * 1-12 correspond to F1-F12 respectively + * 13 corresponds to escape */ - NMZ_OVERLOAD(3955), + COMBAT_TAB_HOTKEY(4675), + STATS_TAB_HOTKEY(4676), + QUESTS_TAB_HOTKEY(4677), + INVENTORY_TAB_HOTKEY(4678), + EQUIPMENT_TAB_HOTKEY(4679), + PRAYER_TAB_HOTKEY(4680), + SPELLBOOK_TAB_HOTKEY(4682), + FRIENDS_TAB_HOTKEY(4684), + ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), + LOGOUT_TAB_HOTKEY(4689), + OPTIONS_TAB_HOTKEY(4686), + EMOTES_TAB_HOTKEY(4687), + CLAN_TAB_HOTKEY(4683), + MUSIC_TAB_HOTKEY(4688), /** - * Pyramid plunder + * Chat Notifications settings + *
+ * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false + * LOOT_DROP_NOTIFICATIONS_VALUE: gp value + * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false + * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered + * DROP_ITEM_WARNINGS: 1 is true, 0 is false + * DROP_ITEM_WARNINGS_VALUE: gp value */ - PYRAMID_PLUNDER_SARCO_OPEN(2362), - PYRAMID_PLUNDER_CHEST_OPEN(2363), - - /** - * In the Wilderness - */ - IN_THE_WILDERNESS(5963), - - /** - * Kingdom Management - */ - KINGDOM_WORKERS_WOOD(81), - KINGDOM_WORKERS_HERBS(82), - KINGDOM_WORKERS_FISHING(83), - KINGDOM_WORKERS_MINING(84), - KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked - KINGDOM_WORKERS_HARDWOOD(2131), - KINGDOM_WORKERS_FARM(2132), - KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both - KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax - - /** - * Varbit used for Slayer reward points - */ - SLAYER_REWARD_POINTS(4068), - - /** - * 0 = standard - * 1 = ancients - * 2 = lunars - * 3 = arrceus - **/ - SPELLBOOK(4070), - - /** - * Bank settings/flags - **/ - BANK_NOTE_FLAG(3958), - - - /** - * Spells being auto-casted - */ - AUTO_CAST_SPELL(276), - - /** + LOOT_DROP_NOTIFICATIONS(5399), + LOOT_DROP_NOTIFICATIONS_VALUE(5400), + UNTRADEABLE_LOOT_NOTIFICATIONS(5402), + BOSS_KILL_COUNT_UPDATES(4930), + DROP_ITEM_WARNINGS(5411), + DROP_ITEM_WARNINGS_VALUE(5412), + /** * Temple Trekking */ TREK_POINTS(1955), @@ -822,6 +796,8 @@ public enum Varbits */ BUILDING_MODE(2176), + WINTERTODT_TIMER(7980), + /** * 1 if in game, 0 if not */ @@ -837,64 +813,81 @@ public enum Varbits */ LMS_SAFE_X(5316), - LMS_POISON_PROGRESS(5317), + /** + * Locked Prayers + * 0-7 = Locked + * 8 = Unlocked + */ + CHIVPIETY_UNLOCKED(3909), /** - * The y coordinate of the final safespace (world coord) + * Locked Prayers + * 0 = Locked + * 1 = Unlocked */ - LMS_SAFE_Y(5320), + + RIGOUR_UNLOCKED(5451), + AUGURY_UNLOCKED(5452), + PRESERVE_UNLOCKED(5453), + + /** + * Spells being auto-casted + */ + AUTO_CAST_SPELL(276), /** - * 1 is true, 0 is false. + * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating */ - GAUNTLET_FINAL_ROOM_ENTERED(9177), + BLOAT_DOOR(6447), /** - * 1 is true, 0 is false. + * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb */ - GAUNTLET_ENTERED(9178), + THEATRE_OF_BLOOD_ORB_1(6442), + THEATRE_OF_BLOOD_ORB_2(6443), + THEATRE_OF_BLOOD_ORB_3(6444), + THEATRE_OF_BLOOD_ORB_4(6445), + THEATRE_OF_BLOOD_ORB_5(6446), - WITHDRAW_X_AMOUNT(3960), + //NMZ + NMZ_OVERLOAD(3955), - IN_PVP_AREA(8121), + //Pyramid Plunder + PYRAMID_PLUNDER_SARCO_OPEN(2362), + PYRAMID_PLUNDER_CHEST_OPEN(2363), /** - * Value of hotkey varbits can be 0-13 - * 0 corresponds to no hotkey set - * 1-12 correspond to F1-F12 respectively - * 13 corresponds to escape + * Varbit used for Slayer reward points */ - COMBAT_TAB_HOTKEY(4675), - STATS_TAB_HOTKEY(4676), - QUESTS_TAB_HOTKEY(4677), - INVENTORY_TAB_HOTKEY(4678), - EQUIPMENT_TAB_HOTKEY(4679), - PRAYER_TAB_HOTKEY(4680), - SPELLBOOK_TAB_HOTKEY(4682), - FRIENDS_TAB_HOTKEY(4684), - ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), - LOGOUT_TAB_HOTKEY(4689), - OPTIONS_TAB_HOTKEY(4686), - EMOTES_TAB_HOTKEY(4687), - CLAN_TAB_HOTKEY(4683), - MUSIC_TAB_HOTKEY(4688), + SLAYER_REWARD_POINTS(4068), /** - * Chat Notifications settings - *
- * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false - * LOOT_DROP_NOTIFICATIONS_VALUE: gp value - * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false - * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered - * DROP_ITEM_WARNINGS: 1 is true, 0 is false - * DROP_ITEM_WARNINGS_VALUE: gp value + * 0 = standard + * 1 = ancients + * 2 = lunars + * 3 = arrceus + **/ + SPELLBOOK(4070), + + /** + * Bank settings/flags + **/ + BANK_NOTE_FLAG(3958), + + KINGDOM_WORKERS_WOOD(81), + KINGDOM_WORKERS_HERBS(82), + KINGDOM_WORKERS_FISHING(83), + KINGDOM_WORKERS_MINING(84), + KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked + KINGDOM_WORKERS_HARDWOOD(2131), + KINGDOM_WORKERS_FARM(2132), + KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both + KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax + + /** + * In the Wilderness */ - LOOT_DROP_NOTIFICATIONS(5399), - LOOT_DROP_NOTIFICATIONS_VALUE(5400), - UNTRADEABLE_LOOT_NOTIFICATIONS(5402), - BOSS_KILL_COUNT_UPDATES(4930), - DROP_ITEM_WARNINGS(5411), - DROP_ITEM_WARNINGS_VALUE(5412), + IN_THE_WILDERNESS(5963), ; /** diff --git a/runelite-client/src/main/java/com/openosrs/client/util/Clipboard.java b/runelite-client/src/main/java/com/openosrs/client/util/Clipboard.java deleted file mode 100644 index 5f9f236c16..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/util/Clipboard.java +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2018, Connor - * 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 com.openosrs.client.util; - -import java.awt.Toolkit; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.StringSelection; -import java.awt.datatransfer.Transferable; -import java.awt.datatransfer.UnsupportedFlavorException; -import java.io.IOException; - -public class Clipboard -{ - public static String retrieve() - { - Transferable contents = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null); - - if (contents == null || !contents.isDataFlavorSupported(DataFlavor.stringFlavor)) - { - return null; - } - - try - { - return (String) contents.getTransferData(DataFlavor.stringFlavor); - } - catch (UnsupportedFlavorException | IOException ex) - { - return null; - } - } - - public static void store(String contents) - { - final StringSelection selection = new StringSelection(contents); - - Toolkit.getDefaultToolkit().getSystemClipboard().setContents(selection, null); - } -} \ No newline at end of file From 97dc44f65d91c7ff4bca88ff905b9a78240ed3ec Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 10:28:53 +0000 Subject: [PATCH 065/133] varbits: remove unused, fix compile error --- .../main/java/net/runelite/api/Varbits.java | 298 +----------------- 1 file changed, 14 insertions(+), 284 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index f0901f473d..0bdf355c33 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -405,6 +405,17 @@ public enum Varbits */ QUEST_THE_HAND_IN_THE_SAND(1527), + /** + * 0 = Sir Bedivere + * 1 = Sir Pelleas + * 2 = Sir Tristram + * 3 = Sir Palomedes + * 4 = Sir Lucan + * 5 = Sir Gawain + * 6 = Sir Kay + * 7 = Sir Lancelot + * 8 = Completed (Chivalry and Piety are unlocked) + */ CAMELOT_TRAINING_ROOM_STATUS(3909), /** @@ -571,7 +582,7 @@ public enum Varbits EXPLORER_RING_ALCHS(4554), EXPLORER_RING_RUNENERGY(4553), - + WINTERTODT_TIMER(7980), /** * League relics @@ -607,291 +618,10 @@ public enum Varbits * * @see
The OSRS Wiki's Minimap page */ - PVP_SPEC_ORB(8121), - - //OPENOSRS - - /** - * Grand Exchange - */ - GRAND_EXCHANGE_PRICE_PER_ITEM(4398), - - /* - * Kharedst's Memoirs Teleport Item - */ - KHAREDSTS_MEMOIRS_CHARGES(6035), - - LMS_POISON_PROGRESS(5317), - - /** - * The y coordinate of the final safespace (world coord) - */ - LMS_SAFE_Y(5320), - - /** - * 1 is true, 0 is false. - */ - GAUNTLET_FINAL_ROOM_ENTERED(9177), - - /** - * 1 is true, 0 is false. - */ - GAUNTLET_ENTERED(9178), - - WITHDRAW_X_AMOUNT(3960), - - IN_PVP_AREA(8121), - - /** - * Value of hotkey varbits can be 0-13 - * 0 corresponds to no hotkey set - * 1-12 correspond to F1-F12 respectively - * 13 corresponds to escape - */ - COMBAT_TAB_HOTKEY(4675), - STATS_TAB_HOTKEY(4676), - QUESTS_TAB_HOTKEY(4677), - INVENTORY_TAB_HOTKEY(4678), - EQUIPMENT_TAB_HOTKEY(4679), - PRAYER_TAB_HOTKEY(4680), - SPELLBOOK_TAB_HOTKEY(4682), - FRIENDS_TAB_HOTKEY(4684), - ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), - LOGOUT_TAB_HOTKEY(4689), - OPTIONS_TAB_HOTKEY(4686), - EMOTES_TAB_HOTKEY(4687), - CLAN_TAB_HOTKEY(4683), - MUSIC_TAB_HOTKEY(4688), - - /** - * Chat Notifications settings - *
- * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false - * LOOT_DROP_NOTIFICATIONS_VALUE: gp value - * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false - * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered - * DROP_ITEM_WARNINGS: 1 is true, 0 is false - * DROP_ITEM_WARNINGS_VALUE: gp value - */ - LOOT_DROP_NOTIFICATIONS(5399), - LOOT_DROP_NOTIFICATIONS_VALUE(5400), - UNTRADEABLE_LOOT_NOTIFICATIONS(5402), - BOSS_KILL_COUNT_UPDATES(4930), - DROP_ITEM_WARNINGS(5411), - DROP_ITEM_WARNINGS_VALUE(5412), - /** - * Temple Trekking - */ - TREK_POINTS(1955), - TREK_STARTED(1956), - TREK_EVENT(1958), - TREK_STATUS(6719), - BLOAT_ENTERED_ROOM(6447), - - /** - * f2p Quest varbits, these don't hold the completion value. - */ - QUEST_DEMON_SLAYER(2561), - QUEST_GOBLIN_DIPLOMACY(2378), - QUEST_MISTHALIN_MYSTERY(3468), - QUEST_THE_CORSAIR_CURSE(6071), - QUEST_X_MARKS_THE_SPOT(8063), - QUEST_ERNEST_LEVER_A(1788), - QUEST_ERNEST_LEVER_B(1789), - QUEST_ERNEST_LEVER_C(1790), - QUEST_ERNEST_LEVER_D(1791), - QUEST_ERNEST_LEVER_E(1792), - QUEST_ERNEST_LEVER_F(1793), - - /** - * member Quest varbits, these don't hold the completion value. - */ - QUEST_ANIMAL_MAGNETISM(3185), - QUEST_BETWEEN_A_ROCK(299), - QUEST_CONTACT(3274), - QUEST_ZOGRE_FLESH_EATERS(487), - QUEST_DARKNESS_OF_HALLOWVALE(2573), - QUEST_DEATH_TO_THE_DORGESHUUN(2258), - QUEST_DESERT_TREASURE(358), - QUEST_DEVIOUS_MINDS(1465), - QUEST_EAGLES_PEAK(2780), - QUEST_ELEMENTAL_WORKSHOP_II(2639), - QUEST_ENAKHRAS_LAMENT(1560), - QUEST_ENLIGHTENED_JOURNEY(2866), - QUEST_THE_EYES_OF_GLOUPHRIE(2497), - QUEST_FAIRYTALE_I_GROWING_PAINS(1803), - QUEST_FAIRYTALE_II_CURE_A_QUEEN(2326), - QUEST_THE_FEUD(334), // 14 = able to pickpocket - QUEST_FORGETTABLE_TALE(822), - QUEST_GARDEN_OF_TRANQUILLITY(961), - QUEST_GHOSTS_AHOY(217), - QUEST_THE_GIANT_DWARF(571), - QUEST_THE_GOLEM(346), - QUEST_HORROR_FROM_THE_DEEP(34), - QUEST_ICTHLARINS_LITTLE_HELPER(418), - QUEST_IN_AID_OF_THE_MYREQUE(1990), - QUEST_THE_LOST_TRIBE(532), - QUEST_LUNAR_DIPLOMACY(2448), - QUEST_MAKING_HISTORY(1383), - QUEST_MOUNTAIN_DAUGHTER(260), - QUEST_MOURNINGS_END_PART_II(1103), - QUEST_MY_ARMS_BIG_ADVENTURE(2790), - QUEST_RATCATCHERS(1404), - QUEST_RECIPE_FOR_DISASTER(1850), - QUEST_RECRUITMENT_DRIVE(657), - QUEST_ROYAL_TROUBLE(2140), - QUEST_THE_SLUG_MENACE(2610), - QUEST_SHADOW_OF_THE_STORM(1372), - QUEST_A_SOULS_BANE(2011), - QUEST_SPIRITS_OF_THE_ELID(1444), - QUEST_SWAN_SONG(2098), - QUEST_A_TAIL_OF_TWO_CATS(1028), - QUEST_TEARS_OF_GUTHIX(451), - QUEST_WANTED(1051), - QUEST_COLD_WAR(3293), - QUEST_THE_FREMENNIK_ISLES(3311), - QUEST_TOWER_OF_LIFE(3337), - QUEST_WHAT_LIES_BELOW(3523), - QUEST_OLAFS_QUEST(3534), - QUEST_ANOTHER_SLICE_OF_HAM(3550), - QUEST_DREAM_MENTOR(3618), - QUEST_GRIM_TALES(2783), - QUEST_KINGS_RANSOM(3888), - QUEST_MONKEY_MADNESS_II(5027), - QUEST_CLIENT_OF_KOUREND(5619), - QUEST_BONE_VOYAGE(5795), - QUEST_THE_QUEEN_OF_THIEVES(6037), - QUEST_THE_DEPTHS_OF_DESPAIR(6027), - QUEST_DRAGON_SLAYER_II(6104), - QUEST_TALE_OF_THE_RIGHTEOUS(6358), - QUEST_A_TASTE_OF_HOPE(6396), - QUEST_MAKING_FRIENDS_WITH_MY_ARM(6528), - QUEST_THE_ASCENT_OF_ARCEUUS(7856), - QUEST_THE_FORSAKEN_TOWER(7796), - //TODO - QUEST_SONG_OF_THE_ELVES(7796), - - /** - * mini-quest varbits, these don't hold the completion value. - */ - QUEST_ARCHITECTURAL_ALLIANCE(4982), - QUEST_BEAR_YOUR_SOUL(5078), - QUEST_CURSE_OF_THE_EMPTY_LORD(821), - QUEST_ENCHANTED_KEY(1391), - QUEST_THE_GENERALS_SHADOW(3330), - QUEST_SKIPPY_AND_THE_MOGRES(1344), - QUEST_LAIR_OF_TARN_RAZORLOR(3290), - QUEST_FAMILY_PEST(5347), - QUEST_THE_MAGE_ARENA_II(6067), - //TODO - QUEST_IN_SEARCH_OF_KNOWLEDGE(6067), - - /** - * Spellbook filtering (1 = unfiltered, 0 = filtered) - */ - FILTER_SPELLBOOK(6718), - - /** - * POH Building mode (1 = yes, 0 = no) - */ - BUILDING_MODE(2176), - - WINTERTODT_TIMER(7980), - - /** - * 1 if in game, 0 if not - */ - LMS_IN_GAME(5314), - - /** - * Amount of pvp kills in current game - */ - LMS_KILLS(5315), - - /** - * The x coordinate of the final safespace (world coord) - */ - LMS_SAFE_X(5316), - - /** - * Locked Prayers - * 0-7 = Locked - * 8 = Unlocked - */ - CHIVPIETY_UNLOCKED(3909), - - /** - * Locked Prayers - * 0 = Locked - * 1 = Unlocked - */ - - RIGOUR_UNLOCKED(5451), - AUGURY_UNLOCKED(5452), - PRESERVE_UNLOCKED(5453), - - /** - * Spells being auto-casted - */ - AUTO_CAST_SPELL(276), - - /** - * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating - */ - BLOAT_DOOR(6447), - - /** - * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb - */ - THEATRE_OF_BLOOD_ORB_1(6442), - THEATRE_OF_BLOOD_ORB_2(6443), - THEATRE_OF_BLOOD_ORB_3(6444), - THEATRE_OF_BLOOD_ORB_4(6445), - THEATRE_OF_BLOOD_ORB_5(6446), - - //NMZ - NMZ_OVERLOAD(3955), - - //Pyramid Plunder - PYRAMID_PLUNDER_SARCO_OPEN(2362), - PYRAMID_PLUNDER_CHEST_OPEN(2363), - - /** - * Varbit used for Slayer reward points - */ - SLAYER_REWARD_POINTS(4068), - - /** - * 0 = standard - * 1 = ancients - * 2 = lunars - * 3 = arrceus - **/ - SPELLBOOK(4070), - - /** - * Bank settings/flags - **/ - BANK_NOTE_FLAG(3958), - - KINGDOM_WORKERS_WOOD(81), - KINGDOM_WORKERS_HERBS(82), - KINGDOM_WORKERS_FISHING(83), - KINGDOM_WORKERS_MINING(84), - KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked - KINGDOM_WORKERS_HARDWOOD(2131), - KINGDOM_WORKERS_FARM(2132), - KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both - KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax - - /** - * In the Wilderness - */ - IN_THE_WILDERNESS(5963), - ; + PVP_SPEC_ORB(8121); /** * The raw varbit ID. */ private final int id; -} \ No newline at end of file +} From aadfb88aa6206d08b28d4738d0d95b3b48bef1e9 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 10:47:27 +0000 Subject: [PATCH 066/133] mes: these dont need to be static you can @Inject a plugin instance --- .../menuentryswapper/MenuEntrySwapperPlugin.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 6c9b6cd800..0dabbef623 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 @@ -152,7 +152,7 @@ public class MenuEntrySwapperPlugin extends Plugin @Getter private boolean configuringShiftClick = false; - private static final Multimap swaps = LinkedHashMultimap.create(); + private final Multimap swaps = LinkedHashMultimap.create(); private final ArrayListMultimap optionIndexes = ArrayListMultimap.create(); @Provides @@ -361,22 +361,22 @@ public class MenuEntrySwapperPlugin extends Plugin swap("eat", "guzzle", config::swapRockCake); } - public static void swap(String option, String swappedOption, Supplier enabled) + public void swap(String option, String swappedOption, Supplier enabled) { swap(option, alwaysTrue(), swappedOption, enabled); } - private void swap(String option, String target, String swappedOption, Supplier enabled) + public void swap(String option, String target, String swappedOption, Supplier enabled) { swap(option, equalTo(target), swappedOption, enabled); } - private static void swap(String option, Predicate targetPredicate, String swappedOption, Supplier enabled) + public void swap(String option, Predicate targetPredicate, String swappedOption, Supplier enabled) { swaps.put(option, new Swap(alwaysTrue(), targetPredicate, swappedOption, enabled, true)); } - private void swapContains(String option, Predicate targetPredicate, String swappedOption, Supplier enabled) + public void swapContains(String option, Predicate targetPredicate, String swappedOption, Supplier enabled) { swaps.put(option, new Swap(alwaysTrue(), targetPredicate, swappedOption, enabled, false)); } From 131deca54eb5566976fd00e2f3801b83052925b8 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 10:47:43 +0000 Subject: [PATCH 067/133] eventbus: remove method name requirement --- .../src/main/java/net/runelite/client/eventbus/EventBus.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java index 2340e306dd..3d9cf9363e 100644 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java +++ b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java @@ -138,7 +138,10 @@ public class EventBus } final String preferredName = "on" + parameterClazz.getSimpleName(); - Preconditions.checkArgument(method.getName().equals(preferredName), "Subscribed method " + method + " should be named " + preferredName); + if (!method.getName().equals(preferredName)) + { + log.warn("Subscribed method " + method + " should be named " + preferredName); + } method.setAccessible(true); SubscriberMethod lambda = null; From a2d9993fdcaba724f3066a1e712122a72ccbd1d1 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 17:08:01 +0000 Subject: [PATCH 068/133] fix compile error with widgethiddenchanged --- .../main/java/net/runelite/api/events/WidgetHiddenChanged.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-api/src/main/java/net/runelite/api/events/WidgetHiddenChanged.java b/runelite-api/src/main/java/net/runelite/api/events/WidgetHiddenChanged.java index b66c05267f..705202468e 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/WidgetHiddenChanged.java +++ b/runelite-api/src/main/java/net/runelite/api/events/WidgetHiddenChanged.java @@ -31,7 +31,7 @@ import lombok.Data; * An event where the hidden state of a {@link Widget} has been modified. */ @Data -public class WidgetHiddenChanged implements Event +public class WidgetHiddenChanged { /** * The affected widget. From 19d926c0b6ac2c2f4b43f90370a0acc190abb0e4 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 17:08:27 +0000 Subject: [PATCH 069/133] remove unused config option --- .../openosrs/client/config/OpenOSRSConfig.java | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/runelite-client/src/main/java/com/openosrs/client/config/OpenOSRSConfig.java b/runelite-client/src/main/java/com/openosrs/client/config/OpenOSRSConfig.java index 29d8d6712b..88faf46cb5 100644 --- a/runelite-client/src/main/java/com/openosrs/client/config/OpenOSRSConfig.java +++ b/runelite-client/src/main/java/com/openosrs/client/config/OpenOSRSConfig.java @@ -106,22 +106,11 @@ public interface OpenOSRSConfig extends Config return true; } - @ConfigItem( - keyName = "keyboardPin", - name = "Keyboard bank pin", - description = "Enables you to type your bank pin", - position = 22 - ) - default boolean keyboardPin() - { - return false; - } - @ConfigItem( keyName = "detachHotkey", name = "Detach Cam", - description = "Detach Camera hotkey, press this and it will activate detatched camera.", - position = 23 + description = "Detach Camera hotkey, press this and it will activate detached camera.", + position = 22 ) default Keybind detachHotkey() { From 925f0f54e2fa273d2aa19ce2da58236677e63044 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Mon, 8 Feb 2021 17:09:56 +0000 Subject: [PATCH 070/133] fix naming inconsistency --- runescape-client/src/main/java/GameEngine.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/runescape-client/src/main/java/GameEngine.java b/runescape-client/src/main/java/GameEngine.java index 23d72bba20..5cab90a2d5 100644 --- a/runescape-client/src/main/java/GameEngine.java +++ b/runescape-client/src/main/java/GameEngine.java @@ -25,7 +25,7 @@ import net.runelite.mapping.ObfuscatedName; import net.runelite.mapping.ObfuscatedSignature; @ObfuscatedName("bd") -@Implements("GameShell") +@Implements("GameEngine") public abstract class GameEngine extends Applet implements Runnable, FocusListener, WindowListener { @ObfuscatedName("h") @ObfuscatedSignature( @@ -37,14 +37,14 @@ public abstract class GameEngine extends Applet implements Runnable, FocusListen @ObfuscatedSignature( descriptor = "Lbd;" ) - @Export("gameShell") + @Export("gameEngine") static GameEngine gameEngine; @ObfuscatedName("t") @ObfuscatedGetter( intValue = 548418733 ) - @Export("GameShell_redundantStartThreadCount") - static int GameShell_redundantStartThreadCount; + @Export("GameEngine_redundantStartThreadCount") + static int GameEngine_redundantStartThreadCount; @ObfuscatedName("j") @ObfuscatedGetter( longValue = -299301004563523829L @@ -193,7 +193,7 @@ public abstract class GameEngine extends Applet implements Runnable, FocusListen static { gameEngine = null; // L: 41 - GameShell_redundantStartThreadCount = 0; // L: 43 + GameEngine_redundantStartThreadCount = 0; // L: 43 stopTimeMs = 0L; // L: 44 isKilled = false; // L: 45 cycleDurationMillis = 20; // L: 48 @@ -452,8 +452,8 @@ public abstract class GameEngine extends Applet implements Runnable, FocusListen protected final void startThread(int var1, int var2, int var3) { try { if (gameEngine != null) { // L: 220 - ++GameShell_redundantStartThreadCount; // L: 221 - if (GameShell_redundantStartThreadCount >= 3) { // L: 222 + ++GameEngine_redundantStartThreadCount; // L: 221 + if (GameEngine_redundantStartThreadCount >= 3) { // L: 222 this.error("alreadyloaded"); // L: 223 return; // L: 224 } From ca76056feeca9075fecd8bbf17dc5581e8b86837 Mon Sep 17 00:00:00 2001 From: Auenc Date: Tue, 9 Feb 2021 03:56:11 +0000 Subject: [PATCH 071/133] idle notifier: Add Ectofuntus animations (#13177) --- .../src/main/java/net/runelite/api/AnimationID.java | 6 ++++++ .../client/plugins/idlenotifier/IdleNotifierPlugin.java | 4 ++++ 2 files changed, 10 insertions(+) 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 cb2981e15c..9358aa4a9f 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -209,6 +209,12 @@ public final class AnimationID public static final int HOME_MAKE_TABLET = 4067; public static final int DRAGONFIRE_SHIELD_SPECIAL = 6696; + // Ectofuntus animations + public static final int ECTOFUNTUS_FILL_SLIME_BUCKET = 4471; + public static final int ECTOFUNTUS_GRIND_BONES = 1648; + public static final int ECTOFUNTUS_INSERT_BONES = 1649; + public static final int ECTOFUNTUS_EMPTY_BIN = 1650; + // NPC animations public static final int TZTOK_JAD_MAGIC_ATTACK = 2656; public static final int TZTOK_JAD_RANGE_ATTACK = 2652; 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 e9d2d01896..6e3be57ee4 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 @@ -268,6 +268,10 @@ public class IdleNotifierPlugin extends Plugin case MAGIC_ENCHANTING_BOLTS: /* Prayer */ case USING_GILDED_ALTAR: + case ECTOFUNTUS_FILL_SLIME_BUCKET: + case ECTOFUNTUS_INSERT_BONES: + case ECTOFUNTUS_GRIND_BONES: + case ECTOFUNTUS_EMPTY_BIN: /* Farming */ case FARMING_MIX_ULTRACOMPOST: case FARMING_HARVEST_BUSH: From 2a953b497bc7658264a3e6ba73832b2d9748eb55 Mon Sep 17 00:00:00 2001 From: Cyborger1 <45152844+Cyborger1@users.noreply.github.com> Date: Tue, 9 Feb 2021 00:29:34 -0500 Subject: [PATCH 072/133] clues: Improve Lovakengj Sulphur Mine area descriptions (#13180) --- .../client/plugins/cluescrolls/clues/CoordinateClue.java | 2 +- .../plugins/cluescrolls/clues/hotcold/HotColdLocation.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java index 5918f65343..5c0e093b6c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java @@ -229,7 +229,7 @@ public class CoordinateClue extends ClueScroll implements TextClueScroll, Locati .put(new WorldPoint(2202, 3825, 0), new CoordinateClueInfo("Pirates' Cove, between Lunar Isle and Rellekka.", ANCIENT_WIZARDS)) .put(new WorldPoint(1761, 3853, 0), new CoordinateClueInfo("Arceuus essence mine (CIS).", BRASSICAN_MAGE)) .put(new WorldPoint(2090, 3863, 0), new CoordinateClueInfo("South of Lunar Isle, west of Astral altar.", ANCIENT_WIZARDS)) - .put(new WorldPoint(1442, 3878, 0), new CoordinateClueInfo("Sulphur Mine.", BRASSICAN_MAGE)) + .put(new WorldPoint(1442, 3878, 0), new CoordinateClueInfo("Northern area of the Lovakengj Sulphur Mine. Facemask or Slayer Helmet recommended.", BRASSICAN_MAGE)) .put(new WorldPoint(3380, 3929, 0), new CoordinateClueInfo("Wilderness. Near Volcano.", ANCIENT_WIZARDS)) .put(new WorldPoint(3188, 3939, 0), new CoordinateClueInfo("Wilderness. Resource Area.", BRASSICAN_MAGE)) .put(new WorldPoint(3304, 3941, 0), new CoordinateClueInfo("Wilderness. East of Rogues' Castle.", ANCIENT_WIZARDS)) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java index d2433db61a..74e71b934d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/hotcold/HotColdLocation.java @@ -165,7 +165,7 @@ public enum HotColdLocation ZEAH_BLASTMINE_NORTH(new WorldPoint(1488, 3881, 0), ZEAH, "Northern part of the Lovakengj blast mine.", BRASSICAN_MAGE), ZEAH_LOVAKITE_FURNACE(new WorldPoint(1507, 3819, 0), ZEAH, "Next to the lovakite furnace in Lovakengj.", ANCIENT_WIZARDS), ZEAH_LOVAKENGJ_MINE(new WorldPoint(1477, 3778, 0), ZEAH, "Next to mithril rock in the Lovakengj mine.", ANCIENT_WIZARDS), - ZEAH_SULPHR_MINE(new WorldPoint(1428, 3869, 0), ZEAH, "Western entrance in the Lovakengj sulphur mine.", BRASSICAN_MAGE), + ZEAH_SULPHR_MINE(new WorldPoint(1428, 3869, 0), ZEAH, "Western entrance in the Lovakengj sulphur mine. Facemask or Slayer Helmet recommended.", BRASSICAN_MAGE), ZEAH_SHAYZIEN_BANK(new WorldPoint(1517, 3603, 0), ZEAH, "South-east of the bank in Shayzien.", ANCIENT_WIZARDS), ZEAH_OVERPASS(new WorldPoint(1467, 3714, 0), ZEAH, "Overpass between Lovakengj and Shayzien.", BRASSICAN_MAGE), ZEAH_LIZARDMAN(new WorldPoint(1490, 3698, 0), ZEAH, "Within Lizardman Canyon, east of the ladder. Requires 5% favour with Shayzien.", ANCIENT_WIZARDS), From 525c64b8dfa239b9bba5e8e7e12ee8684ac130ef Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Feb 2021 11:43:12 -0500 Subject: [PATCH 073/133] farming tracker: don't send notifications for empty crop state --- .../timetracking/farming/FarmingTracker.java | 168 ++++++++++-------- .../farming/FarmingTrackerTest.java | 132 ++++++++++++++ 2 files changed, 221 insertions(+), 79 deletions(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingTrackerTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java index e01c451b8d..4a381220b2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/farming/FarmingTracker.java @@ -24,6 +24,7 @@ */ package net.runelite.client.plugins.timetracking.farming; +import com.google.common.annotations.VisibleForTesting; import com.google.inject.Inject; import com.google.inject.Singleton; import java.time.Instant; @@ -500,14 +501,12 @@ public class FarmingTracker Integer offsetPrecisionMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), TimeTrackingConfig.FARM_TICK_OFFSET_PRECISION, int.class); Integer offsetTimeMins = configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), TimeTrackingConfig.FARM_TICK_OFFSET, int.class); - RuneScapeProfileType profileType = profile.getType(); - for (Map.Entry> tab : farmingWorld.getTabs().entrySet()) { for (FarmingPatch patch : tab.getValue()) { ProfilePatch profilePatch = new ProfilePatch(patch, profile.getKey()); - boolean patchNotified = (wasNotified.get(profilePatch) != null ? wasNotified.get(profilePatch) : false); + boolean patchNotified = wasNotified.getOrDefault(profilePatch, false); String configKey = patch.notifyConfigKey(); boolean shouldNotify = Boolean.TRUE .equals(configManager.getConfiguration(TimeTrackingConfig.CONFIG_GROUP, profile.getKey(), configKey, Boolean.class)); @@ -519,94 +518,105 @@ public class FarmingTracker } int tickRate = prediction.getProduce().getTickrate(); - + if (offsetPrecisionMins == null || offsetTimeMins == null || (offsetPrecisionMins < tickRate && offsetPrecisionMins < 40) || prediction.getProduce() == Produce.WEEDS - || unixNow <= prediction.getDoneEstimate() || patchNotified || prediction.getCropState() == CropState.FILLING) + || unixNow <= prediction.getDoneEstimate() || patchNotified || prediction.getCropState() == CropState.FILLING || prediction.getCropState() == CropState.EMPTY) { continue; } wasNotified.put(profilePatch, true); - if (!firstNotifyCheck && shouldNotify) + if (!firstNotifyCheck && shouldNotify) { - final StringBuilder notificationStringBuilder = new StringBuilder(); - // Same RS account - if (client.getGameState() == GameState.LOGGED_IN && profile.getDisplayName().equals(client.getLocalPlayer().getName())) - { - // Same RS account but different profile type - if (profileType != RuneScapeProfileType.getCurrent(client)) - { - notificationStringBuilder.append("(") - .append(Text.titleCase(profile.getType())) - .append(") "); - } - // Same RS account AND profile falls through here so no bracketed prefix is added - } - else - { - // Different RS account AND profile type - if (profileType != RuneScapeProfileType.getCurrent(client) || client.getGameState() == GameState.LOGIN_SCREEN) - { - //Don't print profile type when logged out if is STANDARD - if (client.getGameState() == GameState.LOGIN_SCREEN && profileType == RuneScapeProfileType.STANDARD) - { - notificationStringBuilder.append("(") - .append(profile.getDisplayName()) - .append(") "); - } - else - { - notificationStringBuilder.append("(") - .append(profile.getDisplayName()) - .append(" - ") - .append(Text.titleCase(profile.getType())) - .append(") "); - } - } - // Different RS account but same profile type - else - { - notificationStringBuilder.append("(") - .append(profile.getDisplayName()) - .append(") "); - } - } - - notificationStringBuilder - .append("Your ") - .append(prediction.getProduce().getName()); - - switch (prediction.getCropState()) - { - case HARVESTABLE: - case GROWING: - if (prediction.getProduce().getName().toLowerCase(Locale.ENGLISH).contains("compost")) - { - notificationStringBuilder.append(" is ready to collect in "); - } - else - { - notificationStringBuilder.append(" is ready to harvest in "); - } - break; - case DISEASED: - notificationStringBuilder.append(" has become diseased in "); - break; - case DEAD: - notificationStringBuilder.append(" has died in "); - break; - } - - notificationStringBuilder.append(patch.getRegion().isDefinite() ? "the " : "") - .append(patch.getRegion().getName()) - .append("."); - - notifier.notify(notificationStringBuilder.toString()); + sendNotification(profile, prediction, patch); } } } } firstNotifyCheck = false; } + + @VisibleForTesting + void sendNotification(RuneScapeProfile profile, PatchPrediction prediction, FarmingPatch patch) + { + final RuneScapeProfileType profileType = profile.getType(); + + final StringBuilder stringBuilder = new StringBuilder(); + // Same RS account + if (client.getGameState() == GameState.LOGGED_IN && profile.getDisplayName().equals(client.getLocalPlayer().getName())) + { + // Same RS account but different profile type + if (profileType != RuneScapeProfileType.getCurrent(client)) + { + stringBuilder.append("(") + .append(Text.titleCase(profile.getType())) + .append(") "); + } + // Same RS account AND profile falls through here so no bracketed prefix is added + } + else + { + // Different RS account AND profile type + if (profileType != RuneScapeProfileType.getCurrent(client) || client.getGameState() == GameState.LOGIN_SCREEN) + { + //Don't print profile type when logged out if is STANDARD + if (client.getGameState() == GameState.LOGIN_SCREEN && profileType == RuneScapeProfileType.STANDARD) + { + stringBuilder.append("(") + .append(profile.getDisplayName()) + .append(") "); + } + else + { + stringBuilder.append("(") + .append(profile.getDisplayName()) + .append(" - ") + .append(Text.titleCase(profile.getType())) + .append(") "); + } + } + // Different RS account but same profile type + else + { + stringBuilder.append("(") + .append(profile.getDisplayName()) + .append(") "); + } + } + + stringBuilder + .append("Your ") + .append(prediction.getProduce().getName()); + + switch (prediction.getCropState()) + { + case HARVESTABLE: + case GROWING: + if (prediction.getProduce().getName().toLowerCase(Locale.ENGLISH).contains("compost")) + { + stringBuilder.append(" is ready to collect in "); + } + else + { + stringBuilder.append(" is ready to harvest in "); + } + break; + case DISEASED: + stringBuilder.append(" has become diseased in "); + break; + case DEAD: + stringBuilder.append(" has died in "); + break; + default: + // EMPTY and FILLING are caught above + throw new IllegalStateException(); + } + + stringBuilder.append(patch.getRegion().isDefinite() ? "the " : "") + .append(patch.getRegion().getName()) + .append("."); + + notifier.notify(stringBuilder.toString()); + } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingTrackerTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingTrackerTest.java new file mode 100644 index 0000000000..f407fd0a34 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timetracking/farming/FarmingTrackerTest.java @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2021, 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.timetracking.farming; + +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.EnumSet; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.Player; +import net.runelite.api.Varbits; +import net.runelite.api.WorldType; +import net.runelite.client.Notifier; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.RuneScapeProfile; +import net.runelite.client.config.RuneScapeProfileType; +import net.runelite.client.game.ItemManager; +import net.runelite.client.plugins.timetracking.TimeTrackingConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; +import org.mockito.junit.MockitoJUnitRunner; + +@RunWith(MockitoJUnitRunner.class) +public class FarmingTrackerTest +{ + @Inject + private FarmingTracker farmingTracker; + + @Mock + @Bind + private Client client; + + @Mock + @Bind + private ItemManager itemManager; + + @Mock + @Bind + private ConfigManager configManager; + + @Mock + @Bind + private TimeTrackingConfig config; + + @Mock + @Bind + private FarmingWorld farmingWorld; + + @Mock + @Bind + private Notifier notifier; + + @Before + public void before() + { + Guice.createInjector(BoundFieldModule.of(this)).injectMembers(this); + + when(client.getGameState()).thenReturn(GameState.LOGGED_IN); + when(client.getWorldType()).thenReturn(EnumSet.noneOf(WorldType.class)); + + Player player = mock(Player.class); + when(player.getName()).thenReturn("Adam"); + when(client.getLocalPlayer()).thenReturn(player); + } + + @Test(expected = IllegalStateException.class) + public void testEmptyNotification() + { + RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null); + + PatchPrediction patchPrediction = new PatchPrediction(Produce.EMPTY_COMPOST_BIN, CropState.EMPTY, 0L, 0, 0); + FarmingRegion region = new FarmingRegion("Ardougne", 10548, false, + new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), + new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), + new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB), + new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) + ); + FarmingPatch patch = region.getPatches()[4]; + patch.setRegion(region); + farmingTracker.sendNotification(runeScapeProfile, patchPrediction, patch); + } + + @Test + public void testHarvestableNotification() + { + RuneScapeProfile runeScapeProfile = new RuneScapeProfile("Adam", RuneScapeProfileType.STANDARD, null, null); + + PatchPrediction patchPrediction = new PatchPrediction(Produce.RANARR, CropState.HARVESTABLE, 0L, 0, 0); + FarmingRegion region = new FarmingRegion("Ardougne", 10548, false, + new FarmingPatch("North", Varbits.FARMING_4771, PatchImplementation.ALLOTMENT), + new FarmingPatch("South", Varbits.FARMING_4772, PatchImplementation.ALLOTMENT), + new FarmingPatch("", Varbits.FARMING_4773, PatchImplementation.FLOWER), + new FarmingPatch("", Varbits.FARMING_4774, PatchImplementation.HERB), + new FarmingPatch("", Varbits.FARMING_4775, PatchImplementation.COMPOST) + ); + FarmingPatch patch = region.getPatches()[3]; + patch.setRegion(region); + farmingTracker.sendNotification(runeScapeProfile, patchPrediction, patch); + + verify(notifier).notify("Your Ranarr is ready to harvest in Ardougne."); + } +} \ No newline at end of file From 0163f79248f5fd063ef081a127ed74ae215d3eb2 Mon Sep 17 00:00:00 2001 From: David Date: Tue, 9 Feb 2021 19:25:23 +0100 Subject: [PATCH 074/133] menu entry swapper: Add battlestaff use swap (#13140) --- .../menuentryswapper/MenuEntrySwapperConfig.java | 11 +++++++++++ .../menuentryswapper/MenuEntrySwapperPlugin.java | 2 ++ 2 files changed, 13 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java index dda54b3c21..63b2e569b5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperConfig.java @@ -144,6 +144,17 @@ public interface MenuEntrySwapperConfig extends Config return false; } + @ConfigItem( + keyName = "swapBattlestaves", + name = "Battlestaff", + description = "Swap Wield with Use on Battlestaves without orbs", + section = itemSection + ) + default boolean swapBattlestaves() + { + return false; + } + @ConfigItem( keyName = "swapPrayerBook", name = "Recite-Prayer", 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 b72c138391..b6cb75aa15 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 @@ -334,6 +334,8 @@ public class MenuEntrySwapperPlugin extends Plugin swap("bury", "use", config::swapBones); + swap("wield", "battlestaff", "use", config::swapBattlestaves); + swap("clean", "use", config::swapHerbs); swap("read", "recite-prayer", config::swapPrayerBook); From c3e96d72f11f48e116233af76d239de264d0faca Mon Sep 17 00:00:00 2001 From: ObsoleteXero <51947152+ObsoleteXero@users.noreply.github.com> Date: Tue, 9 Feb 2021 23:56:22 +0530 Subject: [PATCH 075/133] idle notifier: Add Loom crafting animation (#13154) --- runelite-api/src/main/java/net/runelite/api/AnimationID.java | 1 + .../client/plugins/idlenotifier/IdleNotifierPlugin.java | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) 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 9358aa4a9f..7c4d40e61c 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -97,6 +97,7 @@ public final class AnimationID public static final int CRAFTING_SPINNING = 894; public static final int CRAFTING_POTTERS_WHEEL = 883; public static final int CRAFTING_POTTERY_OVEN = 24975; + public static final int CRAFTING_LOOM = 2270; public static final int SMITHING_SMELTING = 899; public static final int SMITHING_CANNONBALL = 827; //cball smithing uses this and SMITHING_SMELTING public static final int SMITHING_ANVIL = 898; 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 6e3be57ee4..79ca4413e3 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 @@ -147,7 +147,7 @@ public class IdleNotifierPlugin extends Plugin case COOKING_FIRE: case COOKING_RANGE: case COOKING_WINE: - /* Crafting(Gem Cutting, Glassblowing, Spinning, Battlestaves, Pottery) */ + /* Crafting(Gem Cutting, Glassblowing, Spinning, Weaving, Battlestaves, Pottery) */ case GEM_CUTTING_OPAL: case GEM_CUTTING_JADE: case GEM_CUTTING_REDTOPAZ: @@ -158,6 +158,7 @@ public class IdleNotifierPlugin extends Plugin case GEM_CUTTING_AMETHYST: case CRAFTING_GLASSBLOWING: case CRAFTING_SPINNING: + case CRAFTING_LOOM: case CRAFTING_BATTLESTAVES: case CRAFTING_LEATHER: case CRAFTING_POTTERS_WHEEL: From 8d543933c901eb021428643393c0e074e1a76ddb Mon Sep 17 00:00:00 2001 From: Anjaneya Bhardwaj Date: Tue, 9 Feb 2021 14:01:11 -0500 Subject: [PATCH 076/133] fishing: Add minnows flying fish notification (#13126) --- .../client/plugins/fishing/FishingConfig.java | 13 ++++++++++++- .../client/plugins/fishing/FishingPlugin.java | 5 +++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java index e48d80edbd..6e68c4270a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java @@ -150,6 +150,17 @@ public interface FishingConfig extends Config @ConfigItem( position = 10, + keyName = "flyingFishNotification", + name = "Flying fish notification", + description = "Send a notification when a flying fish spawns on your fishing spot." + ) + default boolean flyingFishNotification() + { + return true; + } + + @ConfigItem( + position = 11, keyName = "trawlerNotification", name = "Trawler activity notification", description = "Send a notification when fishing trawler activity drops below 15%." @@ -160,7 +171,7 @@ public interface FishingConfig extends Config } @ConfigItem( - position = 11, + position = 12, keyName = "trawlerTimer", name = "Trawler timer in MM:SS", description = "Trawler Timer will display a more accurate timer in MM:SS format." 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 1311021c29..0c5fa99dc2 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 @@ -215,6 +215,11 @@ public class FishingPlugin extends Plugin spotOverlay.setHidden(false); fishingSpotMinimapOverlay.setHidden(false); } + + if (event.getMessage().equals("A flying fish jumps up and eats some of your minnows!") && config.flyingFishNotification()) + { + notifier.notify("A flying fish is eating your minnows!"); + } } @Subscribe From 7656cafc70682a7801c5e8314adc66423e07ce64 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Feb 2021 13:45:39 -0500 Subject: [PATCH 077/133] chat commands: comma separate values in several commands --- .../plugins/chatcommands/ChatCommandsPlugin.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 8923f1684a..8c06945a66 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 @@ -666,7 +666,7 @@ public class ChatCommandsPlugin extends Plugin .append(ChatColorType.NORMAL) .append(" kill count: ") .append(ChatColorType.HIGHLIGHT) - .append(Integer.toString(kc)) + .append(String.format("%,d", kc)) .build(); log.debug("Setting response {}", response); @@ -748,15 +748,15 @@ public class ChatCommandsPlugin extends Plugin .append(ChatColorType.NORMAL) .append("Duel Arena wins: ") .append(ChatColorType.HIGHLIGHT) - .append(Integer.toString(wins)) + .append(String.format("%,d", wins)) .append(ChatColorType.NORMAL) .append(" losses: ") .append(ChatColorType.HIGHLIGHT) - .append(Integer.toString(losses)) + .append(String.format("%,d", losses)) .append(ChatColorType.NORMAL) .append(" streak: ") .append(ChatColorType.HIGHLIGHT) - .append(Integer.toString((winningStreak != 0 ? winningStreak : -losingStreak))) + .append(String.format("%,d", winningStreak != 0 ? winningStreak : -losingStreak)) .build(); log.debug("Setting response {}", response); @@ -957,7 +957,7 @@ public class ChatCommandsPlugin extends Plugin .append(ChatColorType.NORMAL) .append("Barbarian Assault High-level gambles: ") .append(ChatColorType.HIGHLIGHT) - .append(Integer.toString(gc)) + .append(String.format("%,d", gc)) .build(); log.debug("Setting response {}", response); @@ -1420,7 +1420,7 @@ public class ChatCommandsPlugin extends Plugin .append(ChatColorType.NORMAL) .append("Clue scroll (" + level + ")").append(": ") .append(ChatColorType.HIGHLIGHT) - .append(Integer.toString(quantity)); + .append(String.format("%,d", quantity)); if (rank != -1) { From bfc9af5526861e6c2b21163702329814c42fbf80 Mon Sep 17 00:00:00 2001 From: loldudester Date: Tue, 9 Feb 2021 21:31:16 +0000 Subject: [PATCH 078/133] TimeTracking: Return shortened weekdays in doneEstimates --- .../runelite/client/plugins/timetracking/TabContentPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TabContentPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TabContentPanel.java index dd3a2bba5b..88b6362e26 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TabContentPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TabContentPanel.java @@ -77,7 +77,7 @@ public abstract class TabContentPanel extends JPanel LocalDateTime currentTime = LocalDateTime.now(); if (endTime.getDayOfWeek() != currentTime.getDayOfWeek()) { - sb.append(endTime.getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())).append(" "); + sb.append(endTime.getDayOfWeek().getDisplayName(TextStyle.SHORT, Locale.getDefault())).append(" "); } sb.append("at "); sb.append(formatter.format(endTime)); From faeca5d16380241a907608a6bf505682d2cbb4ed Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Tue, 9 Feb 2021 22:34:38 +0000 Subject: [PATCH 079/133] mixins: fix event --- .../net/runelite/mixins/RSPlayerMixin.java | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java index 72d008feaa..80c4f3fcfd 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSPlayerMixin.java @@ -28,25 +28,13 @@ import java.awt.Polygon; import java.awt.Shape; import java.util.ArrayList; import net.runelite.api.HeadIcon; -import static net.runelite.api.HeadIcon.MAGIC; -import static net.runelite.api.HeadIcon.MELEE; -import static net.runelite.api.HeadIcon.RANGED; -import static net.runelite.api.HeadIcon.REDEMPTION; -import static net.runelite.api.HeadIcon.RETRIBUTION; -import static net.runelite.api.HeadIcon.SMITE; import net.runelite.api.Model; import net.runelite.api.Perspective; import net.runelite.api.SkullIcon; -import static net.runelite.api.SkullIcon.DEAD_MAN_FIVE; -import static net.runelite.api.SkullIcon.DEAD_MAN_FOUR; -import static net.runelite.api.SkullIcon.DEAD_MAN_ONE; -import static net.runelite.api.SkullIcon.DEAD_MAN_THREE; -import static net.runelite.api.SkullIcon.DEAD_MAN_TWO; -import static net.runelite.api.SkullIcon.SKULL; -import static net.runelite.api.SkullIcon.SKULL_FIGHT_PIT; +import static net.runelite.api.SkullIcon.*; import net.runelite.api.coords.LocalPoint; -import net.runelite.api.events.PlayerChanged; import net.runelite.api.events.OverheadPrayerChanged; +import net.runelite.api.events.PlayerChanged; import net.runelite.api.events.PlayerSkullChanged; import net.runelite.api.mixins.Copy; import net.runelite.api.mixins.FieldHook; @@ -71,10 +59,10 @@ public abstract class RSPlayerMixin implements RSPlayer private boolean friended; @Inject - private int oldHeadIcon = -1; + private int oldHeadIcon = -2; @Inject - private int oldSkullIcon = -1; + private int oldSkullIcon = -2; @Inject @Override @@ -101,14 +89,11 @@ public abstract class RSPlayerMixin implements RSPlayer @FieldHook("headIconPrayer") public void prayerChanged(int idx) { - if (!(getRsOverheadIcon() == -1 && oldHeadIcon == -1)) + if (getRsOverheadIcon() != oldHeadIcon) { final HeadIcon headIcon = getHeadIcon(getRsOverheadIcon()); - if (getRsOverheadIcon() != oldHeadIcon) - { - client.getCallbacks().post( - new OverheadPrayerChanged(this, getHeadIcon(oldHeadIcon), headIcon)); - } + client.getCallbacks().post( + new OverheadPrayerChanged(this, getHeadIcon(oldHeadIcon), headIcon)); } oldHeadIcon = getRsOverheadIcon(); } @@ -143,6 +128,11 @@ public abstract class RSPlayerMixin implements RSPlayer @Inject private HeadIcon getHeadIcon(int overheadIcon) { + if (overheadIcon == -1) + { + return null; + } + return HeadIcon.values()[overheadIcon]; } From 8d37c4363de7090a96d080b08f50a9aa739dafe7 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Tue, 9 Feb 2021 22:34:51 +0000 Subject: [PATCH 080/133] orange -> blue --- .../src/main/java/net/runelite/client/ui/ColorScheme.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java b/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java index b66c62d7fd..538a59b1c5 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ColorScheme.java @@ -38,10 +38,10 @@ public class ColorScheme public static final Color BRAND_BLUE_TRANSPARENT = new Color(25, 194, 255, 120); /* The orange color used for the branding's accents */ - public static final Color BRAND_ORANGE = new Color(220, 138, 0); + public static final Color BRAND_ORANGE = BRAND_BLUE; /* The orange color used for the branding's accents, with lowered opacity */ - public static final Color BRAND_ORANGE_TRANSPARENT = new Color(220, 138, 0, 120); + public static final Color BRAND_ORANGE_TRANSPARENT = BRAND_BLUE_TRANSPARENT; public static final Color DARKER_GRAY_COLOR = new Color(30, 30, 30); public static final Color DARK_GRAY_COLOR = new Color(40, 40, 40); From b8d806da76a86eb926b06f6fe25b52bcfe6588fd Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Tue, 9 Feb 2021 22:35:06 +0000 Subject: [PATCH 081/133] fix swingutil compile error --- .../client/plugins/openosrs/externals/PluginsPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java index b345c85c15..6989e2ae19 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java @@ -5,7 +5,6 @@ import com.google.gson.JsonSyntaxException; import com.openosrs.client.events.OPRSPluginChanged; import com.openosrs.client.events.OPRSRepositoryChanged; import net.runelite.client.util.DeferredDocumentChangedListener; -import com.openosrs.client.util.SwingUtil; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Component; @@ -45,6 +44,7 @@ import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.components.IconTextField; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.SwingUtil; import org.pf4j.update.PluginInfo; import org.pf4j.update.UpdateManager; import org.pf4j.update.UpdateRepository; From 1b70cb55fc379bdf143e74d8739f0e845589a786 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Tue, 9 Feb 2021 22:36:10 +0000 Subject: [PATCH 082/133] soundmanager: rework --- .../java/com/openosrs/client/game/Sound.java | 85 ++++++++++++------- .../openosrs/client/game/SoundManager.java | 48 ++++++++--- 2 files changed, 90 insertions(+), 43 deletions(-) diff --git a/runelite-client/src/main/java/com/openosrs/client/game/Sound.java b/runelite-client/src/main/java/com/openosrs/client/game/Sound.java index 24b7588040..27ca3e5111 100644 --- a/runelite-client/src/main/java/com/openosrs/client/game/Sound.java +++ b/runelite-client/src/main/java/com/openosrs/client/game/Sound.java @@ -1,42 +1,67 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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 com.openosrs.client.game; -public enum Sound +import java.io.File; +import java.net.URL; +import lombok.EqualsAndHashCode; +import lombok.SneakyThrows; +import lombok.Value; +import net.runelite.client.RuneLite; + +public abstract class Sound { - FIFTEEN_SECONDS(1, "net/runelite/client/game/sounds/15seconds.wav"), - FIVE_SECONDS(2, "net/runelite/client/game/sounds/5seconds.wav"), - ATTACK_WITH_MAGIC(3, "net/runelite/client/game/sounds/attackmagic.wav"), - ATTACK_WITH_MELEE(4, "net/runelite/client/game/sounds/attackmelee.wav"), - ATTACK_WITH_RANGED(5, "net/runelite/client/game/sounds/attackranged.wav"), - INCOMING(6, "net/runelite/client/game/sounds/incoming.wav"), - MOVE(7, "net/runelite/client/game/sounds/move.wav"), - PRAY_MAGIC(8, "net/runelite/client/game/sounds/praymagic.wav"), - PRAY_MELEE(9, "net/runelite/client/game/sounds/praymelee.wav"), - PRAY_RANGED(10, "net/runelite/client/game/sounds/prayranged.wav"), - REENABLE_PRAYER(11, "net/runelite/client/game/sounds/reenableprayer.wav"), - RUNAWAY(12, "net/runelite/client/game/sounds/runaway.wav"), - LOW_HEATLH(13, "net/runelite/client/game/sounds/lowhealth.wav"), - LOW_PRAYER(14, "net/runelite/client/game/sounds/lowprayer.wav"), - OUT_OF_COMBAT(15, "net/runelite/client/game/sounds/outofcombat.wav"), - RESTORED_SPECIAL_ATTACK(16, "net/runelite/client/game/sounds/restorespec.wav"), - IDLE(17, "net/runelite/client/game/sounds/idle.wav"), - BREAK(18, "net/runelite/client/game/sounds/break.wav"); + public abstract URL getPath(); - private final String filePath; - private final int id; + // TODO: these inner classes should probably be removed - Sound(int id, String filePath) + @Value + @EqualsAndHashCode(callSuper = true) // stop the warning + static class SoundJarResource extends Sound { - this.id = id; - this.filePath = filePath; + String path; + + @Override + public URL getPath() + { + return RuneLite.class.getResource(path); + } } - public String getFilePath() + @Value + @EqualsAndHashCode(callSuper = true) // stop the warning + static class SoundFileResource extends Sound { - return this.filePath; - } + File file; - public int getId() - { - return this.id; + @Override + @SneakyThrows + public URL getPath() + { + return file.toURI().toURL(); + } } } \ No newline at end of file diff --git a/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java b/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java index e6e7489570..73df208095 100644 --- a/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java +++ b/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java @@ -1,3 +1,28 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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 com.openosrs.client.game; import com.google.inject.Inject; @@ -21,17 +46,14 @@ public class SoundManager { public void play(final Sound sound) { - new Thread(new Runnable() + new Thread(() -> { - @Override - public void run() + try (AudioInputStream in = AudioSystem.getAudioInputStream(sound.getPath())) { - try + AudioFormat outFormat = SoundManager.this.getOutFormat(in.getFormat()); + DataLine.Info info = new DataLine.Info(SourceDataLine.class, outFormat); + try (SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info)) { - AudioInputStream in = AudioSystem.getAudioInputStream(this.getClass().getClassLoader().getResource(sound.getFilePath())); - AudioFormat outFormat = SoundManager.this.getOutFormat(in.getFormat()); - DataLine.Info info = new DataLine.Info(SourceDataLine.class, outFormat); - SourceDataLine line = (SourceDataLine) AudioSystem.getLine(info); if (line != null) { line.open(outFormat, 2200); @@ -44,15 +66,15 @@ public class SoundManager gainControl.setValue((float) (Math.log((double) volume / 100.0) / Math.log(10.0) * 20.0)); } line.start(); - SoundManager.this.stream(AudioSystem.getAudioInputStream(outFormat, in), line); + stream(AudioSystem.getAudioInputStream(outFormat, in), line); line.drain(); line.stop(); } } - catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) - { - throw new IllegalStateException(e); - } + } + catch (IOException | LineUnavailableException | UnsupportedAudioFileException e) + { + throw new IllegalStateException(e); } }).start(); } From 4adef168beaa39be779762ed916a5fa40aa929f5 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Tue, 9 Feb 2021 22:36:25 +0000 Subject: [PATCH 083/133] worldlocation: fix compile error --- .../src/main/java/com/openosrs/client/game/WorldLocation.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/com/openosrs/client/game/WorldLocation.java b/runelite-client/src/main/java/com/openosrs/client/game/WorldLocation.java index ce347a1b26..0782f79406 100644 --- a/runelite-client/src/main/java/com/openosrs/client/game/WorldLocation.java +++ b/runelite-client/src/main/java/com/openosrs/client/game/WorldLocation.java @@ -33,9 +33,9 @@ import java.util.stream.Collectors; import lombok.Getter; import net.runelite.api.coords.WorldArea; import net.runelite.api.coords.WorldPoint; -import com.openosrs.client.util.PvPUtil; +import net.runelite.client.util.PvPUtil; - /* +/* * Enums sorted alphabetically by main regions (Kingdoms) and then their sub-regions or notable features * Example: * Wilderness From b4ba3ccd86939bbbe757ac3cd7923fcfdd51f98a Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Tue, 9 Feb 2021 23:32:56 +0000 Subject: [PATCH 084/133] fix plugin directories --- runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java | 2 +- runelite-client/src/main/java/net/runelite/client/RuneLite.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java b/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java index 703803a93f..8430967ed5 100644 --- a/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java +++ b/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java @@ -8,7 +8,7 @@ import java.util.UUID; public class OpenOSRS { public static final File OPENOSRS_DIR = new File(System.getProperty("user.home"), ".openosrs"); - public static final File EXTERNALPLUGIN_DIR = new File(OPENOSRS_DIR, "plugins-oprs"); + public static final File EXTERNALPLUGIN_DIR = new File(OPENOSRS_DIR, "plugins"); public static final String SYSTEM_VERSION; public static String uuid = UUID.randomUUID().toString(); 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 9dd8b019c4..23376ad4f0 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -96,7 +96,7 @@ public class RuneLite { public static final File RUNELITE_DIR = new File(System.getProperty("user.home"), ".openosrs"); public static final File CACHE_DIR = new File(RUNELITE_DIR, "cache"); - public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugins"); + public static final File PLUGINS_DIR = new File(RUNELITE_DIR, "plugin-hub"); public static final File PROFILES_DIR = new File(RUNELITE_DIR, "profiles"); public static final File SCREENSHOT_DIR = new File(RUNELITE_DIR, "screenshots"); public static final File LOGS_DIR = new File(RUNELITE_DIR, "logs"); From 1b4cd55579482a17feef9af5f157416ab0d837ef Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Tue, 9 Feb 2021 23:33:20 +0000 Subject: [PATCH 085/133] remove c.o.c.u.overlayutil - unused --- .../client/ui/overlay/OverlayUtil.java | 174 ------------------ 1 file changed, 174 deletions(-) delete mode 100644 runelite-client/src/main/java/com/openosrs/client/ui/overlay/OverlayUtil.java diff --git a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/OverlayUtil.java b/runelite-client/src/main/java/com/openosrs/client/ui/overlay/OverlayUtil.java deleted file mode 100644 index 47a6d2e716..0000000000 --- a/runelite-client/src/main/java/com/openosrs/client/ui/overlay/OverlayUtil.java +++ /dev/null @@ -1,174 +0,0 @@ -package com.openosrs.client.ui.overlay; - - -import java.awt.BasicStroke; -import java.awt.Color; -import java.awt.Font; -import java.awt.Graphics2D; -import java.awt.Polygon; -import java.awt.Rectangle; -import java.awt.Shape; -import java.awt.Stroke; -import java.awt.image.BufferedImage; -import java.util.List; -import net.runelite.api.*; -import net.runelite.api.Point; -import net.runelite.api.coords.LocalPoint; -import net.runelite.api.coords.WorldArea; -import net.runelite.api.coords.WorldPoint; -import net.runelite.api.vars.InterfaceTab; -import net.runelite.api.widgets.Widget; - - -public class OverlayUtil extends net.runelite.client.ui.overlay.OverlayUtil -{ - public static void drawTiles(Graphics2D graphics, Client client, WorldPoint point, WorldPoint playerPoint, Color color, int strokeWidth, int outlineAlpha, int fillAlpha) - { - if (point.distanceTo(playerPoint) >= 32) - { - return; - } - LocalPoint lp = LocalPoint.fromWorld(client, point); - if (lp == null) - { - return; - } - - Polygon poly = Perspective.getCanvasTilePoly(client, lp); - if (poly == null) - { - return; - } - drawStrokeAndFillPoly(graphics, color, strokeWidth, outlineAlpha, fillAlpha, poly); - } - - public static void drawStrokeAndFillPoly(Graphics2D graphics, Color color, int strokeWidth, int outlineAlpha, int fillAlpha, Polygon poly) - { - graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), outlineAlpha)); - graphics.setStroke(new BasicStroke(strokeWidth)); - graphics.draw(poly); - graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), fillAlpha)); - graphics.fill(poly); - } - - public static Rectangle renderPrayerOverlay(Graphics2D graphics, Client client, Prayer prayer, Color color) - { - Widget widget = client.getWidget(prayer.getWidgetInfo()); - - if (widget == null || client.getVar(VarClientInt.INVENTORY_TAB) != InterfaceTab.PRAYER.getId()) - { - return null; - } - - Rectangle bounds = widget.getBounds(); - renderPolygon(graphics, rectangleToPolygon(bounds), color); - return bounds; - } - - private static Polygon rectangleToPolygon(Rectangle rect) - { - int[] xpoints = {rect.x, rect.x + rect.width, rect.x + rect.width, rect.x}; - int[] ypoints = {rect.y, rect.y, rect.y + rect.height, rect.y + rect.height}; - - return new Polygon(xpoints, ypoints, 4); - } - - public static void renderTextLocation(Graphics2D graphics, String txtString, int fontSize, int fontStyle, Color fontColor, Point canvasPoint, boolean shadows, int yOffset) - { - graphics.setFont(new Font("Arial", fontStyle, fontSize)); - if (canvasPoint != null) - { - final Point canvasCenterPoint = new Point( - canvasPoint.getX(), - canvasPoint.getY() + yOffset); - final Point canvasCenterPoint_shadow = new Point( - canvasPoint.getX() + 1, - canvasPoint.getY() + 1 + yOffset); - if (shadows) - { - renderTextLocation(graphics, canvasCenterPoint_shadow, txtString, Color.BLACK); - } - renderTextLocation(graphics, canvasCenterPoint, txtString, fontColor); - } - } - - public static void setProgressIcon(Graphics2D graphics, Point point, BufferedImage currentPhaseIcon, int totalWidth, int bgPadding, int currentPosX, Color colorIconBackground, int overlayIconDistance, Color colorIconBorder, Color colorIconBorderFill) - { - graphics.setStroke(new BasicStroke(2)); - graphics.setColor(colorIconBackground); - graphics.fillOval( - point.getX() - totalWidth / 2 + currentPosX - bgPadding, - point.getY() - currentPhaseIcon.getHeight() / 2 - overlayIconDistance - bgPadding, - currentPhaseIcon.getWidth() + bgPadding * 2, - currentPhaseIcon.getHeight() + bgPadding * 2); - - graphics.setColor(colorIconBorder); - graphics.drawOval( - point.getX() - totalWidth / 2 + currentPosX - bgPadding, - point.getY() - currentPhaseIcon.getHeight() / 2 - overlayIconDistance - bgPadding, - currentPhaseIcon.getWidth() + bgPadding * 2, - currentPhaseIcon.getHeight() + bgPadding * 2); - - graphics.drawImage( - currentPhaseIcon, - point.getX() - totalWidth / 2 + currentPosX, - point.getY() - currentPhaseIcon.getHeight() / 2 - overlayIconDistance, - null); - - graphics.setColor(colorIconBorderFill); - } - - public static List getHitSquares(WorldPoint npcLoc, int npcSize, int thickness, boolean includeUnder) - { - List little = new WorldArea(npcLoc, npcSize, npcSize).toWorldPointList(); - List big = new WorldArea(npcLoc.getX() - thickness, npcLoc.getY() - thickness, npcSize + (thickness * 2), npcSize + (thickness * 2), npcLoc.getPlane()).toWorldPointList(); - if (!includeUnder) - { - big.removeIf(little::contains); - } - return big; - } - - public static void renderFilledPolygon(Graphics2D graphics, Shape poly, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(2)); - graphics.draw(poly); - graphics.fill(poly); - graphics.setStroke(originalStroke); - } - - public static void renderAreaTilePolygon(Graphics2D graphics, Shape poly, Color color) - { - graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 10)); - graphics.fill(poly); - } - - public static void renderFullLine(Graphics2D graphics, int[][] line, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(2)); - graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]); - graphics.setStroke(originalStroke); - } - - public static void renderDashedLine(Graphics2D graphics, int[][] line, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0)); - graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]); - graphics.setStroke(originalStroke); - } - - public static void renderOutlinePolygon(Graphics2D graphics, Shape poly, Color color) - { - graphics.setColor(color); - final Stroke originalStroke = graphics.getStroke(); - graphics.setStroke(new BasicStroke(2)); - graphics.draw(poly); - graphics.setStroke(originalStroke); - } -} From 80e5d442432fe220acefeb485842e0831c35774a Mon Sep 17 00:00:00 2001 From: Nick Wolff Date: Tue, 9 Feb 2021 21:50:35 -0800 Subject: [PATCH 086/133] emoji: Add weary face emoji --- .../net/runelite/client/plugins/emojis/Emoji.java | 1 + .../runelite/client/plugins/emojis/weary_face.png | Bin 0 -> 339 bytes 2 files changed, 1 insertion(+) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/emojis/weary_face.png diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/Emoji.java b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/Emoji.java index 97e94e40bf..8eb163ba53 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/Emoji.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/Emoji.java @@ -92,6 +92,7 @@ enum Emoji PLEADING("(n_n)"), XD("Xd"), SPOON("--o"), + WEARY_FACE("Dx"), ; private static final Map emojiMap; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/emojis/weary_face.png b/runelite-client/src/main/resources/net/runelite/client/plugins/emojis/weary_face.png new file mode 100644 index 0000000000000000000000000000000000000000..f8b113ea2d7d09c6b2bce31ae2abf2a0b7a8adc4 GIT binary patch literal 339 zcmV-Z0j&OsP)@W)t48Jm$7QnQ|Jqjw!j1D%-3(_{K{0#7O$bO1X|I`^ZbZk}R=@ zCV*TG>A6CJUJkK{CcBU;`^ihwrZ=E?9o(%u+pRmHc^=oPIq|?o;j%vS!AF>K9g}=Z zqmO3w!$^&7C(XmG)y%ol$+f<=>Px# z0(4SNQvd*Vlg&5)003u6L_t&t9R zW^0KKY=3R&jIZ Date: Tue, 9 Feb 2021 14:25:46 +0000 Subject: [PATCH 087/133] ground markers: show existing label when labeling a marker --- .../groundmarkers/GroundMarkerPlugin.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) 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 cfb6171941..81ac210bfc 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,6 +34,7 @@ import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.Optional; import java.util.stream.Collectors; import javax.inject.Inject; import lombok.AccessLevel; @@ -313,21 +314,22 @@ public class GroundMarkerPlugin extends Plugin WorldPoint worldPoint = WorldPoint.fromLocalInstance(client, localPoint); final int regionId = worldPoint.getRegionID(); + GroundMarkerPoint searchPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), null, null); + Collection points = getPoints(regionId); + GroundMarkerPoint existing = points.stream() + .filter(p -> p.equals(searchPoint)) + .findFirst().orElse(null); + if (existing == null) + { + return; + } + chatboxPanelManager.openTextInput("Tile label") + .value(Optional.ofNullable(existing.getLabel()).orElse("")) .onDone((input) -> { input = Strings.emptyToNull(input); - GroundMarkerPoint searchPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), null, null); - Collection points = getPoints(regionId); - GroundMarkerPoint existing = points.stream() - .filter(p -> p.equals(searchPoint)) - .findFirst().orElse(null); - if (existing == null) - { - return; - } - GroundMarkerPoint newPoint = new GroundMarkerPoint(regionId, worldPoint.getRegionX(), worldPoint.getRegionY(), client.getPlane(), existing.getColor(), input); points.remove(searchPoint); points.add(newPoint); From 38ff2446d7edb4e974fd84e4b568c18f5069896a Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Wed, 10 Feb 2021 09:45:11 +0000 Subject: [PATCH 088/133] remove more unused things --- .../java/com/openosrs/api/AnimationID.java | 239 -------------- .../main/java/com/openosrs/api/Varbits.java | 312 ------------------ 2 files changed, 551 deletions(-) delete mode 100644 runelite-api/src/main/java/com/openosrs/api/AnimationID.java delete mode 100644 runelite-api/src/main/java/com/openosrs/api/Varbits.java diff --git a/runelite-api/src/main/java/com/openosrs/api/AnimationID.java b/runelite-api/src/main/java/com/openosrs/api/AnimationID.java deleted file mode 100644 index 8a3de35994..0000000000 --- a/runelite-api/src/main/java/com/openosrs/api/AnimationID.java +++ /dev/null @@ -1,239 +0,0 @@ -package com.openosrs.api; - -//This is where Animation IDs should go that aren't found in RuneLite -public class AnimationID extends net.runelite.api.AnimationID -{ - - // NPC animations - public static final int TZTOK_JAD_MAGIC_ATTACK = 2656; - public static final int TZTOK_JAD_RANGE_ATTACK = 2652; - public static final int HELLHOUND_DEFENCE = 6566; - - public static final int FISHING_TRAILBLAZER_HARPOON_2 = 8785; - - public static final int CRYSTALLINE_RAT_DEATH = 8334; - public static final int CRYSTALLINE_BAT_DEATH = 4917; - public static final int CRYSTALLINE_WOLF_DEATH = 8335; - public static final int CRYSTALLINE_SPIDER_DEATH = 8338; - public static final int CRYSTALLINE_UNICORN_DEATH = 6377; - public static final int CRYSTALLINE_DRAGON_DEATH = 92; - public static final int CRYSTALLINE_BEAR_DEATH = 4929; - public static final int CRYSTALLINE_DARK_BEAST_DEATH = 2733; - public static final int CORRUPTED_SCORPION_DEATH = 6256; - - public static final int TABLET_TELEPORT = 4069; - - public static final int THIEVING_STALL = 832; - public static final int PICKPOCKET_SUCCESS = 881; - public static final int PULL_LEVER = 2140; - public static final int STANDARD_PURPLE_TELEPORT = 714; - public static final int ECTOPHIAL_TELEPORT = 878; - public static final int FAIRY_RING_TELEPORT = 3265; - public static final int SCROLL_TELEPORT = 3864; - public static final int XERICS_TALISMAN_TELEPORT = 3865; - public static final int WILDERNESS_OBELISK_TELEPORT = 3945; - public static final int SEED_POD_TELEPORT = 4544; - - //block animations for players and perhaps npcs as well? - public static final int BLOCK_DEFENDER = 4177; - public static final int BLOCK_NO_SHIELD = 420; - public static final int BLOCK_SHIELD = 1156; - public static final int BLOCK_SWORD = 388; - public static final int BLOCK_UNARMED = 424; // Same Animation as failed pickpocked - - public static final int NIGHTMARE_DEATH = 8612; - public static final int LOW_LEVEL_MAGIC_ATTACK = 1162; - public static final int HIGH_LEVEL_MAGIC_ATTACK = 1167; - public static final int BLOWPIPE_ATTACK = 5061; - - // NPC animations - public static final int BLACKJACK_KO = 838; - - // Fight Caves - public static final int TZTOK_JAD_MELEE_ATTACK = 2655; - public static final int TOK_XIL_RANGE_ATTACK = 2633; - public static final int TOK_XIL_MELEE_ATTACK = 2628; - public static final int KET_ZEK_MELEE_ATTACK = 2644; - public static final int KET_ZEK_MAGE_ATTACK = 2647; - public static final int MEJ_KOT_MELEE_ATTACK = 2637; - public static final int MEJ_KOT_HEAL_ATTACK = 2639; - - // Vorkath - public static final int VORKATH_WAKE_UP = 7950; - public static final int VORKATH_DEATH = 7949; - public static final int VORKATH_SLASH_ATTACK = 7951; - public static final int VORKATH_ATTACK = 7952; - public static final int VORKATH_FIRE_BOMB_OR_SPAWN_ATTACK = 7960; - public static final int VORKATH_ACID_ATTACK = 7957; - - // Tekton - public static final int TEKTON_ANVIL = 7475; - public static final int TEKTON_AUTO1 = 7482; - public static final int TEKTON_AUTO2 = 7483; - public static final int TEKTON_AUTO3 = 7484; - public static final int TEKTON_FAST_AUTO1 = 7478; - public static final int TEKTON_FAST_AUTO2 = 7488; - public static final int TEKTON_ENRAGE_AUTO1 = 7492; - public static final int TEKTON_ENRAGE_AUTO2 = 7493; - public static final int TEKTON_ENRAGE_AUTO3 = 7494; - - // Hydra - public static final int HYDRA_WALKING = 8232; - public static final int HYDRA_IDLE = 8233; - public static final int HYDRA_POISON_1 = 8234; - public static final int HYDRA_RANGED_1 = 8235; - public static final int HYDRA_MAGIC_1 = 8236; - public static final int HYDRA_1_1 = 8237; - public static final int HYDRA_1_2 = 8238; - public static final int HYDRA_LIGHTNING = 8241; - public static final int HYDRA_RANGED_2 = 8242; - public static final int HYDRA_MAGIC_2 = 8243; - public static final int HYDRA_2_1 = 8244; - public static final int HYDRA_2_2 = 8245; - public static final int HYDRA_FIRE = 8248; - public static final int HYDRA_RANGED_3 = 8249; - public static final int HYDRA_MAGIC_3 = 8250; - public static final int HYDRA_3_1 = 8251; - public static final int HYDRA_3_2 = 8252; - public static final int HYDRA_MAGIC_4 = 8254; - public static final int HYDRA_POISON_4 = 8254; - public static final int HYDRA_RANGED_4 = 8255; - public static final int HYDRA_RANGED_OR_POISON_ATTACK = 8256; - public static final int HYDRA_4_1 = 8257; - public static final int HYDRA_4_2 = 8258; - - // Inferno animations - public static final int JAL_NIB = 7574; - public static final int JAL_MEJRAH = 7578; - public static final int JAL_MEJRAH_STAND = 7577; - public static final int JAL_AK_RANGE_ATTACK = 7581; - public static final int JAL_AK_MELEE_ATTACK = 7582; - public static final int JAL_AK_MAGIC_ATTACK = 7583; - public static final int JAL_IMKOT = 7597; - public static final int JAL_XIL_MELEE_ATTACK = 7604; - public static final int JAL_XIL_RANGE_ATTACK = 7605; - public static final int JAL_ZEK_MAGE_ATTACK = 7610; - public static final int JAL_ZEK_MELEE_ATTACK = 7612; - public static final int JALTOK_JAD_MELEE_ATTACK = 7590; - public static final int JALTOK_JAD_MAGE_ATTACK = 7592; - public static final int JALTOK_JAD_RANGE_ATTACK = 7593; - public static final int TZKAL_ZUK = 7566; - public static final int JAL_MEJJAK = 2858; - - // General Graardor - public static final int MINION_AUTO1 = 6154; - public static final int MINION_AUTO2 = 6156; - public static final int MINION_AUTO3 = 7071; - public static final int MINION_AUTO4 = 7073; - public static final int GENERAL_AUTO1 = 7018; - public static final int GENERAL_AUTO2 = 7020; - public static final int GENERAL_AUTO3 = 7021; - - // Kr'il Tsutsaroth - public static final int ZAMMY_GENERIC_AUTO = 64; - public static final int KRIL_AUTO = 6948; - public static final int KRIL_SPEC = 6950; - public static final int ZAKL_AUTO = 7077; - public static final int BALFRUG_AUTO = 4630; - - // Commander Zilyana - public static final int ZILYANA_MELEE_AUTO = 6964; - public static final int ZILYANA_AUTO = 6967; - public static final int ZILYANA_SPEC = 6970; - public static final int STARLIGHT_AUTO = 6376; - public static final int BREE_AUTO = 7026; - public static final int GROWLER_AUTO = 7037; - - // Kree'arra - public static final int KREE_RANGED = 6978; - public static final int SKREE_AUTO = 6955; - public static final int GEERIN_AUTO = 6956; - public static final int GEERIN_FLINCH = 6958; - public static final int KILISA_AUTO = 6957; - - // Vetion - public static final int VETION_EARTHQUAKE = 5507; - - // Zulrah - public static final int ZULRAH_DEATH = 5804; - public static final int ZULRAH_PHASE = 5072; - - //Dagannoth Kings - public static final int DAG_REX = 2853; - public static final int DAG_PRIME = 2854; - public static final int DAG_SUPREME = 2855; - - // Lizardman shaman - public static final int LIZARDMAN_SHAMAN_SPAWN = 7157; - public static final int LIZARDMAN_SHAMAN_SPAWN_EXPLOSION = 7159; - - // Cerberus - public static final int CERBERUS_MAGIC_ATTACK = 4489; - public static final int CERBERUS_RANGED_ATTACK = 4490; - public static final int CERBERUS_MELEE_ATTACK = 4491; - public static final int CERBERUS_LAVA_ATTACK = 4493; - public static final int CERBERUS_SUMMON_GHOSTS = 4494; - - // Gauntlet Hunleff - public static final int HUNLEFF_TRAMPLE = 8420; - public static final int HUNLEFF_ATTACK = 8419; - public static final int HUNLEFF_TORNADO = 8418; - public static final int HUNLLEF_SWITCH_TO_MAGIC = 8754; - public static final int HUNLLEF_SWITCH_TO_RANGED = 8755; - - //Zalcano - public static final int ZALCANO_KNOCKED_DOWN = 8437; - public static final int ZALCANO_WAKEUP = 8439; - public static final int ZALCANO_ROCK_GLOWING = 8448; - - // Theatre of Blood - Sugadinti Maiden - public static final int SUGADINTI_MAIDEN_BLOOD_SPLAT_ATTACK = 8091; - public static final int SUGADINTI_MAIDEN_MAGIC_ATTACK = 8092; - public static final int SUGADINTI_MAIDEN_DEATH = 8094; - - // Theatre of Blood - Pestilent Bloat - public static final int BLOAT_SLEEP = 8082; - - // Theatre of Blood - Sotetseg - public static final int SOTETSEG_MELEE_ATTACK = 8138; - public static final int SOTETSEG_REGULAR_PROJECTILE_ATTACK = 8139; - - // Theatre of Blood - Verzik Vitur - public static final int VERZIK_PHASE_1_MAGIC_ATTACK = 8109; - public static final int VERZIK_PHASE_1_MAGIC_ATTACK_CHANNEL = 8110; - public static final int VERZIK_CHANGE_TO_PHASE_2 = 8111; - public static final int VERZIK_PHASE_2_MAGIC_ATTACK = 8114; - public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_1 = 8116; - public static final int VERZIK_PHASE_2_HEALING_CHANNEL = 8117; - public static final int VERZIK_PHASE_2_BELLY_FLOP_ATTACK_2 = 8118; - public static final int VERZIK_CHANGE_TO_PHASE_3 = 8119; - public static final int VERZIK_PHASE_3_MELEE_ATTACK = 8123; - public static final int VERZIK_PHASE_3_MAGIC_ATTACK = 8124; - public static final int VERZIK_PHASE_3_RANGED_ATTACK = 8125; - public static final int VERZIK_PHASE_3_GREEN_POOL_ATTACK = 8126; - public static final int VERZIK_PHASE_3_WEB_ATTACK = 8127; - public static final int VERZIK_DEATH_1 = 8128; - public static final int VERZIK_DEATH_2 = 8129; - - // The Nightmare of Ashihama - public static final int NIGHTMARE_SPAWN_SLEEPWALKERS = 8572; - public static final int NIGHTMARE_FLOATY = 8592; - public static final int NIGHTMARE_WALKING = 8592; - public static final int NIGHTMARE_IDLE = 8593; - public static final int NIGHTMARE_MELEE_ATTACK = 8594; - public static final int NIGHTMARE_MAGIC_ATTACK = 8595; - public static final int NIGHTMARE_RANGED_ATTACK = 8596; - public static final int NIGHTMARE_SURGE_ATTACK = 8597; - public static final int NIGHTMARE_GHOST_AOE_ATTACK = 8598; - public static final int NIGHTMARE_CURSE_PRAYERS_ATTACK = 8599; - public static final int NIGHTMARE_SPAWN_INFECTIOUS_SPORES = 8600; - public static final int NIGHTMARE_SPAWN_ROOM_SECTION_FLOWERS = 8601; - public static final int NIGHTMARE_CHANNEL_DEVASTATING_ATTACK = 8604; - public static final int NIGHTMARE_SWITCH_TO_DEVIL_PHASE = 8605; - public static final int NIGHTMARE_PARASITE_ATTACK = 8606; - public static final int NIGHTMARE_JUMP_DOWN = 8607; - public static final int NIGHTMARE_SINK_DOWN = 8608; - public static final int NIGHTMARE_JUMP_UP = 8609; - public static final int NIGHTMARE_JUMP_UP_2 = 8610; - public static final int NIGHTMARE_WAKE_UP = 8611; -} diff --git a/runelite-api/src/main/java/com/openosrs/api/Varbits.java b/runelite-api/src/main/java/com/openosrs/api/Varbits.java deleted file mode 100644 index bc98952741..0000000000 --- a/runelite-api/src/main/java/com/openosrs/api/Varbits.java +++ /dev/null @@ -1,312 +0,0 @@ -package com.openosrs.api; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -@Getter -public enum Varbits -{ - /* - * Kharedst's Memoirs Teleport Item - */ - KHAREDSTS_MEMOIRS_CHARGES(6035), - - /** - * Grand Exchange - */ - GRAND_EXCHANGE_PRICE_PER_ITEM(4398), - - - /** - * Locked Prayers - * 0-7 = Locked - * 8 = Unlocked - */ - CHIVPIETY_UNLOCKED(3909), - - /** - * Locked Prayers - * 0 = Locked - * 1 = Unlocked - */ - - RIGOUR_UNLOCKED(5451), - AUGURY_UNLOCKED(5452), - PRESERVE_UNLOCKED(5453), - - /** - * Theatre of Blood 1=In Party, 2=Inside/Spectator, 3=Dead Spectating - */ - THEATRE_OF_BLOOD(6440), - BLOAT_DOOR(6447), - - /** - * Theatre of Blood orb varbits each number stands for the player's health on a scale of 1-27 (I think), 0 hides the orb - */ - THEATRE_OF_BLOOD_ORB_1(6442), - THEATRE_OF_BLOOD_ORB_2(6443), - THEATRE_OF_BLOOD_ORB_3(6444), - THEATRE_OF_BLOOD_ORB_4(6445), - THEATRE_OF_BLOOD_ORB_5(6446), - - /** - * Nightmare Zone - */ - NMZ_ABSORPTION(3956), - NMZ_POINTS(3949), - NMZ_OVERLOAD(3955), - - /** - * Pyramid plunder - */ - PYRAMID_PLUNDER_SARCO_OPEN(2362), - PYRAMID_PLUNDER_CHEST_OPEN(2363), - PYRAMID_PLUNDER_ROOM_LOCATION(2365), - PYRAMID_PLUNDER_TIMER(2375), - PYRAMID_PLUNDER_THIEVING_LEVEL(2376), - PYRAMID_PLUNDER_ROOM(2377), - - /** - * In the Wilderness - */ - IN_THE_WILDERNESS(5963), - - /** - * Kingdom Management - */ - KINGDOM_FAVOR(72), - KINGDOM_COFFER(74), - KINGDOM_WORKERS_WOOD(81), - KINGDOM_WORKERS_HERBS(82), - KINGDOM_WORKERS_FISHING(83), - KINGDOM_WORKERS_MINING(84), - KINGDOM_WORKERS_FISH_COOKED_BUTTON(135), // 0 - Raw, 1 - Cooked - KINGDOM_WORKERS_HARDWOOD(2131), - KINGDOM_WORKERS_FARM(2132), - KINGDOM_WORKERS_HARDWOOD_BUTTON(2133), // 0 - Mahogany, 1 - Teak, 2 - Both - KINGDOM_WORKERS_HERBS_BUTTON(2134), // 0 - Herbs, 1 - Flax - - /** - * Varbit used for Slayer reward points - */ - SLAYER_REWARD_POINTS(4068), - - /** - * 0 = standard - * 1 = ancients - * 2 = lunars - * 3 = arrceus - **/ - SPELLBOOK(4070), - - /** - * Bank settings/flags - **/ - BANK_NOTE_FLAG(3958), - - - /** - * Spells being auto-casted - */ - AUTO_CAST_SPELL(276), - - /** - * Temple Trekking - */ - TREK_POINTS(1955), - TREK_STARTED(1956), - TREK_EVENT(1958), - TREK_STATUS(6719), - BLOAT_ENTERED_ROOM(6447), - - /** - * f2p Quest varbits, these don't hold the completion value. - */ - QUEST_DEMON_SLAYER(2561), - QUEST_GOBLIN_DIPLOMACY(2378), - QUEST_MISTHALIN_MYSTERY(3468), - QUEST_THE_CORSAIR_CURSE(6071), - QUEST_X_MARKS_THE_SPOT(8063), - QUEST_ERNEST_LEVER_A(1788), - QUEST_ERNEST_LEVER_B(1789), - QUEST_ERNEST_LEVER_C(1790), - QUEST_ERNEST_LEVER_D(1791), - QUEST_ERNEST_LEVER_E(1792), - QUEST_ERNEST_LEVER_F(1793), - - /** - * member Quest varbits, these don't hold the completion value. - */ - QUEST_ANIMAL_MAGNETISM(3185), - QUEST_BETWEEN_A_ROCK(299), - QUEST_CONTACT(3274), - QUEST_ZOGRE_FLESH_EATERS(487), - QUEST_DARKNESS_OF_HALLOWVALE(2573), - QUEST_DEATH_TO_THE_DORGESHUUN(2258), - QUEST_DESERT_TREASURE(358), - QUEST_DEVIOUS_MINDS(1465), - QUEST_EAGLES_PEAK(2780), - QUEST_ELEMENTAL_WORKSHOP_II(2639), - QUEST_ENAKHRAS_LAMENT(1560), - QUEST_ENLIGHTENED_JOURNEY(2866), - QUEST_THE_EYES_OF_GLOUPHRIE(2497), - QUEST_FAIRYTALE_I_GROWING_PAINS(1803), - QUEST_FAIRYTALE_II_CURE_A_QUEEN(2326), - QUEST_THE_FEUD(334), // 14 = able to pickpocket - QUEST_FORGETTABLE_TALE(822), - QUEST_GARDEN_OF_TRANQUILLITY(961), - QUEST_GHOSTS_AHOY(217), - QUEST_THE_GIANT_DWARF(571), - QUEST_THE_GOLEM(346), - QUEST_HORROR_FROM_THE_DEEP(34), - QUEST_ICTHLARINS_LITTLE_HELPER(418), - QUEST_IN_AID_OF_THE_MYREQUE(1990), - QUEST_THE_LOST_TRIBE(532), - QUEST_LUNAR_DIPLOMACY(2448), - QUEST_MAKING_HISTORY(1383), - QUEST_MOUNTAIN_DAUGHTER(260), - QUEST_MOURNINGS_END_PART_II(1103), - QUEST_MY_ARMS_BIG_ADVENTURE(2790), - QUEST_RATCATCHERS(1404), - QUEST_RECIPE_FOR_DISASTER(1850), - QUEST_RECRUITMENT_DRIVE(657), - QUEST_ROYAL_TROUBLE(2140), - QUEST_THE_SLUG_MENACE(2610), - QUEST_SHADOW_OF_THE_STORM(1372), - QUEST_A_SOULS_BANE(2011), - QUEST_SPIRITS_OF_THE_ELID(1444), - QUEST_SWAN_SONG(2098), - QUEST_A_TAIL_OF_TWO_CATS(1028), - QUEST_TEARS_OF_GUTHIX(451), - QUEST_WANTED(1051), - QUEST_COLD_WAR(3293), - QUEST_THE_FREMENNIK_ISLES(3311), - QUEST_TOWER_OF_LIFE(3337), - QUEST_WHAT_LIES_BELOW(3523), - QUEST_OLAFS_QUEST(3534), - QUEST_ANOTHER_SLICE_OF_HAM(3550), - QUEST_DREAM_MENTOR(3618), - QUEST_GRIM_TALES(2783), - QUEST_KINGS_RANSOM(3888), - QUEST_MONKEY_MADNESS_II(5027), - QUEST_CLIENT_OF_KOUREND(5619), - QUEST_BONE_VOYAGE(5795), - QUEST_THE_QUEEN_OF_THIEVES(6037), - QUEST_THE_DEPTHS_OF_DESPAIR(6027), - QUEST_DRAGON_SLAYER_II(6104), - QUEST_TALE_OF_THE_RIGHTEOUS(6358), - QUEST_A_TASTE_OF_HOPE(6396), - QUEST_MAKING_FRIENDS_WITH_MY_ARM(6528), - QUEST_THE_ASCENT_OF_ARCEUUS(7856), - QUEST_THE_FORSAKEN_TOWER(7796), - //TODO - QUEST_SONG_OF_THE_ELVES(7796), - - /** - * mini-quest varbits, these don't hold the completion value. - */ - QUEST_ARCHITECTURAL_ALLIANCE(4982), - QUEST_BEAR_YOUR_SOUL(5078), - QUEST_CURSE_OF_THE_EMPTY_LORD(821), - QUEST_ENCHANTED_KEY(1391), - QUEST_THE_GENERALS_SHADOW(3330), - QUEST_SKIPPY_AND_THE_MOGRES(1344), - QUEST_LAIR_OF_TARN_RAZORLOR(3290), - QUEST_FAMILY_PEST(5347), - QUEST_THE_MAGE_ARENA_II(6067), - //TODO - QUEST_IN_SEARCH_OF_KNOWLEDGE(6067), - - /** - * Spellbook filtering (1 = unfiltered, 0 = filtered) - */ - FILTER_SPELLBOOK(6718), - - /** - * POH Building mode (1 = yes, 0 = no) - */ - BUILDING_MODE(2176), - - /** - * 1 if in game, 0 if not - */ - LMS_IN_GAME(5314), - - /** - * Amount of pvp kills in current game - */ - LMS_KILLS(5315), - - /** - * The x coordinate of the final safespace (world coord) - */ - LMS_SAFE_X(5316), - - LMS_POISON_PROGRESS(5317), - - /** - * The y coordinate of the final safespace (world coord) - */ - LMS_SAFE_Y(5320), - - /** - * 1 is true, 0 is false. - */ - GAUNTLET_FINAL_ROOM_ENTERED(9177), - - /** - * 1 is true, 0 is false. - */ - GAUNTLET_ENTERED(9178), - - WITHDRAW_X_AMOUNT(3960), - - IN_PVP_AREA(8121), - - /** - * Value of hotkey varbits can be 0-13 - * 0 corresponds to no hotkey set - * 1-12 correspond to F1-F12 respectively - * 13 corresponds to escape - */ - COMBAT_TAB_HOTKEY(4675), - STATS_TAB_HOTKEY(4676), - QUESTS_TAB_HOTKEY(4677), - INVENTORY_TAB_HOTKEY(4678), - EQUIPMENT_TAB_HOTKEY(4679), - PRAYER_TAB_HOTKEY(4680), - SPELLBOOK_TAB_HOTKEY(4682), - FRIENDS_TAB_HOTKEY(4684), - ACCOUNT_MANAGEMENT_TAB_HOTKEY(6517), - LOGOUT_TAB_HOTKEY(4689), - OPTIONS_TAB_HOTKEY(4686), - EMOTES_TAB_HOTKEY(4687), - CLAN_TAB_HOTKEY(4683), - MUSIC_TAB_HOTKEY(4688), - - /** - * Chat Notifications settings - *
- * LOOT_DROP_NOTIFICATIONS: 1 is true, 0 is false - * LOOT_DROP_NOTIFICATIONS_VALUE: gp value - * UNTRADEABLE_LOOT_NOTIFICATIONS: 1 is true, 0 is false - * BOSS_KILL_COUNT_UPDATES: 1 is filtered, 0 is unfiltered - * DROP_ITEM_WARNINGS: 1 is true, 0 is false - * DROP_ITEM_WARNINGS_VALUE: gp value - */ - LOOT_DROP_NOTIFICATIONS(5399), - LOOT_DROP_NOTIFICATIONS_VALUE(5400), - UNTRADEABLE_LOOT_NOTIFICATIONS(5402), - BOSS_KILL_COUNT_UPDATES(4930), - DROP_ITEM_WARNINGS(5411), - DROP_ITEM_WARNINGS_VALUE(5412), - - PARASITE(10151), - ; - - /** - * The raw varbit ID. - */ - private final int id; -} From be7158101257e981aa63e8b4a9795f10f9edcb02 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Wed, 10 Feb 2021 09:48:34 +0000 Subject: [PATCH 089/133] openosrs version: 4.0.0 --- buildSrc/src/main/kotlin/Dependencies.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/Dependencies.kt b/buildSrc/src/main/kotlin/Dependencies.kt index 79dc14b728..a230b4272c 100644 --- a/buildSrc/src/main/kotlin/Dependencies.kt +++ b/buildSrc/src/main/kotlin/Dependencies.kt @@ -27,7 +27,7 @@ object ProjectVersions { const val launcherVersion = "2.2.0" const val rlVersion = "1.6.38" - const val openosrsVersion = "3.5.4" + const val openosrsVersion = "4.0.0" const val rsversion = 193 const val cacheversion = 165 From 68a905f9f073d2aab03e4043480d0d7cc862ffa5 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 11 Feb 2021 21:51:52 +0000 Subject: [PATCH 090/133] can this fucking class stay final please --- runelite-api/src/main/java/net/runelite/api/AnimationID.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 45b2369f23..cb2981e15c 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -30,7 +30,7 @@ package net.runelite.api; * Note: This class is not complete and may not contain a specific animation * required. */ -public class AnimationID +public final class AnimationID { public static final int IDLE = -1; public static final int HERBLORE_PESTLE_AND_MORTAR = 364; From c933f41416c92269e313a9ed66072961af0c38f2 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 11 Feb 2021 22:05:30 +0000 Subject: [PATCH 091/133] update copyright --- .../client/NonScheduledExecutorServiceExceptionLogger.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/NonScheduledExecutorServiceExceptionLogger.java b/runelite-client/src/main/java/net/runelite/client/NonScheduledExecutorServiceExceptionLogger.java index 219781b278..0f0986a703 100644 --- a/runelite-client/src/main/java/net/runelite/client/NonScheduledExecutorServiceExceptionLogger.java +++ b/runelite-client/src/main/java/net/runelite/client/NonScheduledExecutorServiceExceptionLogger.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, ThatGamerBlue + * Copyright (c) 2020, SwazRGB * All rights reserved. * * Redistribution and use in source and binary forms, with or without From 92f5150185b148d998d2f0e52a3125838afc33a5 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 11 Feb 2021 22:05:43 +0000 Subject: [PATCH 092/133] kittype: use ordinal as index --- .../java/net/runelite/api/kit/KitType.java | 37 +++++++++++-------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/kit/KitType.java b/runelite-api/src/main/java/net/runelite/api/kit/KitType.java index 17d58ca3dc..82c6781949 100644 --- a/runelite-api/src/main/java/net/runelite/api/kit/KitType.java +++ b/runelite-api/src/main/java/net/runelite/api/kit/KitType.java @@ -39,25 +39,30 @@ import lombok.Getter; @AllArgsConstructor public enum KitType { - HEAD("Head", 0), - CAPE("Cape", 1), - AMULET("Amulet", 2), - WEAPON("Weapon", 3), - TORSO("Torso", 4), - SHIELD("Shield", 5), - ARMS("Arms", 6), - LEGS("Legs", 7), - HAIR("Hair", 8), - HANDS("Hands", 9), - BOOTS("Boots", 10), - JAW("Jaw", 11), - RING("Ring", 12), - AMMUNITION("Ammo", 13); + HEAD("Head"), + CAPE("Cape"), + AMULET("Amulet"), + WEAPON("Weapon"), + TORSO("Torso"), + SHIELD("Shield"), + ARMS("Arms"), + LEGS("Legs"), + HAIR("Hair"), + HANDS("Hands"), + BOOTS("Boots"), + JAW("Jaw"), + RING("Ring"), + AMMUNITION("Ammo"); private final String name; /** - * Gets the raw equipment index for use in {PlayerAppearance#getEquipmentIds()}. + * Gets the raw equipment index for use in {@link net.runelite.api.PlayerComposition#getEquipmentIds()}. + * + * @return raw equipment index */ - private final int index; + public int getIndex() + { + return ordinal(); + } } \ No newline at end of file From fd908b9596aa9370f2955fb84f5cfdf412b56e6a Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 11 Feb 2021 22:09:46 +0000 Subject: [PATCH 093/133] use logger templates --- .../src/main/java/net/runelite/client/eventbus/EventBus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java index 3d9cf9363e..4195484553 100644 --- a/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java +++ b/runelite-client/src/main/java/net/runelite/client/eventbus/EventBus.java @@ -140,7 +140,7 @@ public class EventBus final String preferredName = "on" + parameterClazz.getSimpleName(); if (!method.getName().equals(preferredName)) { - log.warn("Subscribed method " + method + " should be named " + preferredName); + log.warn("Subscribed method {} should be named {}", method, preferredName); } method.setAccessible(true); From 9a47bf45597808140869fdd97222e723f2e1f374 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 11 Feb 2021 22:25:22 +0000 Subject: [PATCH 094/133] fix plugin manager licenses --- .../OPRSExternalPf4jPluginManager.java | 25 ------------------- .../plugins/OPRSExternalPluginClasspath.java | 2 +- .../plugins/OPRSExternalPluginFileFilter.java | 2 +- .../plugins/OPRSExternalPluginManager.java | 2 +- 4 files changed, 3 insertions(+), 28 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPf4jPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPf4jPluginManager.java index e9f9f99127..30abbfa8eb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPf4jPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPf4jPluginManager.java @@ -1,28 +1,3 @@ -/* - * Copyright (c) 2021, ThatGamerBlue - * 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; import com.openosrs.client.OpenOSRS; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginClasspath.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginClasspath.java index a44aa7e8f7..9415590a95 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginClasspath.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginClasspath.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, ThatGamerBlue + * Copyright (c) 2020, SwazRGB * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginFileFilter.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginFileFilter.java index 01ace2846e..c9f44068e9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginFileFilter.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginFileFilter.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, ThatGamerBlue + * Copyright (c) 2020, SwazRGB * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java index 54ace7488b..3512d1a859 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java @@ -93,7 +93,7 @@ import org.pf4j.update.UpdateRepository; @Singleton public class OPRSExternalPluginManager { - public static final String DEFAULT_PLUGIN_REPOS = "OpenOSRS:https://raw.githubusercontent.com/open-osrs/plugins-v2/master/"; + public static final String DEFAULT_PLUGIN_REPOS = ""; static final String DEVELOPMENT_MANIFEST_PATH = "build/tmp/jar/MANIFEST.MF"; public static ArrayList pluginClassLoaders = new ArrayList<>(); From 4521f9e032f420587b37d811073ce24db80c6dcc Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 11 Feb 2021 22:31:58 +0000 Subject: [PATCH 095/133] license again --- .../runelite/client/util/DeferredDocumentChangedListener.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java b/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java index 64758b3acd..a0cddbf5f4 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java +++ b/runelite-client/src/main/java/net/runelite/client/util/DeferredDocumentChangedListener.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021, ThatGamerBlue + * Copyright (c) 2019, Owain van Brakel * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -22,7 +22,6 @@ * (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.util; import java.util.ArrayList; From 99b87caabad3ac37bb4b09ca2013a2527cae8387 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Thu, 11 Feb 2021 23:11:19 +0000 Subject: [PATCH 096/133] checkstyle --- .../src/main/java/net/runelite/api/queries/ShopItemQuery.java | 1 - .../src/main/java/com/openosrs/client/game/SoundManager.java | 4 ---- 2 files changed, 5 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java b/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java index 415661f6bf..0bd789fbac 100644 --- a/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java +++ b/runelite-api/src/main/java/net/runelite/api/queries/ShopItemQuery.java @@ -32,7 +32,6 @@ import java.util.stream.Collectors; import net.runelite.api.Client; import net.runelite.api.QueryResults; import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; import net.runelite.api.widgets.WidgetItem; public class ShopItemQuery extends WidgetItemQuery diff --git a/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java b/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java index 73df208095..0c1330cd5b 100644 --- a/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java +++ b/runelite-client/src/main/java/com/openosrs/client/game/SoundManager.java @@ -25,7 +25,6 @@ package com.openosrs.client.game; -import com.google.inject.Inject; import java.io.IOException; import javax.inject.Singleton; import javax.sound.sampled.AudioFormat; @@ -37,9 +36,6 @@ import javax.sound.sampled.FloatControl; import javax.sound.sampled.LineUnavailableException; import javax.sound.sampled.SourceDataLine; import javax.sound.sampled.UnsupportedAudioFileException; -import net.runelite.client.config.RuneLiteConfig; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; @Singleton public class SoundManager From 13efaa6a0c7294b1526ae8d25471708d45d248d6 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 10 Feb 2021 21:01:53 -0500 Subject: [PATCH 097/133] gpu: add opencl support for macos This allows using opencl as an alternative to opengl compute shaders on macos, which does not support compute shaders. Now, macos can finally use the extended draw distance feature of the gpu plugin. This also includes code for using opencl with Windows and Linux if we want to enable that in the future. A copy of the existing compute shaders have been checked in and ported to opencl, keeping support for opengl compute shaders on Windows and Linux. Co-authored-by: Paul Norton --- runelite-client/pom.xml | 19 + .../runelite/client/plugins/gpu/GLBuffer.java | 40 ++ .../client/plugins/gpu/GpuPlugin.java | 399 ++++++++------ .../client/plugins/gpu/OpenCLManager.java | 521 ++++++++++++++++++ .../runelite/client/plugins/gpu/cl_types.cl | 55 ++ .../net/runelite/client/plugins/gpu/common.cl | 104 ++++ .../net/runelite/client/plugins/gpu/comp.cl | 97 ++++ .../client/plugins/gpu/comp_unordered.cl | 87 +++ .../client/plugins/gpu/priority_render.cl | 298 ++++++++++ 9 files changed, 1445 insertions(+), 175 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 10000e6125..48eb8825ce 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -177,6 +177,25 @@ 2.4.0-rc-20210117 runtime + + net.runelite.jocl + jocl + 1.0 + + + net.runelite.jocl + jocl + 1.0 + macos-x64 + runtime + + + net.runelite.jocl + jocl + 1.0 + macos-arm64 + runtime + net.runelite archive-patcher diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java new file mode 100644 index 0000000000..cd3b7288ed --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GLBuffer.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2021, 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.gpu; + +import org.jocl.Pointer; +import org.jocl.cl_mem; + +class GLBuffer +{ + int glBufferId = -1; + int size = -1; + cl_mem cl_mem; + + Pointer ptr() + { + return cl_mem != null ? Pointer.to(cl_mem) : null; + } +} 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 a6be4e5872..8ba9db5a67 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 @@ -29,6 +29,11 @@ import com.google.inject.Provides; import com.jogamp.nativewindow.awt.AWTGraphicsConfiguration; import com.jogamp.nativewindow.awt.JAWTWindow; import com.jogamp.opengl.GL; +import static com.jogamp.opengl.GL.GL_ARRAY_BUFFER; +import static com.jogamp.opengl.GL.GL_DYNAMIC_DRAW; +import static com.jogamp.opengl.GL2ES2.GL_STREAM_DRAW; +import static com.jogamp.opengl.GL2ES3.GL_STATIC_COPY; +import static com.jogamp.opengl.GL2ES3.GL_UNIFORM_BUFFER; import com.jogamp.opengl.GL4; import com.jogamp.opengl.GLCapabilities; import com.jogamp.opengl.GLContext; @@ -45,6 +50,7 @@ import java.awt.Image; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.DataBufferInt; +import java.nio.Buffer; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; @@ -93,6 +99,10 @@ import net.runelite.client.plugins.gpu.config.UIScalingMode; import net.runelite.client.plugins.gpu.template.Template; import net.runelite.client.ui.DrawManager; import net.runelite.client.util.OSType; +import org.jocl.CL; +import static org.jocl.CL.CL_MEM_READ_ONLY; +import static org.jocl.CL.CL_MEM_WRITE_ONLY; +import static org.jocl.CL.clCreateFromGLBuffer; @PluginDescriptor( name = "GPU", @@ -105,8 +115,8 @@ import net.runelite.client.util.OSType; public class GpuPlugin extends Plugin implements DrawCallbacks { // This is the maximum number of triangles the compute shaders support - private static final int MAX_TRIANGLE = 4096; - private static final int SMALL_TRIANGLE_COUNT = 512; + static final int MAX_TRIANGLE = 4096; + static final int SMALL_TRIANGLE_COUNT = 512; private static final int FLAG_SCENE_BUFFER = Integer.MIN_VALUE; private static final int DEFAULT_DISTANCE = 25; static final int MAX_DISTANCE = 90; @@ -115,6 +125,9 @@ public class GpuPlugin extends Plugin implements DrawCallbacks @Inject private Client client; + @Inject + private OpenCLManager openCLManager; + @Inject private ClientThread clientThread; @@ -133,7 +146,14 @@ public class GpuPlugin extends Plugin implements DrawCallbacks @Inject private PluginManager pluginManager; - private boolean useComputeShaders; + enum ComputeMode + { + NONE, + OPENGL, + OPENCL + } + + private ComputeMode computeMode = ComputeMode.NONE; private Canvas canvas; private JAWTWindow jawtWindow; @@ -182,23 +202,22 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private int texSceneHandle; private int rboSceneHandle; - // scene vertex buffer id - private int bufferId; - // scene uv buffer id - private int uvBufferId; + // scene vertex buffer + private final GLBuffer sceneVertexBuffer = new GLBuffer(); + // scene uv buffer + private final GLBuffer sceneUvBuffer = new GLBuffer(); - private int tmpBufferId; // temporary scene vertex buffer - private int tmpUvBufferId; // temporary scene uv buffer - private int tmpModelBufferId; // scene model buffer, large - private int tmpModelBufferSmallId; // scene model buffer, small - private int tmpModelBufferUnorderedId; - private int tmpOutBufferId; // target vertex buffer for compute shaders - private int tmpOutUvBufferId; // target uv buffer for compute shaders + private final GLBuffer tmpVertexBuffer = new GLBuffer(); // temporary scene vertex buffer + private final GLBuffer tmpUvBuffer = new GLBuffer(); // temporary scene uv buffer + private final GLBuffer tmpModelBufferLarge = new GLBuffer(); // scene model buffer, large + private final GLBuffer tmpModelBufferSmall = new GLBuffer(); // scene model buffer, small + private final GLBuffer tmpModelBufferUnordered = new GLBuffer(); // scene model buffer, unordered + private final GLBuffer tmpOutBuffer = new GLBuffer(); // target vertex buffer for compute shaders + private final GLBuffer tmpOutUvBuffer = new GLBuffer(); // target uv buffer for compute shaders private int textureArrayId; - private int uniformBufferId; - private final IntBuffer uniformBuffer = GpuIntBuffer.allocateDirect(5 + 3 + 2048 * 4); + private final GLBuffer uniformBuffer = new GLBuffer(); private final float[] textureOffsets = new float[128]; private GpuIntBuffer vertexBuffer; @@ -278,7 +297,6 @@ public class GpuPlugin extends Plugin implements DrawCallbacks { try { - bufferId = uvBufferId = uniformBufferId = tmpBufferId = tmpUvBufferId = tmpModelBufferId = tmpModelBufferSmallId = tmpModelBufferUnorderedId = tmpOutBufferId = tmpOutUvBufferId = -1; texSceneHandle = fboSceneHandle = rboSceneHandle = -1; // AA FBO unorderedModels = smallModels = largeModels = 0; drawingModel = false; @@ -290,8 +308,9 @@ public class GpuPlugin extends Plugin implements DrawCallbacks return false; } - // OSX supports up to OpenGL 4.1, however 4.3 is required for compute shaders - useComputeShaders = config.useComputeShaders() && OSType.getOSType() != OSType.MacOS; + computeMode = config.useComputeShaders() + ? (OSType.getOSType() == OSType.MacOS ? ComputeMode.OPENCL : ComputeMode.OPENGL) + : ComputeMode.NONE; canvas.setIgnoreRepaint(true); @@ -397,7 +416,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks if (client.getGameState() == GameState.LOGGED_IN) { - uploadScene(); + invokeOnMainThread(this::uploadScene); } } catch (Throwable e) @@ -433,6 +452,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks invokeOnMainThread(() -> { + openCLManager.cleanup(); + if (gl != null) { if (textureArrayId != -1) @@ -441,11 +462,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks textureArrayId = -1; } - if (uniformBufferId != -1) - { - glDeleteBuffer(gl, uniformBufferId); - uniformBufferId = -1; - } + destroyGlBuffer(uniformBuffer); shutdownBuffers(); shutdownInterfaceTexture(); @@ -519,12 +536,16 @@ public class GpuPlugin extends Plugin implements DrawCallbacks glProgram = PROGRAM.compile(gl, template); glUiProgram = UI_PROGRAM.compile(gl, template); - if (useComputeShaders) + if (computeMode == ComputeMode.OPENGL) { glComputeProgram = COMPUTE_PROGRAM.compile(gl, template); glSmallComputeProgram = SMALL_COMPUTE_PROGRAM.compile(gl, template); glUnorderedComputeProgram = UNORDERED_COMPUTE_PROGRAM.compile(gl, template); } + else if (computeMode == ComputeMode.OPENCL) + { + openCLManager.init(gl); + } initUniforms(); } @@ -593,8 +614,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks -1f, 1f, 0.0f, 0.0f, 0f // top left }); vboUiBuf.rewind(); - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vboUiHandle); - gl.glBufferData(gl.GL_ARRAY_BUFFER, vboUiBuf.capacity() * Float.BYTES, vboUiBuf, gl.GL_STATIC_DRAW); + gl.glBindBuffer(GL_ARRAY_BUFFER, vboUiHandle); + gl.glBufferData(GL_ARRAY_BUFFER, vboUiBuf.capacity() * Float.BYTES, vboUiBuf, gl.GL_STATIC_DRAW); // position attribute gl.glVertexAttribPointer(0, 3, gl.GL_FLOAT, false, 5 * Float.BYTES, 0); @@ -605,7 +626,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks gl.glEnableVertexAttribArray(1); // unbind VBO - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0); + gl.glBindBuffer(GL_ARRAY_BUFFER, 0); } private void shutdownVao() @@ -622,71 +643,49 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private void initBuffers() { - bufferId = glGenBuffers(gl); - uvBufferId = glGenBuffers(gl); - tmpBufferId = glGenBuffers(gl); - tmpUvBufferId = glGenBuffers(gl); - tmpModelBufferId = glGenBuffers(gl); - tmpModelBufferSmallId = glGenBuffers(gl); - tmpModelBufferUnorderedId = glGenBuffers(gl); - tmpOutBufferId = glGenBuffers(gl); - tmpOutUvBufferId = glGenBuffers(gl); + initGlBuffer(sceneVertexBuffer); + initGlBuffer(sceneUvBuffer); + initGlBuffer(tmpVertexBuffer); + initGlBuffer(tmpUvBuffer); + initGlBuffer(tmpModelBufferLarge); + initGlBuffer(tmpModelBufferSmall); + initGlBuffer(tmpModelBufferUnordered); + initGlBuffer(tmpOutBuffer); + initGlBuffer(tmpOutUvBuffer); + } + + private void initGlBuffer(GLBuffer glBuffer) + { + glBuffer.glBufferId = glGenBuffers(gl); } private void shutdownBuffers() { - if (bufferId != -1) - { - glDeleteBuffer(gl, bufferId); - bufferId = -1; - } + destroyGlBuffer(sceneVertexBuffer); + destroyGlBuffer(sceneUvBuffer); - if (uvBufferId != -1) - { - glDeleteBuffer(gl, uvBufferId); - uvBufferId = -1; - } + destroyGlBuffer(tmpVertexBuffer); + destroyGlBuffer(tmpUvBuffer); + destroyGlBuffer(tmpModelBufferLarge); + destroyGlBuffer(tmpModelBufferSmall); + destroyGlBuffer(tmpModelBufferUnordered); + destroyGlBuffer(tmpOutBuffer); + destroyGlBuffer(tmpOutUvBuffer); + } - if (tmpBufferId != -1) + private void destroyGlBuffer(GLBuffer glBuffer) + { + if (glBuffer.glBufferId != -1) { - glDeleteBuffer(gl, tmpBufferId); - tmpBufferId = -1; + glDeleteBuffer(gl, glBuffer.glBufferId); + glBuffer.glBufferId = -1; } + glBuffer.size = -1; - if (tmpUvBufferId != -1) + if (glBuffer.cl_mem != null) { - glDeleteBuffer(gl, tmpUvBufferId); - tmpUvBufferId = -1; - } - - if (tmpModelBufferId != -1) - { - glDeleteBuffer(gl, tmpModelBufferId); - tmpModelBufferId = -1; - } - - if (tmpModelBufferSmallId != -1) - { - glDeleteBuffer(gl, tmpModelBufferSmallId); - tmpModelBufferSmallId = -1; - } - - if (tmpModelBufferUnorderedId != -1) - { - glDeleteBuffer(gl, tmpModelBufferUnorderedId); - tmpModelBufferUnorderedId = -1; - } - - if (tmpOutBufferId != -1) - { - glDeleteBuffer(gl, tmpOutBufferId); - tmpOutBufferId = -1; - } - - if (tmpOutUvBufferId != -1) - { - glDeleteBuffer(gl, tmpOutUvBufferId); - tmpOutUvBufferId = -1; + CL.clReleaseMemObject(glBuffer.cl_mem); + glBuffer.cl_mem = null; } } @@ -709,21 +708,21 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private void initUniformBuffer() { - uniformBufferId = glGenBuffers(gl); - gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, uniformBufferId); - uniformBuffer.clear(); - uniformBuffer.put(new int[8]); + initGlBuffer(uniformBuffer); + + IntBuffer uniformBuf = GpuIntBuffer.allocateDirect(8 + 2048 * 4); + uniformBuf.put(new int[8]); // uniform block final int[] pad = new int[2]; for (int i = 0; i < 2048; i++) { - uniformBuffer.put(Perspective.SINE[i]); - uniformBuffer.put(Perspective.COSINE[i]); - uniformBuffer.put(pad); + uniformBuf.put(Perspective.SINE[i]); + uniformBuf.put(Perspective.COSINE[i]); + uniformBuf.put(pad); // ivec2 alignment in std140 is 16 bytes } - uniformBuffer.flip(); + uniformBuf.flip(); - gl.glBufferData(gl.GL_UNIFORM_BUFFER, uniformBuffer.limit() * Integer.BYTES, uniformBuffer, gl.GL_DYNAMIC_DRAW); - gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, 0); + updateBuffer(uniformBuffer, GL_UNIFORM_BUFFER, uniformBuf.limit() * Integer.BYTES, uniformBuf, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY); + gl.glBindBuffer(GL_UNIFORM_BUFFER, 0); } private void initAAFbo(int width, int height, int aaSamples) @@ -785,9 +784,11 @@ public class GpuPlugin extends Plugin implements DrawCallbacks invokeOnMainThread(() -> { // UBO. Only the first 32 bytes get modified here, the rest is the constant sin/cos table. - gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, uniformBufferId); - uniformBuffer.clear(); - uniformBuffer + // We can reuse the vertex buffer since it isn't used yet. + vertexBuffer.clear(); + vertexBuffer.ensureCapacity(32); + IntBuffer uniformBuf = vertexBuffer.getBuffer(); + uniformBuf .put(yaw) .put(pitch) .put(client.getCenterX()) @@ -796,12 +797,14 @@ public class GpuPlugin extends Plugin implements DrawCallbacks .put(cameraX) .put(cameraY) .put(cameraZ); - uniformBuffer.flip(); + uniformBuf.flip(); - gl.glBufferSubData(gl.GL_UNIFORM_BUFFER, 0, uniformBuffer.limit() * Integer.BYTES, uniformBuffer); - gl.glBindBuffer(gl.GL_UNIFORM_BUFFER, 0); + gl.glBindBuffer(GL_UNIFORM_BUFFER, uniformBuffer.glBufferId); + gl.glBufferSubData(GL_UNIFORM_BUFFER, 0, uniformBuf.limit() * Integer.BYTES, uniformBuf); + gl.glBindBuffer(GL_UNIFORM_BUFFER, 0); - gl.glBindBufferBase(gl.GL_UNIFORM_BUFFER, 0, uniformBufferId); + gl.glBindBufferBase(GL_UNIFORM_BUFFER, 0, uniformBuffer.glBufferId); + uniformBuf.clear(); }); } @@ -813,7 +816,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private void postDraw() { - if (!useComputeShaders) + if (computeMode == ComputeMode.NONE) { // Upload buffers vertexBuffer.flip(); @@ -822,12 +825,8 @@ public class GpuPlugin extends Plugin implements DrawCallbacks IntBuffer vertexBuffer = this.vertexBuffer.getBuffer(); FloatBuffer uvBuffer = this.uvBuffer.getBuffer(); - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpBufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, gl.GL_DYNAMIC_DRAW); - - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpUvBufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, gl.GL_DYNAMIC_DRAW); - + updateBuffer(tmpVertexBuffer, GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, GL_DYNAMIC_DRAW, 0L); + updateBuffer(tmpUvBuffer, GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, GL_DYNAMIC_DRAW, 0L); return; } @@ -844,79 +843,91 @@ public class GpuPlugin extends Plugin implements DrawCallbacks IntBuffer modelBufferSmall = this.modelBufferSmall.getBuffer(); IntBuffer modelBufferUnordered = this.modelBufferUnordered.getBuffer(); - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpBufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, gl.GL_DYNAMIC_DRAW); + // temp buffers + updateBuffer(tmpVertexBuffer, GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY); + updateBuffer(tmpUvBuffer, GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY); - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpUvBufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, gl.GL_DYNAMIC_DRAW); - - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpModelBufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, modelBuffer.limit() * Integer.BYTES, modelBuffer, gl.GL_DYNAMIC_DRAW); - - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpModelBufferSmallId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, modelBufferSmall.limit() * Integer.BYTES, modelBufferSmall, gl.GL_DYNAMIC_DRAW); - - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpModelBufferUnorderedId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, modelBufferUnordered.limit() * Integer.BYTES, modelBufferUnordered, gl.GL_DYNAMIC_DRAW); + // model buffers + updateBuffer(tmpModelBufferLarge, GL_ARRAY_BUFFER, modelBuffer.limit() * Integer.BYTES, modelBuffer, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY); + updateBuffer(tmpModelBufferSmall, GL_ARRAY_BUFFER, modelBufferSmall.limit() * Integer.BYTES, modelBufferSmall, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY); + updateBuffer(tmpModelBufferUnordered, GL_ARRAY_BUFFER, modelBufferUnordered.limit() * Integer.BYTES, modelBufferUnordered, GL_DYNAMIC_DRAW, CL_MEM_READ_ONLY); // Output buffers - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpOutBufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, + updateBuffer(tmpOutBuffer, + GL_ARRAY_BUFFER, targetBufferOffset * 16, // each vertex is an ivec4, which is 16 bytes null, - gl.GL_STREAM_DRAW); - - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, tmpOutUvBufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, - targetBufferOffset * 16, + GL_STREAM_DRAW, + CL_MEM_WRITE_ONLY); + updateBuffer(tmpOutUvBuffer, + GL_ARRAY_BUFFER, + targetBufferOffset * 16, // each vertex is an ivec4, which is 16 bytes null, - gl.GL_STREAM_DRAW); + GL_STREAM_DRAW, + CL_MEM_WRITE_ONLY); - // Bind UBO to compute programs - gl.glUniformBlockBinding(glSmallComputeProgram, uniBlockSmall, 0); - gl.glUniformBlockBinding(glComputeProgram, uniBlockLarge, 0); + if (computeMode == ComputeMode.OPENCL) + { + // The docs for clEnqueueAcquireGLObjects say all pending GL operations must be completed before calling + // clEnqueueAcquireGLObjects, and recommends calling glFinish() as the only portable way to do that. + // However no issues have been observed from not calling it, and so will leave disabled for now. + // gl.glFinish(); + + openCLManager.compute( + unorderedModels, smallModels, largeModels, + sceneVertexBuffer, sceneUvBuffer, + tmpVertexBuffer, tmpUvBuffer, + tmpModelBufferUnordered, tmpModelBufferSmall, tmpModelBufferLarge, + tmpOutBuffer, tmpOutUvBuffer, + uniformBuffer); + return; + } /* * Compute is split into three separate programs: 'unordered', 'small', and 'large' * to save on GPU resources. Small will sort <= 512 faces, large will do <= 4096. */ + // Bind UBO to compute programs + gl.glUniformBlockBinding(glSmallComputeProgram, uniBlockSmall, 0); + gl.glUniformBlockBinding(glComputeProgram, uniBlockLarge, 0); + // unordered gl.glUseProgram(glUnorderedComputeProgram); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferUnorderedId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, this.bufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, this.uvBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferUnordered.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId); gl.glDispatchCompute(unorderedModels, 1, 1); // small gl.glUseProgram(glSmallComputeProgram); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferSmallId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, this.bufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, this.uvBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferSmall.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId); gl.glDispatchCompute(smallModels, 1, 1); // large gl.glUseProgram(glComputeProgram); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, this.bufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, this.uvBufferId); - gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 0, tmpModelBufferLarge.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 1, sceneVertexBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 2, tmpVertexBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 3, tmpOutBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 4, tmpOutUvBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 5, sceneUvBuffer.glBufferId); + gl.glBindBufferBase(gl.GL_SHADER_STORAGE_BUFFER, 6, tmpUvBuffer.glBufferId); gl.glDispatchCompute(largeModels, 1, 1); } @@ -926,7 +937,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks SceneTilePaint paint, int tileZ, int tileX, int tileY, int zoom, int centerX, int centerY) { - if (!useComputeShaders) + if (computeMode == ComputeMode.NONE) { targetBufferOffset += sceneUploader.upload(paint, tileZ, tileX, tileY, @@ -963,7 +974,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks SceneTileModel model, int tileZ, int tileX, int tileY, int zoom, int centerX, int centerY) { - if (!useComputeShaders) + if (computeMode == ComputeMode.NONE) { targetBufferOffset += sceneUploader.upload(model, tileX, tileY, @@ -1131,7 +1142,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks // Ceil the sizes because even if the size is 599.1 we want to treat it as size 600 (i.e. render to the x=599 pixel). renderViewportHeight = (int) Math.ceil(scaleFactorY * (renderViewportHeight)) + padding * 2; - renderViewportWidth = (int) Math.ceil(scaleFactorX * (renderViewportWidth )) + padding * 2; + renderViewportWidth = (int) Math.ceil(scaleFactorX * (renderViewportWidth )) + padding * 2; // Floor the offsets because even if the offset is 4.9, we want to render to the x=4 pixel anyway. renderHeightOff = (int) Math.floor(scaleFactorY * (renderHeightOff)) - padding; @@ -1195,27 +1206,36 @@ public class GpuPlugin extends Plugin implements DrawCallbacks gl.glBindVertexArray(vaoHandle); int vertexBuffer, uvBuffer; - if (useComputeShaders) + if (computeMode != ComputeMode.NONE) { - // Before reading the SSBOs written to from postDrawScene() we must insert a barrier - gl.glMemoryBarrier(gl.GL_SHADER_STORAGE_BARRIER_BIT); + if (computeMode == ComputeMode.OPENGL) + { + // Before reading the SSBOs written to from postDrawScene() we must insert a barrier + gl.glMemoryBarrier(gl.GL_SHADER_STORAGE_BARRIER_BIT); + } + else + { + // Wait for the command queue to finish, so that we know the compute is done + openCLManager.finish(); + } + // Draw using the output buffer of the compute - vertexBuffer = tmpOutBufferId; - uvBuffer = tmpOutUvBufferId; + vertexBuffer = tmpOutBuffer.glBufferId; + uvBuffer = tmpOutUvBuffer.glBufferId; } else { // Only use the temporary buffers, which will contain the full scene - vertexBuffer = tmpBufferId; - uvBuffer = tmpUvBufferId; + vertexBuffer = tmpVertexBuffer.glBufferId; + uvBuffer = tmpUvBuffer.glBufferId; } gl.glEnableVertexAttribArray(0); - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, vertexBuffer); + gl.glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer); gl.glVertexAttribIPointer(0, 4, gl.GL_INT, 0, 0); gl.glEnableVertexAttribArray(1); - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, uvBuffer); + gl.glBindBuffer(GL_ARRAY_BUFFER, uvBuffer); gl.glVertexAttribPointer(1, 4, gl.GL_FLOAT, false, 0, 0); gl.glDrawArrays(gl.GL_TRIANGLES, 0, targetBufferOffset); @@ -1400,12 +1420,12 @@ public class GpuPlugin extends Plugin implements DrawCallbacks @Subscribe public void onGameStateChanged(GameStateChanged gameStateChanged) { - if (!useComputeShaders || gameStateChanged.getGameState() != GameState.LOGGED_IN) + if (computeMode == ComputeMode.NONE || gameStateChanged.getGameState() != GameState.LOGGED_IN) { return; } - uploadScene(); + invokeOnMainThread(this::uploadScene); } private void uploadScene() @@ -1421,13 +1441,10 @@ public class GpuPlugin extends Plugin implements DrawCallbacks IntBuffer vertexBuffer = this.vertexBuffer.getBuffer(); FloatBuffer uvBuffer = this.uvBuffer.getBuffer(); - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, bufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, gl.GL_STATIC_COPY); + updateBuffer(sceneVertexBuffer, GL_ARRAY_BUFFER, vertexBuffer.limit() * Integer.BYTES, vertexBuffer, GL_STATIC_COPY, CL_MEM_READ_ONLY); + updateBuffer(sceneUvBuffer, GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, GL_STATIC_COPY, CL_MEM_READ_ONLY); - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, uvBufferId); - gl.glBufferData(gl.GL_ARRAY_BUFFER, uvBuffer.limit() * Float.BYTES, uvBuffer, gl.GL_STATIC_COPY); - - gl.glBindBuffer(gl.GL_ARRAY_BUFFER, 0); + gl.glBindBuffer(GL_ARRAY_BUFFER, 0); vertexBuffer.clear(); uvBuffer.clear(); @@ -1492,7 +1509,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks @Override public void draw(Renderable renderable, int orientation, int pitchSin, int pitchCos, int yawSin, int yawCos, int x, int y, int z, long hash) { - if (!useComputeShaders) + if (computeMode == ComputeMode.NONE) { Model model = renderable instanceof Model ? (Model) renderable : renderable.getModel(); if (model != null) @@ -1673,7 +1690,7 @@ public class GpuPlugin extends Plugin implements DrawCallbacks private int getDrawDistance() { - final int limit = useComputeShaders ? MAX_DISTANCE : DEFAULT_DISTANCE; + final int limit = computeMode != ComputeMode.NONE ? MAX_DISTANCE : DEFAULT_DISTANCE; return Ints.constrainToRange(config.drawDistance(), 0, limit); } @@ -1688,4 +1705,36 @@ public class GpuPlugin extends Plugin implements DrawCallbacks runnable.run(); } } + + private void updateBuffer(GLBuffer glBuffer, int target, int size, Buffer data, int usage, long clFlags) + { + gl.glBindBuffer(target, glBuffer.glBufferId); + if (size > glBuffer.size) + { + log.trace("Buffer resize: {} {} -> {}", glBuffer, glBuffer.size, size); + + glBuffer.size = size; + gl.glBufferData(target, size, data, usage); + + if (computeMode == ComputeMode.OPENCL) + { + if (glBuffer.cl_mem != null) + { + CL.clReleaseMemObject(glBuffer.cl_mem); + } + if (size == 0) + { + glBuffer.cl_mem = null; + } + else + { + glBuffer.cl_mem = clCreateFromGLBuffer(openCLManager.context, clFlags, glBuffer.glBufferId, null); + } + } + } + else if (data != null) + { + gl.glBufferSubData(target, 0, size, data); + } + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java new file mode 100644 index 0000000000..77c2f9b575 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/OpenCLManager.java @@ -0,0 +1,521 @@ +/* + * Copyright (c) 2021, 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.gpu; + +import com.google.common.base.Charsets; +import com.jogamp.nativewindow.NativeSurface; +import com.jogamp.opengl.GL4; +import com.jogamp.opengl.GLContext; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.Objects; +import javax.inject.Singleton; +import jogamp.opengl.GLContextImpl; +import jogamp.opengl.GLDrawableImpl; +import jogamp.opengl.egl.EGLContext; +import jogamp.opengl.macosx.cgl.CGL; +import jogamp.opengl.windows.wgl.WindowsWGLContext; +import jogamp.opengl.x11.glx.X11GLXContext; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.plugins.gpu.template.Template; +import net.runelite.client.util.OSType; +import org.jocl.CL; +import static org.jocl.CL.*; +import org.jocl.CLException; +import org.jocl.Pointer; +import org.jocl.Sizeof; +import org.jocl.cl_command_queue; +import org.jocl.cl_context; +import org.jocl.cl_context_properties; +import org.jocl.cl_device_id; +import org.jocl.cl_event; +import org.jocl.cl_kernel; +import org.jocl.cl_mem; +import org.jocl.cl_platform_id; +import org.jocl.cl_program; + +@Singleton +@Slf4j +class OpenCLManager +{ + private static final String GL_SHARING_PLATFORM_EXT = "cl_khr_gl_sharing"; + + private static final String KERNEL_NAME_UNORDERED = "computeUnordered"; + private static final String KERNEL_NAME_LARGE = "computeLarge"; + + private static final int MIN_WORK_GROUP_SIZE = 256; + private static final int SMALL_SIZE = GpuPlugin.SMALL_TRIANGLE_COUNT; + private static final int LARGE_SIZE = GpuPlugin.MAX_TRIANGLE; + // struct shared_data { + // int totalNum[12]; + // int totalDistance[12]; + // int totalMappedNum[18]; + // int min10; + // int dfs[0]; + // }; + private static final int SHARED_SIZE = 12 + 12 + 18 + 1; // in ints + + // The number of faces each worker processes in the two kernels + private int largeFaceCount; + private int smallFaceCount; + + private cl_platform_id platform; + private cl_device_id device; + cl_context context; + private cl_command_queue commandQueue; + + private cl_program programUnordered; + private cl_program programSmall; + private cl_program programLarge; + + private cl_kernel kernelUnordered; + private cl_kernel kernelSmall; + private cl_kernel kernelLarge; + + void init(GL4 gl) + { + CL.setExceptionsEnabled(true); + + switch (OSType.getOSType()) + { + case Windows: + case Linux: + initPlatform(); + initDevice(); + initContext(gl); + break; + case MacOS: + initMacOS(gl); + break; + default: + throw new RuntimeException("Unsupported OS Type " + OSType.getOSType().name()); + } + ensureMinWorkGroupSize(); + initQueue(); + compilePrograms(); + } + + void cleanup() + { + if (programUnordered != null) + { + CL.clReleaseProgram(programUnordered); + programUnordered = null; + } + + if (programSmall != null) + { + CL.clReleaseProgram(programSmall); + programSmall = null; + } + + if (programLarge != null) + { + CL.clReleaseProgram(programLarge); + programLarge = null; + } + + if (kernelUnordered != null) + { + CL.clReleaseKernel(kernelUnordered); + kernelUnordered = null; + } + + if (kernelSmall != null) + { + CL.clReleaseKernel(kernelSmall); + kernelSmall = null; + } + + if (kernelLarge != null) + { + CL.clReleaseKernel(kernelLarge); + kernelLarge = null; + } + + if (commandQueue != null) + { + CL.clReleaseCommandQueue(commandQueue); + commandQueue = null; + } + + if (context != null) + { + CL.clReleaseContext(context); + context = null; + } + + if (device != null) + { + CL.clReleaseDevice(device); + device = null; + } + } + + private String logPlatformInfo(cl_platform_id platform, int param) + { + long[] size = new long[1]; + clGetPlatformInfo(platform, param, 0, null, size); + + byte[] buffer = new byte[(int) size[0]]; + clGetPlatformInfo(platform, param, buffer.length, Pointer.to(buffer), null); + String platformInfo = new String(buffer, Charsets.UTF_8); + log.debug("Platform: {}, {}", stringFor_cl_platform_info(param), platformInfo); + return platformInfo; + } + + private void logBuildInfo(cl_program program, int param) + { + long[] size = new long[1]; + clGetProgramBuildInfo(program, device, param, 0, null, size); + + ByteBuffer buffer = ByteBuffer.allocateDirect((int) size[0]); + clGetProgramBuildInfo(program, device, param, buffer.limit(), Pointer.toBuffer(buffer), null); + + switch (param) + { + case CL_PROGRAM_BUILD_STATUS: + log.debug("Build status: {}, {}", stringFor_cl_program_build_info(param), stringFor_cl_build_status(buffer.getInt())); + break; + case CL_PROGRAM_BINARY_TYPE: + log.debug("Binary type: {}, {}", stringFor_cl_program_build_info(param), stringFor_cl_program_binary_type(buffer.getInt())); + break; + case CL_PROGRAM_BUILD_LOG: + String buildLog = StandardCharsets.US_ASCII.decode(buffer).toString(); + log.trace("Build log: {}, {}", stringFor_cl_program_build_info(param), buildLog); + break; + case CL_PROGRAM_BUILD_OPTIONS: + String message = StandardCharsets.US_ASCII.decode(buffer).toString(); + log.debug("Build options: {}, {}", stringFor_cl_program_build_info(param), message); + break; + default: + throw new IllegalArgumentException(); + } + } + + private void initPlatform() + { + int[] platformCount = new int[1]; + clGetPlatformIDs(0, null, platformCount); + if (platformCount[0] == 0) + { + throw new RuntimeException("No compute platforms found"); + } + + cl_platform_id[] platforms = new cl_platform_id[platformCount[0]]; + clGetPlatformIDs(platforms.length, platforms, null); + + for (cl_platform_id platform : platforms) + { + log.debug("Found cl_platform_id {}", platform); + logPlatformInfo(platform, CL_PLATFORM_PROFILE); + logPlatformInfo(platform, CL_PLATFORM_VERSION); + logPlatformInfo(platform, CL_PLATFORM_NAME); + logPlatformInfo(platform, CL_PLATFORM_VENDOR); + String[] extensions = logPlatformInfo(platform, CL_PLATFORM_EXTENSIONS).split(" "); + if (Arrays.stream(extensions).noneMatch(s -> s.equals(GL_SHARING_PLATFORM_EXT))) + { + throw new RuntimeException("Platform does not support OpenGL buffer sharing"); + } + } + + platform = platforms[0]; + log.debug("Selected cl_platform_id {}", platform); + } + + private void initDevice() + { + int[] deviceCount = new int[1]; + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, 0, null, deviceCount); + if (deviceCount[0] == 0) + { + throw new RuntimeException("No compute devices found"); + } + + cl_device_id[] devices = new cl_device_id[(int) deviceCount[0]]; + clGetDeviceIDs(platform, CL_DEVICE_TYPE_GPU, devices.length, devices, null); + + for (cl_device_id device : devices) + { + long[] size = new long[1]; + clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, 0, null, size); + + byte[] devInfoBuf = new byte[(int) size[0]]; + clGetDeviceInfo(device, CL_DEVICE_EXTENSIONS, devInfoBuf.length, Pointer.to(devInfoBuf), null); + + log.debug("Found cl_device_id: {}", device); + log.debug("Device extensions: {}", new String(devInfoBuf, Charsets.UTF_8)); + } + + device = devices[0]; + log.debug("Selected cl_device_id {}", device); + } + + private void initContext(GL4 gl) + { + // set computation platform + cl_context_properties contextProps = new cl_context_properties(); + contextProps.addProperty(CL_CONTEXT_PLATFORM, platform); + + // pull gl context + GLContext glContext = gl.getContext(); + log.debug("Got GLContext of type {}", glContext.getClass().getSimpleName()); + if (!glContext.isCurrent()) + { + throw new RuntimeException("Can't create OpenCL context from inactive GL Context"); + } + + // get correct props based on os + long glContextHandle = glContext.getHandle(); + GLContextImpl glContextImpl = (GLContextImpl) glContext; + GLDrawableImpl glDrawableImpl = glContextImpl.getDrawableImpl(); + NativeSurface nativeSurface = glDrawableImpl.getNativeSurface(); + + if (glContext instanceof X11GLXContext) + { + long displayHandle = nativeSurface.getDisplayHandle(); + contextProps.addProperty(CL_GL_CONTEXT_KHR, glContextHandle); + contextProps.addProperty(CL_GLX_DISPLAY_KHR, displayHandle); + } + else if (glContext instanceof WindowsWGLContext) + { + long surfaceHandle = nativeSurface.getSurfaceHandle(); + contextProps.addProperty(CL_GL_CONTEXT_KHR, glContextHandle); + contextProps.addProperty(CL_WGL_HDC_KHR, surfaceHandle); + } + else if (glContext instanceof EGLContext) + { + long displayHandle = nativeSurface.getDisplayHandle(); + contextProps.addProperty(CL_GL_CONTEXT_KHR, glContextHandle); + contextProps.addProperty(CL_EGL_DISPLAY_KHR, displayHandle); + } + + log.debug("Creating context with props: {}", contextProps); + context = clCreateContext(contextProps, 1, new cl_device_id[]{device}, null, null, null); + log.debug("Created compute context {}", context); + } + + private void initMacOS(GL4 gl) + { + // get sharegroup from gl context + GLContext glContext = gl.getContext(); + if (!glContext.isCurrent()) + { + throw new RuntimeException("Can't create context from inactive GL"); + } + long cglContext = CGL.CGLGetCurrentContext(); + long cglShareGroup = CGL.CGLGetShareGroup(cglContext); + + // build context props + cl_context_properties contextProps = new cl_context_properties(); + contextProps.addProperty(CL_CONTEXT_PROPERTY_USE_CGL_SHAREGROUP_APPLE, cglShareGroup); + + // ask macos to make the context for us + log.debug("Creating context with props: {}", contextProps); + context = clCreateContext(contextProps, 0, null, null, null, null); + + // pull the compute device out of the provided context + device = new cl_device_id(); + clGetGLContextInfoAPPLE(context, cglContext, CL_CGL_DEVICE_FOR_CURRENT_VIRTUAL_SCREEN_APPLE, Sizeof.cl_device_id, Pointer.to(device), null); + + log.debug("Got macOS CLGL compute device {}", device); + } + + private void ensureMinWorkGroupSize() + { + long[] maxWorkGroupSize = new long[1]; + clGetDeviceInfo(device, CL_DEVICE_MAX_WORK_GROUP_SIZE, Sizeof.size_t, Pointer.to(maxWorkGroupSize), null); + log.debug("Device CL_DEVICE_MAX_WORK_GROUP_SIZE: {}", maxWorkGroupSize[0]); + + if (maxWorkGroupSize[0] < MIN_WORK_GROUP_SIZE) + { + throw new RuntimeException("Compute device does not support min work group size " + MIN_WORK_GROUP_SIZE); + } + + // Largest power of 2 less than or equal to maxWorkGroupSize + int groupSize = 0x80000000 >>> Integer.numberOfLeadingZeros((int) maxWorkGroupSize[0]); + largeFaceCount = LARGE_SIZE / (Math.min(groupSize, LARGE_SIZE)); + smallFaceCount = SMALL_SIZE / (Math.min(groupSize, SMALL_SIZE)); + + log.debug("Face counts: small: {}, large: {}", smallFaceCount, largeFaceCount); + } + + private void initQueue() + { + long[] l = new long[1]; + clGetDeviceInfo(device, CL_DEVICE_QUEUE_PROPERTIES, Sizeof.cl_long, Pointer.to(l), null); + + commandQueue = clCreateCommandQueue(context, device, l[0] & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, null); + log.debug("Created command_queue {}, properties {}", commandQueue, l[0] & CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE); + } + + private cl_program compileProgram(String programSource) + { + log.trace("Compiling program:\n {}", programSource); + cl_program program = clCreateProgramWithSource(context, 1, new String[]{programSource}, null, null); + + try + { + clBuildProgram(program, 0, null, null, null, null); + } + catch (CLException e) + { + logBuildInfo(program, CL_PROGRAM_BUILD_LOG); + throw e; + } + + logBuildInfo(program, CL_PROGRAM_BUILD_STATUS); + logBuildInfo(program, CL_PROGRAM_BINARY_TYPE); + logBuildInfo(program, CL_PROGRAM_BUILD_OPTIONS); + logBuildInfo(program, CL_PROGRAM_BUILD_LOG); + return program; + } + + private cl_kernel getKernel(cl_program program, String kernelName) + { + cl_kernel kernel = clCreateKernel(program, kernelName, null); + log.debug("Loaded kernel {} for program {}", kernelName, program); + return kernel; + } + + private void compilePrograms() + { + Template templateSmall = new Template() + .addInclude(OpenCLManager.class) + .add(key -> key.equals("FACE_COUNT") ? ("#define FACE_COUNT " + smallFaceCount) : null); + Template templateLarge = new Template() + .addInclude(OpenCLManager.class) + .add(key -> key.equals("FACE_COUNT") ? ("#define FACE_COUNT " + largeFaceCount) : null); + + String unordered = new Template() + .addInclude(OpenCLManager.class) + .load("comp_unordered.cl"); + String small = templateSmall.load("comp.cl"); + String large = templateLarge.load("comp.cl"); + + programUnordered = compileProgram(unordered); + programSmall = compileProgram(small); + programLarge = compileProgram(large); + + kernelUnordered = getKernel(programUnordered, KERNEL_NAME_UNORDERED); + kernelSmall = getKernel(programSmall, KERNEL_NAME_LARGE); + kernelLarge = getKernel(programLarge, KERNEL_NAME_LARGE); + } + + void compute(int unorderedModels, int smallModels, int largeModels, + GLBuffer sceneVertexBuffer, + GLBuffer sceneUvBuffer, + GLBuffer vertexBuffer, + GLBuffer uvBuffer, + GLBuffer unorderedBuffer, + GLBuffer smallBuffer, + GLBuffer largeBuffer, + GLBuffer outVertexBuffer, + GLBuffer outUvBuffer, + GLBuffer uniformBuffer + ) + { + cl_mem[] glBuffersAll = { + sceneVertexBuffer.cl_mem, + sceneUvBuffer.cl_mem, + unorderedBuffer.cl_mem, + smallBuffer.cl_mem, + largeBuffer.cl_mem, + vertexBuffer.cl_mem, + uvBuffer.cl_mem, + outVertexBuffer.cl_mem, + outUvBuffer.cl_mem, + uniformBuffer.cl_mem, + }; + cl_mem[] glBuffers = Arrays.stream(glBuffersAll) + .filter(Objects::nonNull) + .toArray(cl_mem[]::new); + + cl_event acquireGLBuffers = new cl_event(); + clEnqueueAcquireGLObjects(commandQueue, glBuffers.length, glBuffers, 0, null, acquireGLBuffers); + + cl_event[] computeEvents = { + new cl_event(), + new cl_event(), + new cl_event() + }; + int numComputeEvents = 0; + + if (unorderedModels > 0) + { + clSetKernelArg(kernelUnordered, 0, Sizeof.cl_mem, unorderedBuffer.ptr()); + clSetKernelArg(kernelUnordered, 1, Sizeof.cl_mem, sceneVertexBuffer.ptr()); + clSetKernelArg(kernelUnordered, 2, Sizeof.cl_mem, vertexBuffer.ptr()); + clSetKernelArg(kernelUnordered, 3, Sizeof.cl_mem, sceneUvBuffer.ptr()); + clSetKernelArg(kernelUnordered, 4, Sizeof.cl_mem, uvBuffer.ptr()); + clSetKernelArg(kernelUnordered, 5, Sizeof.cl_mem, outVertexBuffer.ptr()); + clSetKernelArg(kernelUnordered, 6, Sizeof.cl_mem, outUvBuffer.ptr()); + + // queue compute call after acquireGLBuffers + clEnqueueNDRangeKernel(commandQueue, kernelUnordered, 1, null, + new long[]{unorderedModels * 6L}, new long[]{6}, 1, new cl_event[]{acquireGLBuffers}, computeEvents[numComputeEvents++]); + } + + if (smallModels > 0) + { + clSetKernelArg(kernelSmall, 0, (SHARED_SIZE + SMALL_SIZE) * Integer.BYTES, null); + clSetKernelArg(kernelSmall, 1, Sizeof.cl_mem, smallBuffer.ptr()); + clSetKernelArg(kernelSmall, 2, Sizeof.cl_mem, sceneVertexBuffer.ptr()); + clSetKernelArg(kernelSmall, 3, Sizeof.cl_mem, vertexBuffer.ptr()); + clSetKernelArg(kernelSmall, 4, Sizeof.cl_mem, sceneUvBuffer.ptr()); + clSetKernelArg(kernelSmall, 5, Sizeof.cl_mem, uvBuffer.ptr()); + clSetKernelArg(kernelSmall, 6, Sizeof.cl_mem, outVertexBuffer.ptr()); + clSetKernelArg(kernelSmall, 7, Sizeof.cl_mem, outUvBuffer.ptr()); + clSetKernelArg(kernelSmall, 8, Sizeof.cl_mem, uniformBuffer.ptr()); + + clEnqueueNDRangeKernel(commandQueue, kernelSmall, 1, null, + new long[]{smallModels * (SMALL_SIZE / smallFaceCount)}, new long[]{SMALL_SIZE / smallFaceCount}, 1, new cl_event[]{acquireGLBuffers}, computeEvents[numComputeEvents++]); + } + + if (largeModels > 0) + { + clSetKernelArg(kernelLarge, 0, (SHARED_SIZE + LARGE_SIZE) * Integer.BYTES, null); + clSetKernelArg(kernelLarge, 1, Sizeof.cl_mem, largeBuffer.ptr()); + clSetKernelArg(kernelLarge, 2, Sizeof.cl_mem, sceneVertexBuffer.ptr()); + clSetKernelArg(kernelLarge, 3, Sizeof.cl_mem, vertexBuffer.ptr()); + clSetKernelArg(kernelLarge, 4, Sizeof.cl_mem, sceneUvBuffer.ptr()); + clSetKernelArg(kernelLarge, 5, Sizeof.cl_mem, uvBuffer.ptr()); + clSetKernelArg(kernelLarge, 6, Sizeof.cl_mem, outVertexBuffer.ptr()); + clSetKernelArg(kernelLarge, 7, Sizeof.cl_mem, outUvBuffer.ptr()); + clSetKernelArg(kernelLarge, 8, Sizeof.cl_mem, uniformBuffer.ptr()); + + clEnqueueNDRangeKernel(commandQueue, kernelLarge, 1, null, + new long[]{(long) largeModels * (LARGE_SIZE / largeFaceCount)}, new long[]{LARGE_SIZE / largeFaceCount}, 1, new cl_event[]{acquireGLBuffers}, computeEvents[numComputeEvents++]); + } + + clEnqueueReleaseGLObjects(commandQueue, glBuffers.length, glBuffers, numComputeEvents, computeEvents, null); + } + + void finish() + { + clFinish(commandQueue); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl new file mode 100644 index 0000000000..7f41acab49 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/cl_types.cl @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2021, 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. + */ + +struct uniform { + int cameraYaw; + int cameraPitch; + int centerX; + int centerY; + int zoom; + int cameraX; + int cameraY; + int cameraZ; + int4 sinCosTable[2048]; +}; + +struct shared_data { + int totalNum[12]; // number of faces with a given priority + int totalDistance[12]; // sum of distances to faces of a given priority + int totalMappedNum[18]; // number of faces with a given adjusted priority + int min10; // minimum distance to a face of priority 10 + int dfs[0]; // packed face id and distance, size 512 for small, 4096 for large +}; + +struct modelinfo { + int offset; // offset into buffer + int uvOffset; // offset into uv buffer + int size; // length in faces + int idx; // write idx in target buffer + int flags; // radius, orientation + int x; // scene position x + int y; // scene position y + int z; // scene position z +}; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl new file mode 100644 index 0000000000..f499599cd2 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/common.cl @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2021, 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. + */ + +#define PI 3.1415926535897932384626433832795f +#define UNIT PI / 1024.0f + +float3 toScreen(int4 vertex, int cameraYaw, int cameraPitch, int centerX, int centerY, int zoom) { + float yawSin = sin(cameraYaw * UNIT); + float yawCos = cos(cameraYaw * UNIT); + + float pitchSin = sin(cameraPitch * UNIT); + float pitchCos = cos(cameraPitch * UNIT); + + float rotatedX = (vertex.z * yawSin) + (vertex.x * yawCos); + float rotatedZ = (vertex.z * yawCos) - (vertex.x * yawSin); + + float var13 = (vertex.y * pitchCos) - (rotatedZ * pitchSin); + float var12 = (vertex.y * pitchSin) + (rotatedZ * pitchCos); + + float x = rotatedX * zoom / var12 + centerX; + float y = var13 * zoom / var12 + centerY; + float z = -var12; // in OpenGL depth is negative + + return (float3) (x, y, z); +} + +/* + * Rotate a vertex by a given orientation in JAU + */ +int4 rotate_vertex(__constant struct uniform *uni, int4 vertex, int orientation) { + int4 sinCos = uni->sinCosTable[orientation]; + int s = sinCos.x; + int c = sinCos.y; + int x = vertex.z * s + vertex.x * c >> 16; + int z = vertex.z * c - vertex.x * s >> 16; + return (int4)(x, vertex.y, z, vertex.w); +} + +/* + * Calculate the distance to a vertex given the camera angle + */ +int vertex_distance(int4 vertex, int cameraYaw, int cameraPitch) { + int yawSin = (int)(65536.0f * sin(cameraYaw * UNIT)); + int yawCos = (int)(65536.0f * cos(cameraYaw * UNIT)); + + int pitchSin = (int)(65536.0f * sin(cameraPitch * UNIT)); + int pitchCos = (int)(65536.0f * cos(cameraPitch * UNIT)); + + int j = vertex.z * yawCos - vertex.x * yawSin >> 16; + int l = vertex.y * pitchSin + j * pitchCos >> 16; + + return l; +} + +/* + * Calculate the distance to a face + */ +int face_distance(int4 vA, int4 vB, int4 vC, int cameraYaw, int cameraPitch) { + int dvA = vertex_distance(vA, cameraYaw, cameraPitch); + int dvB = vertex_distance(vB, cameraYaw, cameraPitch); + int dvC = vertex_distance(vC, cameraYaw, cameraPitch); + int faceDistance = (dvA + dvB + dvC) / 3; + return faceDistance; +} + +/* + * Test if a face is visible (not backward facing) + */ +bool face_visible(__constant struct uniform *uni, int4 vA, int4 vB, int4 vC, int4 position) { + // Move model to scene location, and account for camera offset + int4 cameraPos = (int4)(uni->cameraX, uni->cameraY, uni->cameraZ, 0); + vA += position - cameraPos; + vB += position - cameraPos; + vC += position - cameraPos; + + float3 sA = toScreen(vA, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom); + float3 sB = toScreen(vB, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom); + float3 sC = toScreen(vC, uni->cameraYaw, uni->cameraPitch, uni->centerX, uni->centerY, uni->zoom); + + return (sA.x - sB.x) * (sC.y - sB.y) - (sC.x - sB.x) * (sA.y - sB.y) > 0; +} + diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl new file mode 100644 index 0000000000..1212123f25 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp.cl @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2021, 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. + */ + +#include FACE_COUNT + +#include cl_types.cl +#include to_screen.cl +#include common.cl +#include priority_render.cl + +__kernel +__attribute__((work_group_size_hint(256, 1, 1))) +void computeLarge( + __local struct shared_data *shared, + __global const struct modelinfo *ol, + __global const int4 *vb, + __global const int4 *tempvb, + __global const float4 *uv, + __global const float4 *tempuv, + __global int4 *vout, + __global float4 *uvout, + __constant struct uniform *uni) { + + size_t groupId = get_group_id(0); + size_t localId = get_local_id(0) * FACE_COUNT; + struct modelinfo minfo = ol[groupId]; + int4 pos = (int4)(minfo.x, minfo.y, minfo.z, 0); + + if (localId == 0) { + shared->min10 = 1600; + for (int i = 0; i < 12; ++i) { + shared->totalNum[i] = 0; + shared->totalDistance[i] = 0; + } + for (int i = 0; i < 18; ++i) { + shared->totalMappedNum[i] = 0; + } + } + + int prio[FACE_COUNT]; + int dis[FACE_COUNT]; + int4 v1[FACE_COUNT]; + int4 v2[FACE_COUNT]; + int4 v3[FACE_COUNT]; + + for (int i = 0; i < FACE_COUNT; i++) { + get_face(shared, uni, vb, tempvb, localId + i, minfo, uni->cameraYaw, uni->cameraPitch, &prio[i], &dis[i], &v1[i], &v2[i], &v3[i]); + } + + barrier(CLK_LOCAL_MEM_FENCE); + + for (int i = 0; i < FACE_COUNT; i++) { + add_face_prio_distance(shared, uni, localId + i, minfo, v1[i], v2[i], v3[i], prio[i], dis[i], pos); + } + + barrier(CLK_LOCAL_MEM_FENCE); + + int prioAdj[FACE_COUNT]; + int idx[FACE_COUNT]; + for (int i = 0; i < FACE_COUNT; i++) { + idx[i] = map_face_priority(shared, localId + i, minfo, prio[i], dis[i], &prioAdj[i]); + } + + barrier(CLK_LOCAL_MEM_FENCE); + + for (int i = 0; i < FACE_COUNT; i++) { + insert_dfs(shared, localId + i, minfo, prioAdj[i], dis[i], idx[i]); + } + + barrier(CLK_LOCAL_MEM_FENCE); + + for (int i = 0; i < FACE_COUNT; i++) { + sort_and_insert(shared, uv, tempuv, vout, uvout, localId + i, minfo, prioAdj[i], dis[i], v1[i], v2[i], v3[i]); + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl new file mode 100644 index 0000000000..436f9a7d72 --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/comp_unordered.cl @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2021, 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. + */ + +#include cl_types.cl + +__kernel +__attribute__((reqd_work_group_size(6, 1, 1))) +void computeUnordered(__global const struct modelinfo *ol, + __global const int4 *vb, + __global const int4 *tempvb, + __global const float4 *uv, + __global const float4 *tempuv, + __global int4 *vout, + __global float4 *uvout) { + size_t groupId = get_group_id(0); + size_t localId = get_local_id(0); + struct modelinfo minfo = ol[groupId]; + + int offset = minfo.offset; + int size = minfo.size; + int outOffset = minfo.idx; + int uvOffset = minfo.uvOffset; + int flags = minfo.flags; + int4 pos = (int4)(minfo.x, minfo.y, minfo.z, 0); + + if (localId >= size) { + return; + } + + uint ssboOffset = localId; + int4 thisA, thisB, thisC; + + // Grab triangle vertices from the correct buffer + if (flags < 0) { + thisA = vb[offset + ssboOffset * 3]; + thisB = vb[offset + ssboOffset * 3 + 1]; + thisC = vb[offset + ssboOffset * 3 + 2]; + } else { + thisA = tempvb[offset + ssboOffset * 3]; + thisB = tempvb[offset + ssboOffset * 3 + 1]; + thisC = tempvb[offset + ssboOffset * 3 + 2]; + } + + uint myOffset = localId; + + // position vertices in scene and write to out buffer + vout[outOffset + myOffset * 3] = pos + thisA; + vout[outOffset + myOffset * 3 + 1] = pos + thisB; + vout[outOffset + myOffset * 3 + 2] = pos + thisC; + + if (uvOffset < 0) { + uvout[outOffset + myOffset * 3] = (float4)(0.0f, 0.0f, 0.0f, 0.0f); + uvout[outOffset + myOffset * 3 + 1] = (float4)(0.0f, 0.0f, 0.0f, 0.0f); + uvout[outOffset + myOffset * 3 + 2] = (float4)(0.0f, 0.0f, 0.0f, 0.0f); + } else if (flags >= 0) { + uvout[outOffset + myOffset * 3] = tempuv[uvOffset + localId * 3]; + uvout[outOffset + myOffset * 3 + 1] = tempuv[uvOffset + localId * 3 + 1]; + uvout[outOffset + myOffset * 3 + 2] = tempuv[uvOffset + localId * 3 + 2]; + } else { + uvout[outOffset + myOffset * 3] = uv[uvOffset + localId * 3]; + uvout[outOffset + myOffset * 3 + 1] = uv[uvOffset + localId * 3 + 1]; + uvout[outOffset + myOffset * 3 + 2] = uv[uvOffset + localId * 3 + 2]; + } +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl new file mode 100644 index 0000000000..6f1a04470c --- /dev/null +++ b/runelite-client/src/main/resources/net/runelite/client/plugins/gpu/priority_render.cl @@ -0,0 +1,298 @@ +/* + * Copyright (c) 2021, 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. + */ + +// Calculate adjusted priority for a face with a given priority, distance, and +// model global min10 and face distance averages. This allows positioning faces +// with priorities 10/11 into the correct 'slots' resulting in 18 possible +// adjusted priorities +int priority_map(int p, int distance, int _min10, int avg1, int avg2, int avg3) { + // (10, 11) 0 1 2 (10, 11) 3 4 (10, 11) 5 6 7 8 9 (10, 11) + // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 + switch (p) { + case 0: return 2; + case 1: return 3; + case 2: return 4; + case 3: return 7; + case 4: return 8; + case 5: return 11; + case 6: return 12; + case 7: return 13; + case 8: return 14; + case 9: return 15; + case 10: + if (distance > avg1) { + return 0; + } else if (distance > avg2) { + return 5; + } else if (distance > avg3) { + return 9; + } else { + return 16; + } + case 11: + if (distance > avg1 && _min10 > avg1) { + return 1; + } else if (distance > avg2 && (_min10 > avg1 || _min10 > avg2)) { + return 6; + } else if (distance > avg3 && (_min10 > avg1 || _min10 > avg2 || _min10 > avg3)) { + return 10; + } else { + return 17; + } + default: + return -1; + } +} + +// calculate the number of faces with a lower adjusted priority than +// the given adjusted priority +int count_prio_offset(__local struct shared_data *shared, int priority) { + int total = 0; + switch (priority) { + case 17: + total += shared->totalMappedNum[16]; + case 16: + total += shared->totalMappedNum[15]; + case 15: + total += shared->totalMappedNum[14]; + case 14: + total += shared->totalMappedNum[13]; + case 13: + total += shared->totalMappedNum[12]; + case 12: + total += shared->totalMappedNum[11]; + case 11: + total += shared->totalMappedNum[10]; + case 10: + total += shared->totalMappedNum[9]; + case 9: + total += shared->totalMappedNum[8]; + case 8: + total += shared->totalMappedNum[7]; + case 7: + total += shared->totalMappedNum[6]; + case 6: + total += shared->totalMappedNum[5]; + case 5: + total += shared->totalMappedNum[4]; + case 4: + total += shared->totalMappedNum[3]; + case 3: + total += shared->totalMappedNum[2]; + case 2: + total += shared->totalMappedNum[1]; + case 1: + total += shared->totalMappedNum[0]; + case 0: + return total; + } +} + +void get_face( + __local struct shared_data *shared, + __constant struct uniform *uni, + __global const int4 *vb, + __global const int4 *tempvb, + uint localId, struct modelinfo minfo, int cameraYaw, int cameraPitch, + /* out */ int *prio, int *dis, int4 *o1, int4 *o2, int4 *o3) { + int size = minfo.size; + int offset = minfo.offset; + int flags = minfo.flags; + uint ssboOffset; + + if (localId < size) { + ssboOffset = localId; + } else { + ssboOffset = 0; + } + + int4 thisA; + int4 thisB; + int4 thisC; + + // Grab triangle vertices from the correct buffer + if (flags < 0) { + thisA = vb[offset + ssboOffset * 3]; + thisB = vb[offset + ssboOffset * 3 + 1]; + thisC = vb[offset + ssboOffset * 3 + 2]; + } else { + thisA = tempvb[offset + ssboOffset * 3]; + thisB = tempvb[offset + ssboOffset * 3 + 1]; + thisC = tempvb[offset + ssboOffset * 3 + 2]; + } + + if (localId < size) { + int radius = (flags & 0x7fffffff) >> 12; + int orientation = flags & 0x7ff; + + // rotate for model orientation + int4 thisrvA = rotate_vertex(uni, thisA, orientation); + int4 thisrvB = rotate_vertex(uni, thisB, orientation); + int4 thisrvC = rotate_vertex(uni, thisC, orientation); + + // calculate distance to face + int thisPriority = (thisA.w >> 16) & 0xff;// all vertices on the face have the same priority + int thisDistance; + if (radius == 0) { + thisDistance = 0; + } else { + thisDistance = face_distance(thisrvA, thisrvB, thisrvC, cameraYaw, cameraPitch) + radius; + } + + *o1 = thisrvA; + *o2 = thisrvB; + *o3 = thisrvC; + + *prio = thisPriority; + *dis = thisDistance; + } else { + *o1 = (int4)(0, 0, 0, 0); + *o2 = (int4)(0, 0, 0, 0); + *o3 = (int4)(0, 0, 0, 0); + *prio = 0; + *dis = 0; + } +} + +void add_face_prio_distance( + __local struct shared_data *shared, + __constant struct uniform *uni, + uint localId, struct modelinfo minfo, int4 thisrvA, int4 thisrvB, int4 thisrvC, int thisPriority, int thisDistance, int4 pos) { + if (localId < minfo.size) { + // if the face is not culled, it is calculated into priority distance averages + if (face_visible(uni, thisrvA, thisrvB, thisrvC, pos)) { + atomic_add(&shared->totalNum[thisPriority], 1); + atomic_add(&shared->totalDistance[thisPriority], thisDistance); + + // calculate minimum distance to any face of priority 10 for positioning the 11 faces later + if (thisPriority == 10) { + atomic_min(&shared->min10, thisDistance); + } + } + } +} + +int map_face_priority(__local struct shared_data *shared, uint localId, struct modelinfo minfo, int thisPriority, int thisDistance, int *prio) { + int size = minfo.size; + + // Compute average distances for 0/2, 3/4, and 6/8 + + if (localId < size) { + int avg1 = 0; + int avg2 = 0; + int avg3 = 0; + + if (shared->totalNum[1] > 0 || shared->totalNum[2] > 0) { + avg1 = (shared->totalDistance[1] + shared->totalDistance[2]) / (shared->totalNum[1] + shared->totalNum[2]); + } + + if (shared->totalNum[3] > 0 || shared->totalNum[4] > 0) { + avg2 = (shared->totalDistance[3] + shared->totalDistance[4]) / (shared->totalNum[3] + shared->totalNum[4]); + } + + if (shared->totalNum[6] > 0 || shared->totalNum[8] > 0) { + avg3 = (shared->totalDistance[6] + shared->totalDistance[8]) / (shared->totalNum[6] + shared->totalNum[8]); + } + + int adjPrio = priority_map(thisPriority, thisDistance, shared->min10, avg1, avg2, avg3); + int prioIdx = atomic_add(&shared->totalMappedNum[adjPrio], 1); + + *prio = adjPrio; + return prioIdx; + } + + *prio = 0; + return 0; +} + +void insert_dfs(__local struct shared_data *shared, uint localId, struct modelinfo minfo, int adjPrio, int distance, int prioIdx) { + int size = minfo.size; + + if (localId < size) { + // calculate base offset into dfs based on number of faces with a lower priority + int baseOff = count_prio_offset(shared, adjPrio); + // store into face array offset array by unique index + shared->dfs[baseOff + prioIdx] = ((int) localId << 16) | distance; + } +} + +void sort_and_insert( + __local struct shared_data *shared, + __global const float4 *uv, + __global const float4 *tempuv, + __global int4 *vout, + __global float4 *uvout, + uint localId, struct modelinfo minfo, int thisPriority, int thisDistance, int4 thisrvA, int4 thisrvB, int4 thisrvC) { + /* compute face distance */ + int size = minfo.size; + + if (localId < size) { + int outOffset = minfo.idx; + int uvOffset = minfo.uvOffset; + int flags = minfo.flags; + int4 pos = (int4)(minfo.x, minfo.y, minfo.z, 0); + + const int priorityOffset = count_prio_offset(shared, thisPriority); + const int numOfPriority = shared->totalMappedNum[thisPriority]; + int start = priorityOffset; // index of first face with this priority + int end = priorityOffset + numOfPriority; // index of last face with this priority + int myOffset = priorityOffset; + + // we only have to order faces against others of the same priority + // calculate position this face will be in + for (int i = start; i < end; ++i) { + int d1 = shared->dfs[i]; + int theirId = d1 >> 16; + int theirDistance = d1 & 0xffff; + + // the closest faces draw last, so have the highest index + // if two faces have the same distance, the one with the + // higher id draws last + if ((theirDistance > thisDistance) + || (theirDistance == thisDistance && theirId < localId)) { + ++myOffset; + } + } + + // position vertices in scene and write to out buffer + vout[outOffset + myOffset * 3] = pos + thisrvA; + vout[outOffset + myOffset * 3 + 1] = pos + thisrvB; + vout[outOffset + myOffset * 3 + 2] = pos + thisrvC; + + if (uvOffset < 0) { + uvout[outOffset + myOffset * 3] = (float4)(0, 0, 0, 0); + uvout[outOffset + myOffset * 3 + 1] = (float4)(0, 0, 0, 0); + uvout[outOffset + myOffset * 3 + 2] = (float4)(0, 0, 0, 0); + } else if (flags >= 0) { + uvout[outOffset + myOffset * 3] = tempuv[uvOffset + localId * 3]; + uvout[outOffset + myOffset * 3 + 1] = tempuv[uvOffset + localId * 3 + 1]; + uvout[outOffset + myOffset * 3 + 2] = tempuv[uvOffset + localId * 3 + 2]; + } else { + uvout[outOffset + myOffset * 3] = uv[uvOffset + localId * 3]; + uvout[outOffset + myOffset * 3 + 1] = uv[uvOffset + localId * 3 + 1]; + uvout[outOffset + myOffset * 3 + 2] = uv[uvOffset + localId * 3 + 2]; + } + } +} From 5545b9d0ad020912569f5add959838c145965c21 Mon Sep 17 00:00:00 2001 From: buttonpoker Date: Fri, 12 Feb 2021 17:16:44 -0800 Subject: [PATCH 098/133] cannon: Remove Mourner Headquarters elf cannon spot (#13196) Following the release of the Song of the Elves quest, the mourners found in the Mourner Headquarters can no longer be hit by a cannon before, during, or after completion of the quest. [This is evidently a known issue with multi-NPCs.][1] [1]: https://twitter.com/JagexAsh/status/1359968976760094721 --- .../java/net/runelite/client/plugins/cannon/CannonSpots.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java index f5b6f4a4a2..4596cd4e35 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonSpots.java @@ -49,7 +49,7 @@ enum CannonSpots DUST_DEVIL(new WorldPoint(3218, 9366, 0)), EARTH_WARRIOR(new WorldPoint(3120, 9987, 0)), ELDER_CHAOS_DRUID(new WorldPoint(3237, 3622, 0)), - ELVES(new WorldPoint(2044, 4635, 0), new WorldPoint(3278, 6098, 0)), + ELVES(new WorldPoint(3278, 6098, 0)), FIRE_GIANTS(new WorldPoint(2393, 9782, 0), new WorldPoint(2412, 9776, 0), new WorldPoint(2401, 9780, 0), new WorldPoint(3047, 10340, 0)), GREATER_DEMONS(new WorldPoint(1435, 10086, 2), new WorldPoint(3224, 10132, 0)), GREEN_DRAGON(new WorldPoint(3225, 10068, 0)), From 77757229a5c671b3230044eef77cc9c5ce20bb3c Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 13 Feb 2021 09:50:12 -0500 Subject: [PATCH 099/133] entity hider: add option to hide ignored players This is diabled in pvp to avoid abuse. This additionally allows the "hide" options to be set without hiding others now. --- .../main/java/net/runelite/api/Client.java | 11 ++++- .../entityhider/EntityHiderConfig.java | 39 +++++++++++------ .../entityhider/EntityHiderPlugin.java | 43 +++++-------------- 3 files changed, 45 insertions(+), 48 deletions(-) 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 c665139862..77f3903001 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1475,7 +1475,7 @@ public interface Client extends GameEngine * * @param state the new player hidden state */ - void setPlayersHidden(boolean state); + void setOthersHidden(boolean state); /** * Sets whether 2D sprites related to the other players are hidden. @@ -1483,7 +1483,7 @@ public interface Client extends GameEngine * * @param state the new player 2D hidden state */ - void setPlayersHidden2D(boolean state); + void setOthersHidden2D(boolean state); /** * Sets whether or not friends are hidden. @@ -1499,6 +1499,13 @@ public interface Client extends GameEngine */ void setFriendsChatMembersHidden(boolean state); + /** + * Sets whether or not ignored players are hidden. + * + * @param state the new ignored player hidden state + */ + void setIgnoresHidden(boolean state); + /** * Sets whether the local player is hidden. * diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderConfig.java index d3ee118b8c..a18c211da3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderConfig.java @@ -29,16 +29,18 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; -@ConfigGroup("entityhider") +@ConfigGroup(EntityHiderConfig.GROUP) public interface EntityHiderConfig extends Config { + String GROUP = "entityhider"; + @ConfigItem( position = 1, keyName = "hidePlayers", - name = "Hide Players", - description = "Configures whether or not players are hidden" + name = "Hide Others", + description = "Configures whether or not other players are hidden" ) - default boolean hidePlayers() + default boolean hideOthers() { return true; } @@ -46,10 +48,10 @@ public interface EntityHiderConfig extends Config @ConfigItem( position = 2, keyName = "hidePlayers2D", - name = "Hide Players 2D", - description = "Configures whether or not players 2D elements are hidden" + name = "Hide Others 2D", + description = "Configures whether or not other players 2D elements are hidden" ) - default boolean hidePlayers2D() + default boolean hideOthers2D() { return true; } @@ -78,6 +80,17 @@ public interface EntityHiderConfig extends Config @ConfigItem( position = 5, + keyName = "hideIgnores", + name = "Hide Ignores", + description = "Configures whether or not ignored players are hidden" + ) + default boolean hideIgnores() + { + return false; + } + + @ConfigItem( + position = 6, keyName = "hideLocalPlayer", name = "Hide Local Player", description = "Configures whether or not the local player is hidden" @@ -88,7 +101,7 @@ public interface EntityHiderConfig extends Config } @ConfigItem( - position = 6, + position = 7, keyName = "hideLocalPlayer2D", name = "Hide Local Player 2D", description = "Configures whether or not the local player's 2D elements are hidden" @@ -99,7 +112,7 @@ public interface EntityHiderConfig extends Config } @ConfigItem( - position = 7, + position = 8, keyName = "hideNPCs", name = "Hide NPCs", description = "Configures whether or not NPCs are hidden" @@ -110,7 +123,7 @@ public interface EntityHiderConfig extends Config } @ConfigItem( - position = 8, + position = 9, keyName = "hideNPCs2D", name = "Hide NPCs 2D", description = "Configures whether or not NPCs 2D elements are hidden" @@ -121,7 +134,7 @@ public interface EntityHiderConfig extends Config } @ConfigItem( - position = 9, + position = 10, keyName = "hidePets", name = "Hide Pets", description = "Configures whether or not other player pets are hidden" @@ -132,7 +145,7 @@ public interface EntityHiderConfig extends Config } @ConfigItem( - position = 10, + position = 11, keyName = "hideAttackers", name = "Hide Attackers", description = "Configures whether or not NPCs/players attacking you are hidden" @@ -143,7 +156,7 @@ public interface EntityHiderConfig extends Config } @ConfigItem( - position = 11, + position = 12, keyName = "hideProjectiles", name = "Hide Projectiles", description = "Configures whether or not projectiles are hidden" diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderPlugin.java index 155bc6cc38..79a9dc160f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/entityhider/EntityHiderPlugin.java @@ -28,13 +28,9 @@ package net.runelite.client.plugins.entityhider; import com.google.inject.Provides; import javax.inject.Inject; import net.runelite.api.Client; -import net.runelite.api.GameState; -import net.runelite.api.Player; -import net.runelite.api.coords.WorldPoint; -import net.runelite.client.events.ConfigChanged; -import net.runelite.api.events.GameStateChanged; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.events.ConfigChanged; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; @@ -67,27 +63,22 @@ public class EntityHiderPlugin extends Plugin @Subscribe public void onConfigChanged(ConfigChanged e) { - updateConfig(); - } - - @Subscribe - public void onGameStateChanged(GameStateChanged event) - { - if (event.getGameState() == GameState.LOGGED_IN) + if (e.getGroup().equals(EntityHiderConfig.GROUP)) { - client.setIsHidingEntities(isPlayerRegionAllowed()); + updateConfig(); } } private void updateConfig() { - client.setIsHidingEntities(isPlayerRegionAllowed()); + client.setIsHidingEntities(true); - client.setPlayersHidden(config.hidePlayers()); - client.setPlayersHidden2D(config.hidePlayers2D()); + client.setOthersHidden(config.hideOthers()); + client.setOthersHidden2D(config.hideOthers2D()); client.setFriendsHidden(config.hideFriends()); client.setFriendsChatMembersHidden(config.hideFriendsChatMembers()); + client.setIgnoresHidden(config.hideIgnores()); client.setLocalPlayerHidden(config.hideLocalPlayer()); client.setLocalPlayerHidden2D(config.hideLocalPlayer2D()); @@ -107,11 +98,12 @@ public class EntityHiderPlugin extends Plugin { client.setIsHidingEntities(false); - client.setPlayersHidden(false); - client.setPlayersHidden2D(false); + client.setOthersHidden(false); + client.setOthersHidden2D(false); client.setFriendsHidden(false); client.setFriendsChatMembersHidden(false); + client.setIgnoresHidden(false); client.setLocalPlayerHidden(false); client.setLocalPlayerHidden2D(false); @@ -125,19 +117,4 @@ public class EntityHiderPlugin extends Plugin client.setProjectilesHidden(false); } - - private boolean isPlayerRegionAllowed() - { - final Player localPlayer = client.getLocalPlayer(); - - if (localPlayer == null) - { - return true; - } - - final int playerRegionID = WorldPoint.fromLocalInstance(client, localPlayer.getLocalLocation()).getRegionID(); - - // 9520 = Castle Wars - return playerRegionID != 9520; - } } From fadfc15cd08a182702865319da38a047f21d8046 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Sat, 6 Feb 2021 14:10:28 -0800 Subject: [PATCH 100/133] cluescrolls: Fix beginner/master clue step reset Commit 36bb7c8401fdd9ae88a185e9e193b4f7183aed54 improved the clue scroll plugin by resetting the active clue step when a beginner or master clue item was displayed in the chat dialog when receiving a new step. This commit caused a new bug, however, in that it became possible to click on a new clue step while the new step dialog was open such that the new step dialog persisted through a GameTick due to client-server delay, causing the saved clue item ID to be lost. This commit fixes that issue by resetting only the active clue object, but not the recorded clue item ID, as beginner and master clues only use a single item ID. --- .../runelite/client/plugins/cluescrolls/ClueScrollPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java index da3a95a301..7a3ee36e66 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java @@ -580,7 +580,7 @@ public class ClueScrollPlugin extends Plugin if (chatDialogClueItem != null && (chatDialogClueItem.getItemId() == ItemID.CLUE_SCROLL_BEGINNER || chatDialogClueItem.getItemId() == ItemID.CLUE_SCROLL_MASTER)) { - resetClue(true); + resetClue(false); } final Widget clueScrollText = client.getWidget(WidgetInfo.CLUE_SCROLL_TEXT); From 0fa078d8f1b83494412a9345ca1b8129d01bf131 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Sat, 6 Feb 2021 17:19:40 -0800 Subject: [PATCH 101/133] notifier: Add username to tray notification title This also removes the player's ingame name from notifications which are referring to the active player in favor of the title. --- .../java/net/runelite/client/Notifier.java | 20 +++++++++++++++- .../client/plugins/fishing/FishingPlugin.java | 2 +- .../grounditems/GroundItemsPlugin.java | 6 +---- .../idlenotifier/IdleNotifierPlugin.java | 24 +++++++++---------- .../plugins/regenmeter/RegenMeterPlugin.java | 2 +- .../grounditems/GroundItemsPluginTest.java | 10 ++------ .../idlenotifier/IdleNotifierPluginTest.java | 13 ++++------ 7 files changed, 41 insertions(+), 36 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/Notifier.java b/runelite-client/src/main/java/net/runelite/client/Notifier.java index 8184cf9f98..53963e4b1b 100644 --- a/runelite-client/src/main/java/net/runelite/client/Notifier.java +++ b/runelite-client/src/main/java/net/runelite/client/Notifier.java @@ -59,6 +59,7 @@ import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.Constants; import net.runelite.api.GameState; +import net.runelite.api.Player; import net.runelite.client.chat.ChatColorType; import net.runelite.client.chat.ChatMessageBuilder; import net.runelite.client.chat.ChatMessageManager; @@ -175,7 +176,7 @@ public class Notifier if (runeLiteConfig.enableTrayNotifications()) { - sendNotification(appName, message, type); + sendNotification(buildTitle(), message, type); } switch (runeLiteConfig.notificationSound()) @@ -210,6 +211,23 @@ public class Notifier log.debug(message); } + private String buildTitle() + { + Player player = client.getLocalPlayer(); + if (player == null) + { + return appName; + } + + String name = player.getName(); + if (Strings.isNullOrEmpty(name)) + { + return appName; + } + + return appName + " - " + name; + } + public void processFlash(final Graphics2D graphics) { FlashNotification flashNotification = runeLiteConfig.flashNotification(); 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 0c5fa99dc2..e782b81428 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 @@ -377,7 +377,7 @@ public class FishingPlugin extends Plugin { if (!trawlerNotificationSent) { - notifier.notify("[" + client.getLocalPlayer().getName() + "] has low Fishing Trawler activity!"); + notifier.notify("You have low Fishing Trawler activity!"); trawlerNotificationSent = true; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java index e7e79b5186..b7a8fc6500 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java @@ -59,7 +59,6 @@ import net.runelite.api.ItemContainer; import net.runelite.api.ItemID; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; -import net.runelite.api.Player; import net.runelite.api.Tile; import net.runelite.api.TileItem; import net.runelite.api.coords.WorldPoint; @@ -643,11 +642,8 @@ public class GroundItemsPlugin extends Plugin return; } - final Player local = client.getLocalPlayer(); final StringBuilder notificationStringBuilder = new StringBuilder() - .append('[') - .append(local.getName()) - .append("] received a ") + .append("You received a ") .append(dropType) .append(" drop: ") .append(item.getName()); 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 79ca4413e3..6d7dcec442 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 @@ -438,64 +438,64 @@ public class IdleNotifierPlugin extends Plugin if (config.logoutIdle() && checkIdleLogout()) { - notifier.notify("[" + local.getName() + "] is about to log out from idling too long!"); + notifier.notify("You are about to log out from idling too long!"); } if (check6hrLogout()) { - notifier.notify("[" + local.getName() + "] is about to log out from being online for 6 hours!"); + notifier.notify("You are about to log out from being online for 6 hours!"); } if (config.animationIdle() && checkAnimationIdle(waitDuration, local)) { - notifier.notify("[" + local.getName() + "] is now idle!"); + notifier.notify("You are now idle!"); } if (config.movementIdle() && checkMovementIdle(waitDuration, local)) { - notifier.notify("[" + local.getName() + "] has stopped moving!"); + notifier.notify("You have stopped moving!"); } if (config.interactionIdle() && checkInteractionIdle(waitDuration, local)) { if (lastInteractWasCombat) { - notifier.notify("[" + local.getName() + "] is now out of combat!"); + notifier.notify("You are now out of combat!"); } else { - notifier.notify("[" + local.getName() + "] is now idle!"); + notifier.notify("You are now idle!"); } } if (checkLowHitpoints()) { - notifier.notify("[" + local.getName() + "] has low hitpoints!"); + notifier.notify("You have low hitpoints!"); } if (checkLowPrayer()) { - notifier.notify("[" + local.getName() + "] has low prayer!"); + notifier.notify("You have low prayer!"); } if (checkLowEnergy()) { - notifier.notify("[" + local.getName() + "] has low run energy!"); + notifier.notify("You have low run energy!"); } if (checkHighEnergy()) { - notifier.notify("[" + local.getName() + "] has restored run energy!"); + notifier.notify("You have restored run energy!"); } if (checkLowOxygen()) { - notifier.notify("[" + local.getName() + "] has low oxygen!"); + notifier.notify("You have low oxygen!"); } if (checkFullSpecEnergy()) { - notifier.notify("[" + local.getName() + "] has restored spec energy!"); + notifier.notify("You have restored spec energy!"); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java index 6339ce5486..2160148401 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java @@ -166,7 +166,7 @@ public class RegenMeterPlugin extends Plugin if (config.getNotifyBeforeHpRegenSeconds() > 0 && currentHP < maxHP && shouldNotifyHpRegenThisTick(ticksPerHPRegen)) { - notifier.notify("[" + client.getLocalPlayer().getName() + "] regenerates their next hitpoint soon!"); + notifier.notify("Your next hitpoint will regenerate soon!"); } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/grounditems/GroundItemsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/grounditems/GroundItemsPluginTest.java index bc9c2536a1..48d6296ca5 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/grounditems/GroundItemsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/grounditems/GroundItemsPluginTest.java @@ -109,13 +109,7 @@ public class GroundItemsPluginTest return null; }).when(executor).execute(any(Runnable.class)); - when(client.getLocalPlayer()).then(a -> - { - Player player = mock(Player.class); - when(player.getName()).thenReturn("Adam"); - return player; - }); - + when(client.getLocalPlayer()).thenReturn(mock(Player.class)); when(config.getHiddenItems()).thenReturn(""); } @@ -149,6 +143,6 @@ public class GroundItemsPluginTest groundItemsPlugin.onItemSpawned(new ItemSpawned(tile, tileItem)); - verify(notifier).notify("[Adam] received a highlighted drop: Abyssal whip"); + verify(notifier).notify("You received a highlighted drop: Abyssal whip"); } } \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java index 59c84d3086..bb91fffcb8 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java @@ -62,8 +62,6 @@ import org.mockito.junit.MockitoJUnitRunner; @RunWith(MockitoJUnitRunner.class) public class IdleNotifierPluginTest { - private static final String PLAYER_NAME = "Deathbeam"; - @Mock @Bind private Client client; @@ -116,7 +114,6 @@ public class IdleNotifierPluginTest when(fishingSpot.getName()).thenReturn("Fishing spot"); // Mock player - when(player.getName()).thenReturn(PLAYER_NAME); when(player.getAnimation()).thenReturn(AnimationID.IDLE); when(client.getLocalPlayer()).thenReturn(player); @@ -145,7 +142,7 @@ public class IdleNotifierPluginTest when(player.getAnimation()).thenReturn(AnimationID.IDLE); plugin.onAnimationChanged(animationChanged); plugin.onGameTick(new GameTick()); - verify(notifier).notify("[" + PLAYER_NAME + "] is now idle!"); + verify(notifier).notify("You are now idle!"); } @Test @@ -201,7 +198,7 @@ public class IdleNotifierPluginTest when(player.getInteracting()).thenReturn(null); plugin.onInteractingChanged(new InteractingChanged(player, null)); plugin.onGameTick(new GameTick()); - verify(notifier).notify("[" + PLAYER_NAME + "] is now out of combat!"); + verify(notifier).notify("You are now out of combat!"); } @Test @@ -292,7 +289,7 @@ public class IdleNotifierPluginTest plugin.onInteractingChanged(new InteractingChanged(player, null)); plugin.onGameTick(new GameTick()); - verify(notifier).notify("[" + PLAYER_NAME + "] is now idle!"); + verify(notifier).notify("You are now idle!"); } @Test @@ -306,7 +303,7 @@ public class IdleNotifierPluginTest when(client.getVar(eq(VarPlayer.SPECIAL_ATTACK_PERCENT))).thenReturn(500); // 50% plugin.onGameTick(new GameTick()); - verify(notifier).notify(eq("[" + PLAYER_NAME + "] has restored spec energy!")); + verify(notifier).notify(eq("You have restored spec energy!")); } @Test @@ -321,6 +318,6 @@ public class IdleNotifierPluginTest // No movement here plugin.onGameTick(new GameTick()); - verify(notifier).notify(eq("[" + PLAYER_NAME + "] has stopped moving!")); + verify(notifier).notify(eq("You have stopped moving!")); } } \ No newline at end of file From 63cbe6ae09f926669d20810db8ca3715b1138296 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Thu, 11 Feb 2021 15:45:33 +0000 Subject: [PATCH 102/133] chat color: fix usernames not being recoloured if they have an icon --- .../client/chat/ChatMessageManager.java | 4 +- .../client/chat/ChatMessageManagerTest.java | 64 +++++++++++++++++++ 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java index b1795db6ac..6e888ce8f1 100644 --- a/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java +++ b/runelite-client/src/main/java/net/runelite/client/chat/ChatMessageManager.java @@ -54,6 +54,7 @@ import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; import net.runelite.client.ui.JagexColors; import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.Text; @Singleton public class ChatMessageManager @@ -125,7 +126,8 @@ public class ChatMessageManager case PUBLICCHAT: case MODCHAT: { - boolean isFriend = client.isFriended(chatMessage.getName(), true) && !client.getLocalPlayer().getName().equals(chatMessage.getName()); + String sanitizedUsername = Text.removeTags(chatMessage.getName()); + boolean isFriend = client.isFriended(sanitizedUsername, true) && !client.getLocalPlayer().getName().equals(sanitizedUsername); if (isFriend) { diff --git a/runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java b/runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java index 24e3539106..823290e730 100644 --- a/runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java @@ -32,6 +32,7 @@ import java.awt.Color; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.MessageNode; +import net.runelite.api.Player; import net.runelite.api.events.ChatMessage; import net.runelite.client.config.ChatColorConfig; import org.junit.Before; @@ -84,4 +85,67 @@ public class ChatMessageManagerTest verify(messageNode).setValue(eq("Your dodgy necklace protects you. It has 1 charge left.")); } + + @Test + public void testPublicFriendUsernameRecolouring() + { + final String localPlayerName = "RuneLite"; + final String friendName = "Zezima"; + + when(chatColorConfig.opaquePublicFriendUsernames()).thenReturn(Color.decode("#b20000")); + + chatMessageManager.loadColors(); + + // Setup message + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setName(friendName); + + MessageNode messageNode = mock(MessageNode.class); + chatMessage.setMessageNode(messageNode); + when(messageNode.getName()).thenReturn(friendName); + + // Setup friend checking + Player localPlayer = mock(Player.class); + + when(client.isFriended(friendName, true)).thenReturn(true); + when(client.getLocalPlayer()).thenReturn(localPlayer); + when(localPlayer.getName()).thenReturn(localPlayerName); + + chatMessageManager.onChatMessage(chatMessage); + + verify(messageNode).setName("" + friendName + ""); + } + + @Test + public void testPublicIronmanFriendUsernameRecolouring() + { + final String localPlayerName = "RuneLite"; + final String friendName = "BuddhaPuck"; + final String sanitizedFriendName = "BuddhaPuck"; + + when(chatColorConfig.opaquePublicFriendUsernames()).thenReturn(Color.decode("#b20000")); + + chatMessageManager.loadColors(); + + // Setup message + ChatMessage chatMessage = new ChatMessage(); + chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setName(friendName); + + MessageNode messageNode = mock(MessageNode.class); + chatMessage.setMessageNode(messageNode); + when(messageNode.getName()).thenReturn(friendName); + + // Setup friend checking + Player localPlayer = mock(Player.class); + + when(client.isFriended(sanitizedFriendName, true)).thenReturn(true); + when(client.getLocalPlayer()).thenReturn(localPlayer); + when(localPlayer.getName()).thenReturn(localPlayerName); + + chatMessageManager.onChatMessage(chatMessage); + + verify(messageNode).setName("" + friendName + ""); + } } \ No newline at end of file From 5e14d666e00b661d71b97c53efda402c24c6aa58 Mon Sep 17 00:00:00 2001 From: Hydrox6 Date: Sat, 13 Feb 2021 15:57:00 +0000 Subject: [PATCH 103/133] chat color: fix nonsensical test --- .../net/runelite/client/chat/ChatMessageManagerTest.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java b/runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java index 823290e730..b75c79b277 100644 --- a/runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java +++ b/runelite-client/src/test/java/net/runelite/client/chat/ChatMessageManagerTest.java @@ -38,7 +38,6 @@ import net.runelite.client.config.ChatColorConfig; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; -import static org.mockito.ArgumentMatchers.eq; import org.mockito.Mock; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; @@ -68,14 +67,14 @@ public class ChatMessageManagerTest } @Test - public void onChatMessage() + public void testMessageRecoloring() { - when(chatColorConfig.opaquePublicChat()).thenReturn(Color.decode("#b20000")); + when(chatColorConfig.opaqueServerMessage()).thenReturn(Color.decode("#b20000")); chatMessageManager.loadColors(); ChatMessage chatMessage = new ChatMessage(); - chatMessage.setType(ChatMessageType.PUBLICCHAT); + chatMessage.setType(ChatMessageType.GAMEMESSAGE); MessageNode messageNode = mock(MessageNode.class); chatMessage.setMessageNode(messageNode); @@ -83,7 +82,7 @@ public class ChatMessageManagerTest when(messageNode.getValue()).thenReturn("Your dodgy necklace protects you. It has 1 charge left."); chatMessageManager.onChatMessage(chatMessage); - verify(messageNode).setValue(eq("Your dodgy necklace protects you. It has 1 charge left.")); + verify(messageNode).setValue("Your dodgy necklace protects you. It has 1 charge left."); } @Test From 778dc07181839faec0415e351e033b572c8f1289 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 13 Feb 2021 11:21:07 -0500 Subject: [PATCH 104/133] Bump to 1.7.0-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 2 +- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 782e4408cb..206c42e1ed 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 20d0eb865c..2a1bc72387 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 305b0e3b7f..62d2513870 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index e864273049..936a689ced 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 4adddc284a..e0e383d518 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index 9ee7f55c10..62fb44946e 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT pom RuneLite diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 6cc0d185cc..dd96cd1c8c 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 48eb8825ce..b8e92e238d 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 016b2d854f..3df1814484 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.40-SNAPSHOT + 1.7.0-SNAPSHOT script-assembler-plugin From 2cdfdf0284bbb36012df5505c2ec684bc0c795ea Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Sat, 30 Jan 2021 15:22:03 -0800 Subject: [PATCH 105/133] cluescrolls: Re-check named object highlights after hopping --- .../client/plugins/cluescrolls/ClueScrollPlugin.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java index 7a3ee36e66..d33a0e6b29 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/ClueScrollPlugin.java @@ -495,6 +495,10 @@ public class ClueScrollPlugin extends Plugin { resetClue(true); } + else if (state == GameState.HOPPING) + { + namedObjectCheckThisTick = true; + } } @Subscribe @@ -561,6 +565,7 @@ public class ClueScrollPlugin extends Plugin } // Load the current plane's tiles if a tick has elapsed since the player has changed planes + // or upon reaching a logged in state after hopping worlds if (namedObjectCheckThisTick) { namedObjectCheckThisTick = false; From 826f99679c572d69bdbd51bf37f82c96de6de9f1 Mon Sep 17 00:00:00 2001 From: Cyborger1 <45152844+Cyborger1@users.noreply.github.com> Date: Fri, 12 Feb 2021 22:36:45 -0800 Subject: [PATCH 106/133] emoji: Replace spoon and fork-and-knife emojis Without a shadow, these emojis were nearly unreadable on the fixed chat background. These hand-drawn replacements are detailed enough to be clearly understood against all kinds of backgrounds. --- .../client/plugins/emojis/fork_and_knife.png | Bin 116 -> 134 bytes .../runelite/client/plugins/emojis/spoon.png | Bin 120 -> 129 bytes 2 files changed, 0 insertions(+), 0 deletions(-) diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/emojis/fork_and_knife.png b/runelite-client/src/main/resources/net/runelite/client/plugins/emojis/fork_and_knife.png index f82589d6c3c51be592bc41ec81d12c3cf56593c3..6f1140081662f1d3c1906ba828a4cfc2d2b0ca57 100644 GIT binary patch delta 116 zcmXSEW1Jw7$HdIQz`)zopr01&qur~m)} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/emojis/spoon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/emojis/spoon.png index c838403711a6adfdc32576a74d6585c359cf2c56..dc40dd32d41911365fb6d9c5824fb0eaea2a923e 100644 GIT binary patch delta 99 zcmby6l(7tF>WNww+68Q~44r57>UoeBivm0qZj+&>7V+hCfwr2KJYD@<);T3K0RZdm BA5Z`Q delta 90 zcmZoo{Y09#VGgoZ|B1s8bNd^W6#*!evU Date: Sat, 13 Feb 2021 14:16:19 -0500 Subject: [PATCH 107/133] emoji plugin: set message node message instead of rl format message The replaced message has no rl-format tags, and using the rl-format message causes it to lose color whenever the message node is refreshed due to not containing any rl-format tags --- .../net/runelite/client/plugins/emojis/EmojiPlugin.java | 8 +------- .../runelite/client/plugins/emojis/EmojiPluginTest.java | 4 ++-- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java index e63a97c3cc..9fc33d8647 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java @@ -40,7 +40,6 @@ import net.runelite.api.events.ChatMessage; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.OverheadTextChanged; import net.runelite.client.callback.ClientThread; -import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; @@ -63,9 +62,6 @@ public class EmojiPlugin extends Plugin @Inject private ClientThread clientThread; - @Inject - private ChatMessageManager chatMessageManager; - private int modIconsStart = -1; @Override @@ -145,9 +141,7 @@ public class EmojiPlugin extends Plugin return; } - messageNode.setRuneLiteFormatMessage(updatedMessage); - chatMessageManager.update(messageNode); - client.refreshChat(); + messageNode.setValue(updatedMessage); } @Subscribe diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java index 326ea07a38..99fc2dc174 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/emojis/EmojiPluginTest.java @@ -89,7 +89,7 @@ public class EmojiPluginTest emojiPlugin.onChatMessage(chatMessage); - verify(messageNode).setRuneLiteFormatMessage(" "); + verify(messageNode).setValue(" "); } @Test @@ -113,7 +113,7 @@ public class EmojiPluginTest emojiPlugin.onChatMessage(chatMessage); - verify(messageNode).setRuneLiteFormatMessage(""); + verify(messageNode).setValue(""); } @Test From 3550d70e92fa501a430e1cbf1a74fe9bdfc21bb3 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Sat, 13 Feb 2021 21:33:06 +0000 Subject: [PATCH 108/133] if something is broken its probably this commit --- .../api/events/MenuOptionClicked.java | 11 ++++++ .../net/runelite/client/plugins/Plugin.java | 19 ++-------- .../client/plugins/PluginManager.java | 35 +++++++++++++++++-- .../main/java/net/runelite/rs/api/RSTile.java | 4 +++ 4 files changed, 50 insertions(+), 19 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java b/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java index 2cd15c48f9..4ff7b8138e 100644 --- a/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java +++ b/runelite-api/src/main/java/net/runelite/api/events/MenuOptionClicked.java @@ -26,6 +26,7 @@ package net.runelite.api.events; import lombok.Data; import net.runelite.api.MenuAction; +import net.runelite.api.MenuEntry; /** * An event where a menu option has been clicked. @@ -87,4 +88,14 @@ public class MenuOptionClicked { this.consumed = true; } + + public void setMenuEntry(MenuEntry entry) + { + this.setMenuOption(entry.getOption()); + this.setMenuTarget(entry.getTarget()); + this.setId(entry.getId()); + this.setMenuAction(MenuAction.of(entry.getOpcode())); + this.setActionParam(entry.getActionParam()); + this.setWidgetId(entry.getActionParam1()); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java index 415b9d47b1..80aafcc860 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java @@ -27,11 +27,13 @@ package net.runelite.client.plugins; import com.google.inject.Binder; import com.google.inject.Injector; import com.google.inject.Module; +import lombok.Getter; import org.pf4j.ExtensionPoint; import net.runelite.client.RuneLite; public abstract class Plugin implements Module, ExtensionPoint { + @Getter protected Injector injector; @Override @@ -51,23 +53,6 @@ public abstract class Plugin implements Module, ExtensionPoint { } - // This should never be null when we are using it - public final Injector getInjector() - { - if (injector == null) - { - Module pluginModule = (Binder binder) -> - { - binder.bind((Class) this.getClass()).toInstance(this); - binder.install(this); - }; - Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule); - pluginInjector.injectMembers(this); - injector = pluginInjector; - } - return injector; - } - public String getName() { return getClass().getAnnotation(PluginDescriptor.class).name(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index 3ab83b3fd4..500bd48000 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -164,7 +164,20 @@ public class PluginManager try { Injector injector = plugin.getInjector(); - + if (injector == null) + { + // Create injector for the module + Module pluginModule = (Binder binder) -> + { + // Since the plugin itself is a module, it won't bind itself, so we'll bind it here + binder.bind((Class) plugin.getClass()).toInstance(plugin); + binder.install(plugin); + }; + Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule); + pluginInjector.injectMembers(plugin); + plugin.injector = pluginInjector; + injector = pluginInjector; + } for (Key key : injector.getBindings().keySet()) { Class type = key.getTypeLiteral().getRawType(); @@ -194,7 +207,25 @@ public class PluginManager plugins = getPlugins(); } plugins.forEach(pl -> - injectors.add(pl.getInjector())); + { + //TODO: Not sure why this is necessary but it is. The Injector isn't null when its handed off from our ExternalPluginManager. + // Hopefully we can figure out the root cause of the underlying issue. + if (pl.injector == null) + { + // Create injector for the module + Module pluginModule = (Binder binder) -> + { + // Since the plugin itself is a module, it won't bind itself, so we'll bind it here + binder.bind((Class) pl.getClass()).toInstance(pl); + binder.install(pl); + }; + Injector pluginInjector = RuneLite.getInjector().createChildInjector(pluginModule); + pluginInjector.injectMembers(pl); + pl.injector = pluginInjector; + } + + injectors.add(pl.getInjector()); + }); List list = new ArrayList<>(); for (Injector injector : injectors) diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java b/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java index 7dc5bee2f3..19182c5fb2 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSTile.java @@ -28,6 +28,10 @@ public interface RSTile extends Tile @Override GroundObject getGroundObject(); + @Import("floorDecoration") + @Override + void setGroundObject(GroundObject object); + @Import("boundaryObject") @Override WallObject getWallObject(); From c6dc52d940f0dec945de4749f7f6b9b64b52b736 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Sat, 13 Feb 2021 21:40:36 +0000 Subject: [PATCH 109/133] Release 1.7.0 --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 206c42e1ed..377c5e5832 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 2a1bc72387..366f8da7d2 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 62d2513870..3c47730b91 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 936a689ced..ae4dcc347a 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index e0e383d518..7c667af7db 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 Web Service diff --git a/pom.xml b/pom.xml index 62fb44946e..820414b6e5 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 pom RuneLite @@ -61,7 +61,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - HEAD + runelite-parent-1.7.0 diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index dd96cd1c8c..b89bcb6d22 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index b8e92e238d..5c06128f1b 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 3df1814484..a1cc42542b 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0-SNAPSHOT + 1.7.0 script-assembler-plugin From 1684eccd006560f984474ffea1248dd8666df78c Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Sat, 13 Feb 2021 21:40:44 +0000 Subject: [PATCH 110/133] Bump for 1.7.1-SNAPSHOT --- cache-client/pom.xml | 2 +- cache-updater/pom.xml | 2 +- cache/pom.xml | 2 +- http-api/pom.xml | 2 +- http-service/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 377c5e5832..dda9747db8 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 366f8da7d2..daabcfeec2 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 3c47730b91..40d121afe9 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index ae4dcc347a..9ac04bacbf 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 7c667af7db..ac41551a3a 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index 820414b6e5..51d715e468 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT pom RuneLite @@ -61,7 +61,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - runelite-parent-1.7.0 + HEAD diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index b89bcb6d22..58aa0a549a 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 5c06128f1b..35d74b8b6f 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index a1cc42542b..17e6be9199 100644 --- a/runelite-script-assembler-plugin/pom.xml +++ b/runelite-script-assembler-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.7.0 + 1.7.1-SNAPSHOT script-assembler-plugin From 9704551c26761f3541177df8ab87be93b4a07af1 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Sun, 14 Feb 2021 01:28:33 +0000 Subject: [PATCH 111/133] make some decent mixins --- .../main/java/net/runelite/api/Client.java | 56 -------- runelite-client/runelite-client.gradle.kts | 3 + .../mixins/EntityHiderBridgeMixin.java | 127 +++--------------- .../net/runelite/mixins/EntityHiderMixin.java | 106 ++++++--------- .../net/runelite/rs/api/RSFriendSystem.java | 3 + 5 files changed, 62 insertions(+), 233 deletions(-) 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 bd6a38c60c..1a1ec1a921 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1608,62 +1608,6 @@ public interface Client extends GameEngine */ void setNPCsHidden(boolean state); - /** - * Increments the counter for how many times this npc has been selected to be hidden - * - * @param name npc name - */ - void addHiddenNpcName(String name); - - /** - * Decrements the counter for how many times this npc has been selected to be hidden - * - * @param name npc name - */ - void removeHiddenNpcName(String name); - - /** - * Forcibly unhides an npc by setting its counter to zero - * - * @param name npc name - */ - void forciblyUnhideNpcName(String name); - - /** - * Get the list of NPC indices that are currently hidden - * - * @return all of the current hidden NPC Indices - */ - List getHiddenNpcIndices(); - - /** - * If an NPC index is in this List then do not render it - * - * @param npcIndices the npc indices to hide - */ - void setHiddenNpcIndices(List npcIndices); - - /** - * Increments the counter for how many times this npc has been selected to be hidden on death - * - * @param name npc name - */ - void addHiddenNpcDeath(String name); - - /** - * Decrements the counter for how many times this npc has been selected to be hidden on death - * - * @param name npc name - */ - void removeHiddenNpcDeath(String name); - - /** - * Forcibly unhides a hidden-while-dead npc by setting its counter to zero - * - * @param name npc name - */ - void forciblyUnhideNpcDeath(String name); - /** * Sets whether 2D sprites (ie. overhead prayers) related to * the NPCs are hidden. diff --git a/runelite-client/runelite-client.gradle.kts b/runelite-client/runelite-client.gradle.kts index deccc7b0f3..cfc8667c28 100644 --- a/runelite-client/runelite-client.gradle.kts +++ b/runelite-client/runelite-client.gradle.kts @@ -83,6 +83,7 @@ dependencies { implementation(project(":http-api")) implementation(group = "net.runelite.gluegen", name = "gluegen-rt", version = "2.4.0-rc-20200429") implementation(group = "net.runelite.jogl", name = "jogl-all", version = "2.4.0-rc-20200429") + implementation(group = "net.runelite.jocl", name = "jocl", version = "1.0") runtimeOnly(group = "org.pushing-pixels", name = "radiance-trident", version = "2.5.1") runtimeOnly(project(":runescape-api")) @@ -94,6 +95,8 @@ dependencies { runtimeOnly(group = "net.runelite.jogl", name = "jogl-all", version = "2.4.0-rc-20200429", classifier = "natives-windows-amd64") runtimeOnly(group = "net.runelite.jogl", name = "jogl-all", version = "2.4.0-rc-20200429", classifier = "natives-windows-i586") runtimeOnly(group = "net.runelite.jogl", name = "jogl-all", version = "2.4.0-rc-20200429", classifier = "natives-macosx-universal") + runtimeOnly(group = "net.runelite.jocl", name = "jocl", version = "1.0", classifier = "macos-x64") + runtimeOnly(group = "net.runelite.jocl", name = "jocl", version = "1.0", classifier = "macos-arm64") testAnnotationProcessor(group = "org.projectlombok", name = "lombok", version = "1.18.16") diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderBridgeMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderBridgeMixin.java index 4d15ae5cf8..0327b9473e 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderBridgeMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderBridgeMixin.java @@ -41,10 +41,10 @@ public abstract class EntityHiderBridgeMixin implements RSClient public static boolean isHidingEntities; @Inject - public static boolean hidePlayers; + public static boolean hideOthers; @Inject - public static boolean hidePlayers2D; + public static boolean hideOthers2D; @Inject public static boolean hideFriends; @@ -52,6 +52,9 @@ public abstract class EntityHiderBridgeMixin implements RSClient @Inject public static boolean hideClanMates; + @Inject + public static boolean hideIgnores; + @Inject public static boolean hideLocalPlayer; @@ -76,21 +79,9 @@ public abstract class EntityHiderBridgeMixin implements RSClient @Inject public static boolean hideDeadNPCs; - @Inject - public static HashMap hiddenNpcsName = new HashMap<>(); - - @Inject - public static HashMap hiddenNpcsDeath = new HashMap<>(); - @Inject public static List hideSpecificPlayers = new ArrayList<>(); - @Inject - public static Set blacklistDeadNpcs = new HashSet<>(); - - @Inject - public static List hiddenNpcIndices = new ArrayList<>(); - @Inject @Override public void setIsHidingEntities(boolean state) @@ -100,16 +91,16 @@ public abstract class EntityHiderBridgeMixin implements RSClient @Inject @Override - public void setPlayersHidden(boolean state) + public void setOthersHidden(boolean state) { - hidePlayers = state; + hideOthers = state; } @Inject @Override - public void setPlayersHidden2D(boolean state) + public void setOthersHidden2D(boolean state) { - hidePlayers2D = state; + hideOthers2D = state; } @Inject @@ -126,6 +117,13 @@ public abstract class EntityHiderBridgeMixin implements RSClient hideClanMates = state; } + @Inject + @Override + public void setIgnoresHidden(boolean state) + { + hideIgnores = state; + } + @Inject @Override public void setLocalPlayerHidden(boolean state) @@ -154,78 +152,6 @@ public abstract class EntityHiderBridgeMixin implements RSClient hideNPCs2D = state; } - @Inject - @Override - public void addHiddenNpcName(String npc) - { - npc = npc.toLowerCase(); - int i = hiddenNpcsName.getOrDefault(npc, 0); - if (i == Integer.MAX_VALUE) - { - throw new RuntimeException("NPC name " + npc + " has been hidden Integer.MAX_VALUE times, is something wrong?"); - } - - hiddenNpcsName.put(npc, ++i); - } - - @Inject - @Override - public void removeHiddenNpcName(String npc) - { - npc = npc.toLowerCase(); - int i = hiddenNpcsName.getOrDefault(npc, 0); - if (i == 0) - { - return; - } - - hiddenNpcsName.put(npc, --i); - } - - @Inject - @Override - public void forciblyUnhideNpcName(String npc) - { - npc = npc.toLowerCase(); - hiddenNpcsName.put(npc, 0); - } - - @Inject - @Override - public void addHiddenNpcDeath(String npc) - { - npc = npc.toLowerCase(); - int i = hiddenNpcsDeath.getOrDefault(npc, 0); - if (i == Integer.MAX_VALUE) - { - throw new RuntimeException("NPC death " + npc + " has been hidden Integer.MAX_VALUE times, is something wrong?"); - } - - hiddenNpcsDeath.put(npc, ++i); - } - - @Inject - @Override - public void removeHiddenNpcDeath(String npc) - { - npc = npc.toLowerCase(); - int i = hiddenNpcsDeath.getOrDefault(npc, 0); - if (i == 0) - { - return; - } - - hiddenNpcsDeath.put(npc, --i); - } - - @Inject - @Override - public void forciblyUnhideNpcDeath(String npc) - { - npc = npc.toLowerCase(); - hiddenNpcsDeath.put(npc, 0); - } - @Inject @Override public void setHideSpecificPlayers(List players) @@ -233,13 +159,6 @@ public abstract class EntityHiderBridgeMixin implements RSClient hideSpecificPlayers = players; } - @Inject - @Override - public void setBlacklistDeadNpcs(Set blacklist) - { - blacklistDeadNpcs = blacklist; - } - @Inject @Override public void setPetsHidden(boolean state) @@ -267,18 +186,4 @@ public abstract class EntityHiderBridgeMixin implements RSClient { hideDeadNPCs = state; } - - @Inject - @Override - public void setHiddenNpcIndices(List npcIndices) - { - hiddenNpcIndices = npcIndices; - } - - @Inject - @Override - public List getHiddenNpcIndices() - { - return hiddenNpcIndices; - } } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderMixin.java index fc30ebb953..e99e8f9659 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderMixin.java @@ -36,10 +36,10 @@ import net.runelite.api.mixins.Shadow; import net.runelite.api.util.Text; import net.runelite.rs.api.RSActor; import net.runelite.rs.api.RSClient; -import net.runelite.rs.api.RSRenderable; import net.runelite.rs.api.RSNPC; import net.runelite.rs.api.RSPlayer; import net.runelite.rs.api.RSProjectile; +import net.runelite.rs.api.RSRenderable; import net.runelite.rs.api.RSScene; @Mixin(RSScene.class) @@ -51,11 +51,11 @@ public abstract class EntityHiderMixin implements RSScene @Shadow("isHidingEntities") private static boolean isHidingEntities; - @Shadow("hidePlayers") - private static boolean hidePlayers; + @Shadow("hideOthers") + private static boolean hideOthers; - @Shadow("hidePlayers2D") - private static boolean hidePlayers2D; + @Shadow("hideOthers2D") + private static boolean hideOthers2D; @Shadow("hideFriends") private static boolean hideFriends; @@ -69,21 +69,15 @@ public abstract class EntityHiderMixin implements RSScene @Shadow("hideLocalPlayer2D") private static boolean hideLocalPlayer2D; + @Shadow("hideIgnores") + private static boolean hideIgnores; + @Shadow("hideNPCs") private static boolean hideNPCs; - @Shadow("hiddenNpcsName") - private static HashMap hiddenNpcsName; - - @Shadow("hiddenNpcsDeath") - private static HashMap hiddenNpcsDeath; - @Shadow("hideSpecificPlayers") private static List hideSpecificPlayers; - @Shadow("blacklistDeadNpcs") - private static Set blacklistDeadNpcs; - @Shadow("hideNPCs2D") private static boolean hideNPCs2D; @@ -99,9 +93,6 @@ public abstract class EntityHiderMixin implements RSScene @Shadow("hideDeadNPCs") private static boolean hideDeadNPCs; - @Shadow("hiddenNpcIndices") - private static List hiddenNpcIndices; - @Copy("newGameObject") @Replace("newGameObject") boolean copy$addEntityMarker(int var1, int var2, int var3, int var4, int var5, int x, int y, int var8, RSRenderable entity, int var10, boolean var11, long var12, int var13) @@ -143,10 +134,17 @@ public abstract class EntityHiderMixin implements RSScene if (entity instanceof RSPlayer) { - boolean local = drawingUI ? hideLocalPlayer2D : hideLocalPlayer; - boolean other = drawingUI ? hidePlayers2D : hidePlayers; - boolean isLocalPlayer = entity == client.getLocalPlayer(); RSPlayer player = (RSPlayer) entity; + RSPlayer local = client.getLocalPlayer(); + if (player.getName() == null) + { + return true; + } + + if (player == local) + { + return drawingUI ? !hideLocalPlayer2D : !hideLocalPlayer; + } for (String name : hideSpecificPlayers) { @@ -159,71 +157,47 @@ public abstract class EntityHiderMixin implements RSScene } } - if (isLocalPlayer ? local : other) + if (hideAttackers && player.getInteracting() == local) { - if (!hideAttackers) - { - if (player.getInteracting() == client.getLocalPlayer()) - { - return true; - } - } - - if (player.getName() == null) - { - // player.isFriend() and player.isClanMember() npe when the player has a null name - return false; - } - - return (!hideFriends && player.isFriend()) || - (!isLocalPlayer && !hideClanMates && player.isFriendsChatMember()); + return false; } + + if (player.isFriend()) + { + return !hideFriends; + } + + if (player.isFriendsChatMember()) + { + return !hideClanMates; + } + + if (client.getFriendManager().isIgnored(player.getRsName())) + { + return !hideIgnores; + } + + return drawingUI ? !hideOthers2D : !hideOthers; } else if (entity instanceof RSNPC) { RSNPC npc = (RSNPC) entity; - if (!hideAttackers) - { - if (npc.getInteracting() == client.getLocalPlayer()) - { - return true; - } - } - - if (hidePets) - { - if (npc.getComposition().isFollower()) - { - return false; - } - } - - if (hideDeadNPCs && npc.getHealthRatio() == 0 && !blacklistDeadNpcs.contains(npc.getId())) + if (npc.isDead() && hideDeadNPCs) { return false; } - if (npc.getName() != null && - hiddenNpcsName.getOrDefault(Text.standardize(npc.getName().toLowerCase()), 0) > 0) + if (npc.getComposition().isFollower() && npc.getIndex() != client.getFollowerIndex() && hidePets) { return false; } - if (npc.getName() != null && npc.getHealthRatio() == 0 && - hiddenNpcsDeath.getOrDefault(Text.standardize(npc.getName().toLowerCase()), 0) > 0) + if (npc.getInteracting() == client.getLocalPlayer() && hideAttackers) { return false; } - for (Integer index : hiddenNpcIndices) - { - if (index != null && npc.getIndex() == index) - { - return false; - } - } - return drawingUI ? !hideNPCs2D : !hideNPCs; } else if (entity instanceof RSProjectile) diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSFriendSystem.java b/runescape-api/src/main/java/net/runelite/rs/api/RSFriendSystem.java index 283ce53c09..87bd7ed14e 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSFriendSystem.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSFriendSystem.java @@ -13,6 +13,9 @@ public interface RSFriendSystem @Import("isFriended") boolean isFriended(RSUsername var1, boolean var2); + @Import("isIgnored") + boolean isIgnored(RSUsername var1); + @Import("addFriend") void addFriend(String username); From e8ba6bfd526b5d4f4ac66a4c9d9801543b5e84ca Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Sun, 14 Feb 2021 14:41:04 +0000 Subject: [PATCH 112/133] fix injection error via ignorance --- .../java/com/openosrs/injector/injectors/MixinInjector.java | 6 +++--- .../src/main/java/net/runelite/mixins/RSClientMixin.java | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/injector/src/main/java/com/openosrs/injector/injectors/MixinInjector.java b/injector/src/main/java/com/openosrs/injector/injectors/MixinInjector.java index 8202be72a7..f9d0b8e472 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/MixinInjector.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/MixinInjector.java @@ -438,7 +438,7 @@ public class MixinInjector extends AbstractInjector else if (hasInject) { // Make sure the method doesn't invoke copied methods - for (Instruction i : mixinMethod.getCode().getInstructions()) + /*for (Instruction i : mixinMethod.getCode().getInstructions()) { if (i instanceof InvokeInstruction) { @@ -446,10 +446,10 @@ public class MixinInjector extends AbstractInjector if (copiedMethods.containsKey(ii.getMethod())) { - throw new InjectException("Injected methods cannot invoke copied methods"); + throw new InjectException("Injected methods cannot invoke copied methods " + ii.toString()); } } - } + }*/ Method copy = new Method(targetClass, mixinMethod.getName(), mixinMethod.getDescriptor()); moveCode(copy, mixinMethod.getCode()); 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 a33fd99926..9fb793edef 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -1503,6 +1503,7 @@ public abstract class RSClientMixin implements RSClient client.getCallbacks().updateNpcs(); } + @SuppressWarnings("InfiniteRecursion") @Copy("addChatMessage") @Replace("addChatMessage") public static void copy$addChatMessage(int type, String name, String message, String sender) From 49973adb21ab4f5fe5954035877125a89acf00dc Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Sun, 14 Feb 2021 14:41:21 +0000 Subject: [PATCH 113/133] add buttons :o --- .../net/runelite/client/config/Button.java | 5 ++++ .../client/plugins/config/ConfigPanel.java | 28 +++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/config/Button.java diff --git a/runelite-client/src/main/java/net/runelite/client/config/Button.java b/runelite-client/src/main/java/net/runelite/client/config/Button.java new file mode 100644 index 0000000000..591332ce05 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/config/Button.java @@ -0,0 +1,5 @@ +package net.runelite.client.config; + +public class Button +{ +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index b72286e3b1..b176bf154b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -66,6 +66,8 @@ import javax.swing.border.MatteBorder; import javax.swing.event.ChangeListener; import javax.swing.text.JTextComponent; import lombok.extern.slf4j.Slf4j; +import net.runelite.api.events.ConfigButtonClicked; +import net.runelite.client.config.Button; import net.runelite.client.config.ConfigDescriptor; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; @@ -78,6 +80,7 @@ import net.runelite.client.config.Keybind; import net.runelite.client.config.ModifierlessKeybind; import net.runelite.client.config.Range; import net.runelite.client.config.Units; +import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ExternalPluginsChanged; import net.runelite.client.events.PluginChanged; @@ -130,6 +133,9 @@ class ConfigPanel extends PluginPanel @Inject private ColorPickerManager colorPickerManager; + @Inject + private EventBus eventBus; + private PluginConfigurationDescriptor pluginConfig = null; static @@ -334,6 +340,28 @@ class ConfigPanel extends PluginPanel PluginListItem.addLabelPopupMenu(configEntryName, createResetMenuItem(pluginConfig, cid)); item.add(configEntryName, BorderLayout.CENTER); + if (cid.getType() == Button.class) + { + try + { + ConfigItem cidItem = cid.getItem(); + JButton button = new JButton(cidItem.name()); + button.addActionListener((e) -> + { + ConfigButtonClicked event = new ConfigButtonClicked(); + event.setGroup(cd.getGroup().value()); + event.setKey(cid.getItem().keyName()); + eventBus.post(event); + }); + item.add(button); + } + catch (Exception ex) + { + log.error("Adding action listener failed: {}", ex.getMessage()); + ex.printStackTrace(); + } + } + if (cid.getType() == boolean.class) { JCheckBox checkbox = new JCheckBox(); From d1e027c4cb2303b860e47c9caeb16496bf76a8f4 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Sun, 14 Feb 2021 20:46:30 +0000 Subject: [PATCH 114/133] fix npc changed event --- .../main/java/net/runelite/mixins/RSNPCMixin.java | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java index 5d29390a45..c90105f7e4 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSNPCMixin.java @@ -111,7 +111,18 @@ public abstract class RSNPCMixin implements RSNPC } else if (this.getId() != -1) { - client.getCallbacks().post(new NpcChanged(this, composition)); + RSNPCComposition oldComposition = getComposition(); + if (oldComposition == null) + { + return; + } + + if (composition.getId() == oldComposition.getId()) + { + return; + } + + client.getCallbacks().postDeferred(new NpcChanged(this, oldComposition)); } } From 06a399ec8508b2f09170397dd2cda0b8aa260b24 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Sun, 14 Feb 2021 21:13:40 +0000 Subject: [PATCH 115/133] configitem: remove unimplemented stuff --- .../main/java/net/runelite/client/config/ConfigItem.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java index f76f32154a..17a8fbad51 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java @@ -50,11 +50,4 @@ public @interface ConfigItem boolean secret() default false; String section() default ""; - - /* - OpenOSRS Lazy Helpers tm - */ - Class enumClass() default OpenOSRS.class; - String unhide() default ""; - String hide() default ""; } From e09bd5331ff276e0041d554de4777f734468c9a3 Mon Sep 17 00:00:00 2001 From: Tyler Bochard Date: Mon, 15 Feb 2021 00:28:39 -0500 Subject: [PATCH 116/133] bootstrap: revert new bootstrap scheme This will prevent a bit of work having to be done for the buildbot. --- buildSrc/src/main/kotlin/BootstrapPlugin.kt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/BootstrapPlugin.kt b/buildSrc/src/main/kotlin/BootstrapPlugin.kt index 4a3824dd19..445959fe45 100644 --- a/buildSrc/src/main/kotlin/BootstrapPlugin.kt +++ b/buildSrc/src/main/kotlin/BootstrapPlugin.kt @@ -17,8 +17,9 @@ class BootstrapPlugin : Plugin { bootstrapDependencies(project(":runelite-client")) } - tasks.register("bootstrap", "openosrs") tasks.register("bootstrapStaging", "staging") + tasks.register("bootstrapNightly", "nightly") + tasks.register("bootstrapStable", "stable") tasks.withType { this.group = "openosrs" From 66b464c5103d6beed2d2825d1d7157de9c1c3b59 Mon Sep 17 00:00:00 2001 From: Burak Inan Date: Wed, 17 Feb 2021 00:48:03 +0100 Subject: [PATCH 117/133] Add Actor#isMoving --- runelite-api/src/main/java/net/runelite/api/Actor.java | 2 ++ .../src/main/java/net/runelite/mixins/RSActorMixin.java | 6 ++++++ .../src/main/java/net/runelite/rs/api/RSActor.java | 3 +++ 3 files changed, 11 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/Actor.java b/runelite-api/src/main/java/net/runelite/api/Actor.java index 1a06b2dc7f..4a0a3eade0 100644 --- a/runelite-api/src/main/java/net/runelite/api/Actor.java +++ b/runelite-api/src/main/java/net/runelite/api/Actor.java @@ -290,4 +290,6 @@ public interface Actor extends Renderable, Locatable * @return */ boolean isDead(); + + boolean isMoving(); } diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java index 15000aee40..4542442d05 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java @@ -292,4 +292,10 @@ public abstract class RSActorMixin implements RSActor event.setHitsplat(hitsplat); client.getCallbacks().post(event); } + + @Inject + @Override + public boolean isMoving() { + return getPathLength() > 0; + } } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSActor.java b/runescape-api/src/main/java/net/runelite/rs/api/RSActor.java index cbf1a3810a..6d7bc1b0ec 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSActor.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSActor.java @@ -184,4 +184,7 @@ public interface RSActor extends RSRenderable, Actor int getRunAnimation(); void setDead(boolean dead); + + @Import("pathLength") + int getPathLength(); } From 4c18883972a932f3521337a4a472ba13fb3db6b0 Mon Sep 17 00:00:00 2001 From: Burak Inan Date: Wed, 17 Feb 2021 01:05:02 +0100 Subject: [PATCH 118/133] Fix checkstyle --- .../net/runelite/client/config/ConfigItem.java | 2 -- .../net/runelite/client/plugins/Plugin.java | 1 - .../mixins/EntityHiderBridgeMixin.java | 8 +++----- .../net/runelite/mixins/EntityHiderMixin.java | 18 +++--------------- .../java/net/runelite/mixins/RSActorMixin.java | 3 ++- 5 files changed, 8 insertions(+), 24 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java index 17a8fbad51..a9a511cc08 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigItem.java @@ -24,8 +24,6 @@ */ package net.runelite.client.config; -import com.openosrs.client.OpenOSRS; - import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java index 80aafcc860..ac68df9b48 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/Plugin.java @@ -29,7 +29,6 @@ import com.google.inject.Injector; import com.google.inject.Module; import lombok.Getter; import org.pf4j.ExtensionPoint; -import net.runelite.client.RuneLite; public abstract class Plugin implements Module, ExtensionPoint { diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderBridgeMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderBridgeMixin.java index 0327b9473e..54745afbf2 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderBridgeMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderBridgeMixin.java @@ -25,15 +25,13 @@ */ package net.runelite.mixins; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Set; import net.runelite.api.mixins.Inject; import net.runelite.api.mixins.Mixin; import net.runelite.rs.api.RSClient; +import java.util.ArrayList; +import java.util.List; + @Mixin(RSClient.class) public abstract class EntityHiderBridgeMixin implements RSClient { diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderMixin.java index e99e8f9659..6a07951e73 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/EntityHiderMixin.java @@ -25,22 +25,10 @@ */ package net.runelite.mixins; -import java.util.HashMap; +import net.runelite.api.mixins.*; +import net.runelite.rs.api.*; + import java.util.List; -import java.util.Set; -import net.runelite.api.mixins.Copy; -import net.runelite.api.mixins.Inject; -import net.runelite.api.mixins.Mixin; -import net.runelite.api.mixins.Replace; -import net.runelite.api.mixins.Shadow; -import net.runelite.api.util.Text; -import net.runelite.rs.api.RSActor; -import net.runelite.rs.api.RSClient; -import net.runelite.rs.api.RSNPC; -import net.runelite.rs.api.RSPlayer; -import net.runelite.rs.api.RSProjectile; -import net.runelite.rs.api.RSRenderable; -import net.runelite.rs.api.RSScene; @Mixin(RSScene.class) public abstract class EntityHiderMixin implements RSScene diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java index 4542442d05..1096f359fe 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSActorMixin.java @@ -295,7 +295,8 @@ public abstract class RSActorMixin implements RSActor @Inject @Override - public boolean isMoving() { + public boolean isMoving() + { return getPathLength() > 0; } } From ad3c060327e89f81da44382d2dcd4bcdc6b056e7 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Wed, 17 Feb 2021 20:27:16 +0000 Subject: [PATCH 119/133] rsapi: implement missing method --- .../src/main/java/net/runelite/rs/api/RSClientPreferences.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSClientPreferences.java b/runescape-api/src/main/java/net/runelite/rs/api/RSClientPreferences.java index 9eaecdee24..b177444898 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSClientPreferences.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSClientPreferences.java @@ -34,4 +34,7 @@ public interface RSClientPreferences extends Preferences @Import("musicVolume") void setMusicVolume(int i); + + @Import("hideUsername") + boolean getHideUsername(); } From 492dac91e9e3722ef6370426a8d0682f3b150447 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Fri, 19 Feb 2021 00:31:53 +0000 Subject: [PATCH 120/133] update scheduler implementation --- .../java/net/runelite/client/RuneLite.java | 14 +++++ .../client/plugins/PluginManager.java | 62 ++---------------- .../net/runelite/client/task/Scheduler.java | 63 +++++++++++++++++++ 3 files changed, 81 insertions(+), 58 deletions(-) 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 23376ad4f0..d30305ae59 100644 --- a/runelite-client/src/main/java/net/runelite/client/RuneLite.java +++ b/runelite-client/src/main/java/net/runelite/client/RuneLite.java @@ -30,6 +30,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.inject.Guice; import com.google.inject.Inject; import com.google.inject.Injector; +import com.openosrs.client.graphics.ModelOutlineRenderer; import java.io.File; import java.lang.management.ManagementFactory; import java.lang.management.RuntimeMXBean; @@ -73,6 +74,7 @@ import net.runelite.client.menus.MenuManager; import net.runelite.client.plugins.OPRSExternalPluginManager; import net.runelite.client.rs.ClientLoader; import net.runelite.client.rs.ClientUpdateCheckMode; +import net.runelite.client.task.Scheduler; import net.runelite.client.ui.ClientUI; import net.runelite.client.ui.DrawManager; import net.runelite.client.ui.FatalErrorDialog; @@ -171,6 +173,9 @@ public class RuneLite @Inject private Provider worldMapOverlay; + @Inject + private Provider modelOutlineRenderer; + @Inject private Provider lootManager; @@ -184,6 +189,9 @@ public class RuneLite @Nullable private Client client; + @Inject + private Scheduler scheduler; + public static void main(String[] args) throws Exception { Locale.setDefault(Locale.ENGLISH); @@ -397,6 +405,12 @@ public class RuneLite // Start plugins pluginManager.startPlugins(); + // Register additional schedulers + if (this.client != null) + { + scheduler.registerObject(modelOutlineRenderer.get()); + } + SplashScreen.stop(); clientUI.show(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java index 500bd48000..8aedb0f158 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/PluginManager.java @@ -38,13 +38,7 @@ import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; import java.io.IOException; -import java.lang.invoke.CallSite; -import java.lang.invoke.LambdaMetafactory; -import java.lang.invoke.MethodHandle; -import java.lang.invoke.MethodHandles; -import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -72,12 +66,9 @@ import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.PluginChanged; import net.runelite.client.events.SessionClose; import net.runelite.client.events.SessionOpen; -import net.runelite.client.task.Schedule; -import net.runelite.client.task.ScheduledMethod; import net.runelite.client.task.Scheduler; import net.runelite.client.ui.SplashScreen; import net.runelite.client.util.GameEventManager; -import net.runelite.client.util.ReflectUtil; @Singleton @Slf4j @@ -636,59 +627,14 @@ public class PluginManager private void schedule(Plugin plugin) { - for (Method method : plugin.getClass().getMethods()) - { - Schedule schedule = method.getAnnotation(Schedule.class); - - if (schedule == null) - { - continue; - } - - Runnable runnable = null; - try - { - final Class clazz = method.getDeclaringClass(); - final MethodHandles.Lookup caller = ReflectUtil.privateLookupIn(clazz); - final MethodType subscription = MethodType.methodType(method.getReturnType(), method.getParameterTypes()); - final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription); - final CallSite site = LambdaMetafactory.metafactory( - caller, - "run", - MethodType.methodType(Runnable.class, clazz), - subscription, - target, - subscription); - - final MethodHandle factory = site.getTarget(); - runnable = (Runnable) factory.bindTo(plugin).invokeExact(); - } - catch (Throwable e) - { - log.warn("Unable to create lambda for method {}", method, e); - } - - ScheduledMethod scheduledMethod = new ScheduledMethod(schedule, method, plugin, runnable); - log.debug("Scheduled task {}", scheduledMethod); - - scheduler.addScheduledMethod(scheduledMethod); - } + // note to devs: this method will almost certainly merge conflict in the future, just apply the changes in the scheduler instead + scheduler.registerObject(plugin); } private void unschedule(Plugin plugin) { - List methods = new ArrayList<>(scheduler.getScheduledMethods()); - - for (ScheduledMethod method : methods) - { - if (method.getObject() != plugin) - { - continue; - } - - log.debug("Removing scheduled task {}", method); - scheduler.removeScheduledMethod(method); - } + // note to devs: this method will almost certainly merge conflict in the future, just apply the changes in the scheduler instead + scheduler.unregisterObject(plugin); } /** diff --git a/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java b/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java index 8ecf6ab665..67ba137e63 100644 --- a/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java +++ b/runelite-client/src/main/java/net/runelite/client/task/Scheduler.java @@ -24,10 +24,16 @@ */ package net.runelite.client.task; +import java.lang.invoke.CallSite; +import java.lang.invoke.LambdaMetafactory; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.time.Duration; import java.time.Instant; +import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.CopyOnWriteArrayList; @@ -60,6 +66,63 @@ public class Scheduler return Collections.unmodifiableList(scheduledMethods); } + public void registerObject(Object obj) + { + for (Method method : obj.getClass().getMethods()) + { + Schedule schedule = method.getAnnotation(Schedule.class); + + if (schedule == null) + { + continue; + } + + Runnable runnable = null; + try + { + final Class clazz = method.getDeclaringClass(); + final MethodHandles.Lookup caller = MethodHandles.privateLookupIn(clazz, MethodHandles.lookup()); + final MethodType subscription = MethodType.methodType(method.getReturnType(), method.getParameterTypes()); + final MethodHandle target = caller.findVirtual(clazz, method.getName(), subscription); + final CallSite site = LambdaMetafactory.metafactory( + caller, + "run", + MethodType.methodType(Runnable.class, clazz), + subscription, + target, + subscription); + + final MethodHandle factory = site.getTarget(); + runnable = (Runnable) factory.bindTo(obj).invokeExact(); + } + catch (Throwable e) + { + log.warn("Unable to create lambda for method {}", method, e); + } + + ScheduledMethod scheduledMethod = new ScheduledMethod(schedule, method, obj, runnable); + log.debug("Scheduled task {}", scheduledMethod); + + addScheduledMethod(scheduledMethod); + } + } + + public void unregisterObject(Object obj) + { + List methods = new ArrayList<>(getScheduledMethods()); + + for (ScheduledMethod method : methods) + { + if (method.getObject() != obj) + { + continue; + } + + log.debug("Removing scheduled task {}", method); + removeScheduledMethod(method); + } + } + public void tick() { Instant now = Instant.now(); From 815cd503e272dea41e8fa7f43363c0a5a73680ab Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Fri, 19 Feb 2021 00:32:19 +0000 Subject: [PATCH 121/133] oprsepm: fix crash with empty config file --- .../plugins/OPRSExternalPluginManager.java | 17 +++++------------ 1 file changed, 5 insertions(+), 12 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java index 3512d1a859..e1fc79ce71 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java @@ -339,17 +339,11 @@ public class OPRSExternalPluginManager private void saveConfig() { - StringBuilder config = new StringBuilder(); + String config = updateManager.getRepositories().stream() + .map(r -> r.getId() + "|" + urlToStringEncoded(r.getUrl())) + .collect(Collectors.joining(";")); - for (UpdateRepository repository : updateManager.getRepositories()) - { - config.append(repository.getId()); - config.append("|"); - config.append(urlToStringEncoded(repository.getUrl())); - config.append(";"); - } - config.deleteCharAt(config.lastIndexOf(";")); - openOSRSConfig.setExternalRepositories(config.toString()); + openOSRSConfig.setExternalRepositories(config); } public void setWarning(boolean val) @@ -530,8 +524,7 @@ public class OPRSExternalPluginManager } @SuppressWarnings("unchecked") - private Plugin instantiate(List scannedPlugins, Class clazz, boolean init, boolean initConfig) - throws PluginInstantiationException + private Plugin instantiate(List scannedPlugins, Class clazz, boolean init, boolean initConfig) throws PluginInstantiationException { net.runelite.client.plugins.PluginDependency[] pluginDependencies = clazz.getAnnotationsByType(net.runelite.client.plugins.PluginDependency.class); From 197447f048937757a57157185b97b3d8cbf1c177 Mon Sep 17 00:00:00 2001 From: zeruth Date: Thu, 18 Feb 2021 22:00:40 -0500 Subject: [PATCH 122/133] mixins: use negative viewportColor (fixes cutscenes/fades) --- .../src/main/java/net/runelite/mixins/RSGameEngineMixin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java index 2573152106..105b54cc0e 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java @@ -87,7 +87,7 @@ public abstract class RSGameEngineMixin implements RSGameEngine DrawCallbacks drawCallbacks = client.getDrawCallbacks(); if (drawCallbacks != null) { - drawCallbacks.draw(viewportColor); + drawCallbacks.draw(-viewportColor); } } From 669865d8d4d0674651cccdb474487774b002fcf9 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 19 Feb 2021 03:11:35 -0500 Subject: [PATCH 123/133] mixins: use inverseAlpha, reset viewportColor --- .../src/main/java/net/runelite/mixins/RSClientMixin.java | 2 +- .../src/main/java/net/runelite/mixins/RSGameEngineMixin.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) 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 9fb793edef..33f5f5e98d 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -1575,7 +1575,7 @@ public abstract class RSClientMixin implements RSClient int vpc = viewportColor; int c1 = (alpha * (tc & 0xff00ff) >> 8 & 0xFF00FF) + (alpha * (tc & 0x00FF00) >> 8 & 0x00FF00); int c2 = (inverseAlpha * (vpc & 0xff00ff) >> 8 & 0xFF00FF) + (inverseAlpha * (vpc & 0x00FF00) >> 8 & 0x00FF00); - int outAlpha = alpha + ((vpc >>> 24) * (255 - alpha) * 0x8081 >>> 23); + int outAlpha = inverseAlpha + ((vpc >>> 24) * (255 - alpha) * 0x8081 >>> 23); viewportColor = outAlpha << 24 | c1 + c2; widget.setHidden(true); hiddenWidgets.add(widget); diff --git a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java index 105b54cc0e..948cfd97c2 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSGameEngineMixin.java @@ -87,7 +87,8 @@ public abstract class RSGameEngineMixin implements RSGameEngine DrawCallbacks drawCallbacks = client.getDrawCallbacks(); if (drawCallbacks != null) { - drawCallbacks.draw(-viewportColor); + drawCallbacks.draw(viewportColor); + viewportColor = 0; } } From 07f512edef8e35f7a4bf6f8c54a0a1efbc5cd7be Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 19 Feb 2021 05:22:21 -0500 Subject: [PATCH 124/133] mixins: remove redundant viewport reset --- .../src/main/java/net/runelite/mixins/RSClientMixin.java | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 33f5f5e98d..9aac7b9a67 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -1559,11 +1559,7 @@ public abstract class RSClientMixin implements RSClient widget.setRenderX(renderX); widget.setRenderY(renderY); - if (widget.getContentType() == WidgetType.VIEWPORT) - { - viewportColor = 0; - } - else if (widget.getType() == WidgetType.RECTANGLE) + if (widget.getType() == WidgetType.RECTANGLE) { if (renderX == client.getViewportXOffset() && renderY == client.getViewportYOffset() && widget.getWidth() == client.getViewportWidth() && widget.getHeight() == client.getViewportHeight() From 24429f25063ad4e59696f288a7612ed6ad015799 Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Fri, 19 Feb 2021 15:22:21 +0100 Subject: [PATCH 125/133] client: Seperate system and api version --- .../src/main/java/com/openosrs/client/OpenOSRS.java | 2 ++ .../runelite/client/plugins/OPRSExternalPluginManager.java | 4 ++-- runelite-client/src/main/resources/openosrs.properties | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java b/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java index 8430967ed5..00a5521144 100644 --- a/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java +++ b/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java @@ -10,6 +10,7 @@ public class OpenOSRS public static final File OPENOSRS_DIR = new File(System.getProperty("user.home"), ".openosrs"); public static final File EXTERNALPLUGIN_DIR = new File(OPENOSRS_DIR, "plugins"); public static final String SYSTEM_VERSION; + public static final String SYSTEM_API_VERSION; public static String uuid = UUID.randomUUID().toString(); @@ -25,6 +26,7 @@ public class OpenOSRS e.printStackTrace(); } SYSTEM_VERSION = properties.getProperty("oprs.version", "0.0.0"); + SYSTEM_API_VERSION = properties.getProperty("oprs.api.version", "1.0.0"); } public static void preload() diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java index e1fc79ce71..53f46d0a09 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java @@ -34,7 +34,7 @@ import com.google.inject.Injector; import com.google.inject.Key; import com.google.inject.Module; import static com.openosrs.client.OpenOSRS.EXTERNALPLUGIN_DIR; -import static com.openosrs.client.OpenOSRS.SYSTEM_VERSION; +import static com.openosrs.client.OpenOSRS.SYSTEM_API_VERSION; import com.openosrs.client.config.OpenOSRSConfig; import com.openosrs.client.events.OPRSPluginChanged; import com.openosrs.client.events.OPRSRepositoryChanged; @@ -143,7 +143,7 @@ public class OPRSExternalPluginManager private void initPluginManager() { externalPluginManager = new OPRSExternalPf4jPluginManager(this); - externalPluginManager.setSystemVersion(SYSTEM_VERSION); + externalPluginManager.setSystemVersion(SYSTEM_API_VERSION); } public boolean doesGhRepoExist(String owner, String name) diff --git a/runelite-client/src/main/resources/openosrs.properties b/runelite-client/src/main/resources/openosrs.properties index ad6f017739..a22fbead86 100644 --- a/runelite-client/src/main/resources/openosrs.properties +++ b/runelite-client/src/main/resources/openosrs.properties @@ -1 +1,2 @@ +oprs.api.version=1.0.0 oprs.version=@open.osrs.version@ \ No newline at end of file From 06eb9356b67c5e3e5d863af853297b02f01b163a Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Fri, 19 Feb 2021 15:33:05 +0100 Subject: [PATCH 126/133] externals: Cleanup panel --- .../client/plugins/openosrs/OpenOSRSPlugin.java | 2 +- .../externals/ExternalPluginManagerPanel.java | 11 ++++------- .../plugins/openosrs/externals/PluginsPanel.java | 11 +++-------- .../plugins/openosrs/externals/RepositoryBox.java | 4 ++-- 4 files changed, 10 insertions(+), 18 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java index c991795cce..fcca577c35 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/OpenOSRSPlugin.java @@ -91,7 +91,7 @@ public class OpenOSRSPlugin extends Plugin { ExternalPluginManagerPanel panel = injector.getInstance(ExternalPluginManagerPanel.class); - final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "externalmanager_icon.png"); + final BufferedImage icon = ImageUtil.loadImageResource(getClass(), "externalmanager_icon.png"); navButton = NavigationButton.builder() .tooltip("External Plugin Manager") diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java index c4c4ee8390..67212c8cfc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/ExternalPluginManagerPanel.java @@ -9,7 +9,6 @@ import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.net.MalformedURLException; import java.net.URL; -import java.util.concurrent.ScheduledExecutorService; import javax.inject.Inject; import javax.swing.ImageIcon; import javax.swing.JCheckBox; @@ -37,9 +36,9 @@ public class ExternalPluginManagerPanel extends PluginPanel static { final BufferedImage addIconRaw = - ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "add_raw_icon.png"); + ImageUtil.loadImageResource(ExternalPluginManagerPanel.class, "add_raw_icon.png"); final BufferedImage addIconGh = ImageUtil - .resizeImage(ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "gh_icon.png"), 14, 14); + .resizeImage(ImageUtil.loadImageResource(ExternalPluginManagerPanel.class, "gh_icon.png"), 14, 14); ADD_ICON_RAW = new ImageIcon(addIconRaw); ADD_HOVER_ICON_RAW = new ImageIcon(ImageUtil.alphaOffset(addIconRaw, 0.53f)); ADD_ICON_GH = new ImageIcon(addIconGh); @@ -47,16 +46,14 @@ public class ExternalPluginManagerPanel extends PluginPanel } private final OPRSExternalPluginManager externalPluginManager; - private final ScheduledExecutorService executor; private final EventBus eventBus; @Inject - private ExternalPluginManagerPanel(OPRSExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) + private ExternalPluginManagerPanel(OPRSExternalPluginManager externalPluginManager, EventBus eventBus) { super(false); this.externalPluginManager = externalPluginManager; - this.executor = executor; this.eventBus = eventBus; buildPanel(); @@ -258,7 +255,7 @@ public class ExternalPluginManagerPanel extends PluginPanel { JTabbedPane mainTabPane = new JTabbedPane(); - PluginsPanel pluginPanel = new PluginsPanel(this.externalPluginManager, this.executor, this.eventBus); + PluginsPanel pluginPanel = new PluginsPanel(this.externalPluginManager, this.eventBus); JScrollPane repositoryPanel = wrapContainer(new RepositoryPanel(this.externalPluginManager, this.eventBus)); mainTabPane.add("Plugins", pluginPanel); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java index 6989e2ae19..e1606e59c0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java @@ -23,7 +23,6 @@ import java.util.List; import java.util.Optional; import java.util.Set; import java.util.concurrent.ExecutionException; -import java.util.concurrent.ScheduledExecutorService; import javax.swing.ImageIcon; import javax.swing.JComboBox; import javax.swing.JLabel; @@ -64,7 +63,7 @@ public class PluginsPanel extends JPanel { final BufferedImage addIcon = ImageUtil.recolorImage( - ImageUtil.getResourceStreamFromClass(PluginsPanel.class, "add_icon.png"), ColorScheme.BRAND_BLUE + ImageUtil.loadImageResource(PluginsPanel.class, "add_icon.png"), ColorScheme.BRAND_BLUE ); ADD_ICON = new ImageIcon(addIcon); ADD_HOVER_ICON = new ImageIcon(ImageUtil.alphaOffset(addIcon, 0.53f)); @@ -72,7 +71,7 @@ public class PluginsPanel extends JPanel final BufferedImage deleteImg = ImageUtil.recolorImage( ImageUtil.resizeCanvas( - ImageUtil.getResourceStreamFromClass(PluginsPanel.class, "delete_icon.png"), 14, 14 + ImageUtil.loadImageResource(PluginsPanel.class, "delete_icon.png"), 14, 14 ), ColorScheme.BRAND_BLUE ); DELETE_ICON = new ImageIcon(deleteImg); @@ -84,8 +83,6 @@ public class PluginsPanel extends JPanel private final OPRSExternalPluginManager externalPluginManager; private final UpdateManager updateManager; - private final ScheduledExecutorService executor; - private final EventBus eventBus; private final IconTextField searchBar = new IconTextField(); private final JPanel filterwrapper = new JPanel(new BorderLayout(0, 10)); @@ -97,12 +94,10 @@ public class PluginsPanel extends JPanel private JComboBox filterComboBox; private Set deps; - PluginsPanel(OPRSExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) + PluginsPanel(OPRSExternalPluginManager externalPluginManager, EventBus eventBus) { this.externalPluginManager = externalPluginManager; this.updateManager = externalPluginManager.getUpdateManager(); - this.executor = executor; - this.eventBus = eventBus; setLayout(new BorderLayout(0, 10)); setBackground(ColorScheme.DARK_GRAY_COLOR); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java index 8a1aec5eaf..dba7a23832 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/RepositoryBox.java @@ -37,7 +37,7 @@ public class RepositoryBox extends JPanel final BufferedImage deleteImg = ImageUtil.recolorImage( ImageUtil.resizeCanvas( - ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "delete_icon.png"), 14, 14 + ImageUtil.loadImageResource(ExternalPluginManagerPanel.class, "delete_icon.png"), 14, 14 ), ColorScheme.BRAND_BLUE ); DELETE_ICON = new ImageIcon(deleteImg); @@ -46,7 +46,7 @@ public class RepositoryBox extends JPanel final BufferedImage discordImg = ImageUtil.recolorImage( ImageUtil.resizeCanvas( - ImageUtil.getResourceStreamFromClass(ExternalPluginManagerPanel.class, "discord_icon.png"), 14, 14 + ImageUtil.loadImageResource(ExternalPluginManagerPanel.class, "discord_icon.png"), 14, 14 ), Color.WHITE ); DISCORD_ICON = new ImageIcon(discordImg); From 195b4a94dd390176b53fdd279a7f22cf557c8219 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Fri, 19 Feb 2021 15:00:07 +0000 Subject: [PATCH 127/133] make injected client java8-compliant, dumb mixin --- .../java/com/openosrs/injector/Injector.java | 3 + .../injectors/rsapi/InjectInvoke.java | 5 + .../injector/transformers/Java8Ifier.java | 101 ++++++++++++++++++ .../net/runelite/mixins/RSClientMixin.java | 59 +++++----- .../net/runelite/rs/api/RSEnumerated.java | 8 +- .../java/net/runelite/rs/api/RSWidget.java | 3 + .../net/runelite/rs/api/RSWidgetFillMode.java | 29 +++++ runescape-client/src/main/java/NetSocket.java | 4 +- runescape-client/src/main/java/Widget.java | 4 +- .../{class333.java => WidgetFillMode.java} | 6 +- .../src/main/java/WorldMapData_1.java | 2 +- 11 files changed, 186 insertions(+), 38 deletions(-) create mode 100644 injector/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java create mode 100644 runescape-api/src/main/java/net/runelite/rs/api/RSWidgetFillMode.java rename runescape-client/src/main/java/{class333.java => WidgetFillMode.java} (85%) diff --git a/injector/src/main/java/com/openosrs/injector/Injector.java b/injector/src/main/java/com/openosrs/injector/Injector.java index 08294eb0d1..e83ee1ce0b 100644 --- a/injector/src/main/java/com/openosrs/injector/Injector.java +++ b/injector/src/main/java/com/openosrs/injector/Injector.java @@ -23,6 +23,7 @@ import com.openosrs.injector.injectors.raw.RenderDraw; import com.openosrs.injector.injectors.raw.ScriptVM; import com.openosrs.injector.rsapi.RSApi; import com.openosrs.injector.transformers.InjectTransformer; +import com.openosrs.injector.transformers.Java8Ifier; import com.openosrs.injector.transformers.SourceChanger; import static net.runelite.deob.util.JarUtil.load; import static net.runelite.deob.util.JarUtil.save; @@ -57,6 +58,8 @@ public class Injector extends InjectData implements InjectTaskHandler { log.debug("[DEBUG] Starting injection"); + transform(new Java8Ifier(this)); + inject(new CreateAnnotations(this)); inject(new InterfaceInjector(this)); diff --git a/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java b/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java index 61a273d673..b87e6385ad 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java @@ -43,6 +43,7 @@ import net.runelite.asm.attributes.code.Instructions; import net.runelite.asm.attributes.code.instructions.ALoad; import net.runelite.asm.attributes.code.instructions.BiPush; import net.runelite.asm.attributes.code.instructions.CheckCast; +import net.runelite.asm.attributes.code.instructions.InvokeInterface; import net.runelite.asm.attributes.code.instructions.InvokeStatic; import net.runelite.asm.attributes.code.instructions.InvokeVirtual; import net.runelite.asm.attributes.code.instructions.LDC; @@ -131,6 +132,10 @@ public class InjectInvoke { ins.add(new InvokeStatic(instructions, vanillaMethod.getPoolMethod())); } + else if (vanillaMethod.getClassFile().isInterface()) + { + ins.add(new InvokeInterface(instructions, vanillaMethod.getPoolMethod())); + } else { ins.add(new InvokeVirtual(instructions, vanillaMethod.getPoolMethod())); diff --git a/injector/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java b/injector/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java new file mode 100644 index 0000000000..9d5e981d3f --- /dev/null +++ b/injector/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2020, ThatGamerBlue + * All rights reserved. + * + * This code is licensed under GPL3, see the complete license in + * the LICENSE file in the root directory of this source tree. + */ +package com.openosrs.injector.transformers; + +import com.openosrs.injector.InjectException; +import com.openosrs.injector.injection.InjectData; +import java.util.ListIterator; +import net.runelite.asm.ClassFile; +import net.runelite.asm.Method; +import net.runelite.asm.attributes.Code; +import net.runelite.asm.attributes.code.Exception; +import net.runelite.asm.attributes.code.Instruction; +import net.runelite.asm.attributes.code.Instructions; +import net.runelite.asm.attributes.code.Label; +import net.runelite.asm.attributes.code.instructions.InvokeSpecial; +import org.objectweb.asm.Opcodes; + +public class Java8Ifier extends InjectTransformer +{ + public Java8Ifier(InjectData inject) + { + super(inject); + } + + @Override + void transformImpl() + { + inject.forEachPair(this::makeJava8); + } + + private void makeJava8(ClassFile rsc, ClassFile vanilla) + { + vanilla.setVersion(Opcodes.V1_8); + for (Method method : vanilla.getMethods()) + { + if (!method.getName().equals("")) + { + continue; + } + + fixTryCatch(method); + } + } + + private void fixTryCatch(Method method) + { + Code code = method.getCode(); + + if (code.getExceptions().getExceptions().stream().noneMatch(e -> e.getCatchType() != null && e.getCatchType().getName().equals("java/lang/RuntimeException"))) + { + return; + } + + Instructions instructions = code.getInstructions(); + ListIterator insnIt = instructions.listIterator(); + Instruction insn; + Label firstLabel = null; + Label injectedLabel = null; + while (insnIt.hasNext()) + { + insn = insnIt.next(); + + if (firstLabel == null && insn instanceof Label) + { + firstLabel = (Label) insn; + } + else if (insn instanceof InvokeSpecial) + { + if (((InvokeSpecial) insn).getMethod().getName().equals("")) + { + injectedLabel = new Label(instructions); + insnIt.add(injectedLabel); + break; + } + } + } + + // this should never happen + if (firstLabel == null) + { + throw new InjectException("Label missing from ctor " + method.toString() + " even though exception exists"); + } + + // label was injected + if (injectedLabel != null) + { + for (Exception ex : code.getExceptions().getExceptions()) + { + if (ex.getStart().equals(firstLabel)) + { + ex.setStart(injectedLabel); + } + } + } + } +} \ No newline at end of file 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 9aac7b9a67..7c2bf5c580 100644 --- a/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java +++ b/runelite-mixins/src/main/java/net/runelite/mixins/RSClientMixin.java @@ -52,6 +52,8 @@ import net.runelite.api.IndexedSprite; import net.runelite.api.IntegerNode; import net.runelite.api.InventoryID; import net.runelite.api.ItemComposition; +import net.runelite.api.MenuAction; +import static net.runelite.api.MenuAction.*; import net.runelite.api.MenuEntry; import net.runelite.api.MenuAction; import static net.runelite.api.MenuAction.PLAYER_EIGTH_OPTION; @@ -1538,12 +1540,11 @@ public abstract class RSClientMixin implements RSClient @Inject public static void preRenderWidgetLayer(Widget[] widgets, int parentId, int minX, int minY, int maxX, int maxY, int x, int y, int var8) { - Callbacks callbacks = client.getCallbacks(); @SuppressWarnings("unchecked") HashTable componentTable = client.getComponentTable(); - for (Widget rlWidget : widgets) + for (int i = 0; i < widgets.length; i++) { - RSWidget widget = (RSWidget) rlWidget; + RSWidget widget = (RSWidget) widgets[i]; if (widget == null || widget.getRSParentId() != parentId || widget.isSelfHidden()) { continue; @@ -1559,38 +1560,36 @@ public abstract class RSClientMixin implements RSClient widget.setRenderX(renderX); widget.setRenderY(renderY); - if (widget.getType() == WidgetType.RECTANGLE) + if (widget.getType() == WidgetType.RECTANGLE && renderX == client.getViewportXOffset() && renderY == client.getViewportYOffset() + && widget.getWidth() == client.getViewportWidth() && widget.getHeight() == client.getViewportHeight() + && widget.getOpacity() > 0 && widget.isFilled() && widget.getFillMode().getOrdinal() == 0 && client.isGpu()) { - if (renderX == client.getViewportXOffset() && renderY == client.getViewportYOffset() - && widget.getWidth() == client.getViewportWidth() && widget.getHeight() == client.getViewportHeight() - && widget.getOpacity() > 0 && widget.isFilled() && client.isGpu()) - { - int tc = widget.getTextColor(); - int alpha = widget.getOpacity() & 0xFF; - int inverseAlpha = 256 - alpha; - int vpc = viewportColor; - int c1 = (alpha * (tc & 0xff00ff) >> 8 & 0xFF00FF) + (alpha * (tc & 0x00FF00) >> 8 & 0x00FF00); - int c2 = (inverseAlpha * (vpc & 0xff00ff) >> 8 & 0xFF00FF) + (inverseAlpha * (vpc & 0x00FF00) >> 8 & 0x00FF00); - int outAlpha = inverseAlpha + ((vpc >>> 24) * (255 - alpha) * 0x8081 >>> 23); - viewportColor = outAlpha << 24 | c1 + c2; - widget.setHidden(true); - hiddenWidgets.add(widget); - continue; - } + int tc = widget.getTextColor(); + int alpha = widget.getOpacity() & 0xFF; + int inverseAlpha = 256 - alpha; + int vpc = viewportColor; + int c1 = (inverseAlpha * (tc & 0xFF00FF) >> 8 & 0xFF00FF) + (inverseAlpha * (tc & 0x00FF00) >> 8 & 0x00FF00); + int c2 = (alpha * (vpc & 0xFF00FF) >> 8 & 0xFF00FF) + (alpha * (vpc & 0x00FF00) >> 8 & 0x00FF00); + int outAlpha = inverseAlpha + ((vpc >>> 24) * (255 - inverseAlpha) * 0x8081 >>> 23); + viewportColor = outAlpha << 24 | c1 + c2; + widget.setHidden(true); + hiddenWidgets.add(widget); } - - WidgetNode childNode = componentTable.get(widget.getId()); - if (childNode != null) + else { - int widgetId = widget.getId(); - int groupId = childNode.getId(); - RSWidget[] children = client.getWidgets()[groupId]; - - for (RSWidget child : children) + WidgetNode childNode = componentTable.get(widget.getId()); + if (childNode != null) { - if (child.getRSParentId() == -1) + int widgetId = widget.getId(); + int groupId = childNode.getId(); + RSWidget[] children = client.getWidgets()[groupId]; + + for (RSWidget child : children) { - child.setRenderParentId(widgetId); + if (child.getRSParentId() == -1) + { + child.setRenderParentId(widgetId); + } } } } diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSEnumerated.java b/runescape-api/src/main/java/net/runelite/rs/api/RSEnumerated.java index 1b97cfebc9..3189893968 100644 --- a/runescape-api/src/main/java/net/runelite/rs/api/RSEnumerated.java +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSEnumerated.java @@ -1,3 +1,9 @@ package net.runelite.rs.api; -public interface RSEnumerated {} +import net.runelite.mapping.Import; + +public interface RSEnumerated +{ + @Import("rsOrdinal") + int getOrdinal(); +} 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 57546ccfe4..9f22cac637 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 @@ -589,5 +589,8 @@ public interface RSWidget extends Widget @Override void setOnVarTransmitListener(Object[] o); + @Import("fillMode") + RSWidgetFillMode getFillMode(); + void broadcastHidden(boolean hidden); } \ No newline at end of file diff --git a/runescape-api/src/main/java/net/runelite/rs/api/RSWidgetFillMode.java b/runescape-api/src/main/java/net/runelite/rs/api/RSWidgetFillMode.java new file mode 100644 index 0000000000..14495522a4 --- /dev/null +++ b/runescape-api/src/main/java/net/runelite/rs/api/RSWidgetFillMode.java @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2021, ThatGamerBlue + * 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; + +public interface RSWidgetFillMode extends RSEnumerated +{ +} diff --git a/runescape-client/src/main/java/NetSocket.java b/runescape-client/src/main/java/NetSocket.java index dfc77db500..c872bafa17 100644 --- a/runescape-client/src/main/java/NetSocket.java +++ b/runescape-client/src/main/java/NetSocket.java @@ -333,8 +333,8 @@ public final class NetSocket extends AbstractSocket implements Runnable { garbageValue = "8" ) @Export("FillMode_values") - public static class333[] FillMode_values() { - return new class333[]{class333.field3897, class333.field3898, class333.SOLID}; // L: 15 + public static WidgetFillMode[] FillMode_values() { + return new WidgetFillMode[]{WidgetFillMode.field3897, WidgetFillMode.field3898, WidgetFillMode.SOLID}; // L: 15 } @ObfuscatedName("gh") diff --git a/runescape-client/src/main/java/Widget.java b/runescape-client/src/main/java/Widget.java index 0f8b51840e..c25c1e7d78 100644 --- a/runescape-client/src/main/java/Widget.java +++ b/runescape-client/src/main/java/Widget.java @@ -213,7 +213,7 @@ public class Widget extends Node { descriptor = "Llj;" ) @Export("fillMode") - public class333 fillMode; + public WidgetFillMode fillMode; @ObfuscatedName("ar") @ObfuscatedGetter( intValue = -318915915 @@ -711,7 +711,7 @@ public class Widget extends Node { this.mouseOverColor = 0; this.mouseOverColor2 = 0; this.fill = false; - this.fillMode = class333.SOLID; + this.fillMode = WidgetFillMode.SOLID; this.transparencyTop = 0; this.transparencyBot = 0; this.lineWid = 1; diff --git a/runescape-client/src/main/java/class333.java b/runescape-client/src/main/java/WidgetFillMode.java similarity index 85% rename from runescape-client/src/main/java/class333.java rename to runescape-client/src/main/java/WidgetFillMode.java index 0ab3733dad..d7651a784f 100644 --- a/runescape-client/src/main/java/class333.java +++ b/runescape-client/src/main/java/WidgetFillMode.java @@ -1,10 +1,12 @@ import net.runelite.mapping.Export; +import net.runelite.mapping.Implements; import net.runelite.mapping.ObfuscatedGetter; import net.runelite.mapping.ObfuscatedName; import net.runelite.mapping.ObfuscatedSignature; @ObfuscatedName("lj") -public enum class333 implements Enumerated { +@Implements("WidgetFillMode") +public enum WidgetFillMode implements Enumerated { @ObfuscatedName("h") @ObfuscatedSignature( descriptor = "Llj;" @@ -33,7 +35,7 @@ public enum class333 implements Enumerated { ) final int field3900; - class333(int var3, int var4) { + WidgetFillMode(int var3, int var4) { this.field3902 = var3; // L: 19 this.field3900 = var4; // L: 20 } // L: 21 diff --git a/runescape-client/src/main/java/WorldMapData_1.java b/runescape-client/src/main/java/WorldMapData_1.java index 40ccbef1e6..346b7c4c0c 100644 --- a/runescape-client/src/main/java/WorldMapData_1.java +++ b/runescape-client/src/main/java/WorldMapData_1.java @@ -340,7 +340,7 @@ public class WorldMapData_1 extends AbstractWorldMapData { return 1; // L: 708 } else if (var0 == ScriptOpcodes.CC_SETFILLMODE) { // L: 710 var8 = Interpreter.Interpreter_intStack[--VarcInt.Interpreter_intStackSize]; // L: 711 - class333 var6 = (class333)UrlRequester.findEnumerated(NetSocket.FillMode_values(), var8); // L: 712 + WidgetFillMode var6 = (WidgetFillMode)UrlRequester.findEnumerated(NetSocket.FillMode_values(), var8); // L: 712 if (var6 != null) { // L: 713 var3.fillMode = var6; // L: 714 CollisionMap.invalidateWidget(var3); // L: 715 From afd057cbb0e231d3cc2e430723ba6545ddc17289 Mon Sep 17 00:00:00 2001 From: ThatGamerBlue Date: Fri, 19 Feb 2021 15:05:19 +0000 Subject: [PATCH 128/133] injector: update license location --- injector/LICENSE | 674 ++++++++++++++++++ .../openosrs/injector/InjectException.java | 2 +- .../com/openosrs/injector/InjectUtil.java | 2 +- .../java/com/openosrs/injector/Injector.java | 2 +- .../openosrs/injector/InjectorValidator.java | 2 +- .../java/com/openosrs/injector/Validator.java | 2 +- .../injector/injection/InjectData.java | 2 +- .../injector/injection/InjectTaskHandler.java | 2 +- .../injector/injectors/AbstractInjector.java | 2 +- .../injector/injectors/CreateAnnotations.java | 2 +- .../injector/injectors/InjectConstruct.java | 2 +- .../injector/injectors/InjectHook.java | 2 +- .../injector/injectors/InjectHookMethod.java | 2 +- .../openosrs/injector/injectors/Injector.java | 2 +- .../injector/injectors/InterfaceInjector.java | 2 +- .../injector/injectors/MixinInjector.java | 2 +- .../injector/injectors/RSApiInjector.java | 2 +- .../injectors/raw/AddPlayerToMenu.java | 2 +- .../injectors/raw/ClearColorBuffer.java | 2 +- .../injectors/raw/DrawAfterWidgets.java | 2 +- .../injector/injectors/raw/DrawMenu.java | 2 +- .../injector/injectors/raw/Occluder.java | 2 +- .../injectors/raw/RasterizerAlpha.java | 2 +- .../injector/injectors/raw/RenderDraw.java | 2 +- .../injector/injectors/raw/ScriptVM.java | 2 +- .../injectors/rsapi/InjectGetter.java | 2 +- .../injectors/rsapi/InjectInvoke.java | 2 +- .../injectors/rsapi/InjectSetter.java | 2 +- .../com/openosrs/injector/rsapi/RSApi.java | 2 +- .../openosrs/injector/rsapi/RSApiClass.java | 2 +- .../openosrs/injector/rsapi/RSApiMethod.java | 2 +- .../transformers/InjectTransformer.java | 2 +- .../injector/transformers/Java8Ifier.java | 2 +- .../injector/transformers/SourceChanger.java | 2 +- 34 files changed, 707 insertions(+), 33 deletions(-) create mode 100644 injector/LICENSE diff --git a/injector/LICENSE b/injector/LICENSE new file mode 100644 index 0000000000..e72bfddabc --- /dev/null +++ b/injector/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. \ No newline at end of file diff --git a/injector/src/main/java/com/openosrs/injector/InjectException.java b/injector/src/main/java/com/openosrs/injector/InjectException.java index aa1a352a0d..d3297ec78e 100644 --- a/injector/src/main/java/com/openosrs/injector/InjectException.java +++ b/injector/src/main/java/com/openosrs/injector/InjectException.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector; diff --git a/injector/src/main/java/com/openosrs/injector/InjectUtil.java b/injector/src/main/java/com/openosrs/injector/InjectUtil.java index f55539fb34..79ceabb196 100644 --- a/injector/src/main/java/com/openosrs/injector/InjectUtil.java +++ b/injector/src/main/java/com/openosrs/injector/InjectUtil.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector; diff --git a/injector/src/main/java/com/openosrs/injector/Injector.java b/injector/src/main/java/com/openosrs/injector/Injector.java index e83ee1ce0b..95df2a568f 100644 --- a/injector/src/main/java/com/openosrs/injector/Injector.java +++ b/injector/src/main/java/com/openosrs/injector/Injector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector; diff --git a/injector/src/main/java/com/openosrs/injector/InjectorValidator.java b/injector/src/main/java/com/openosrs/injector/InjectorValidator.java index 1e3bc6b739..c8664dc160 100644 --- a/injector/src/main/java/com/openosrs/injector/InjectorValidator.java +++ b/injector/src/main/java/com/openosrs/injector/InjectorValidator.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector; diff --git a/injector/src/main/java/com/openosrs/injector/Validator.java b/injector/src/main/java/com/openosrs/injector/Validator.java index ba3b7ef037..d4679c7a35 100644 --- a/injector/src/main/java/com/openosrs/injector/Validator.java +++ b/injector/src/main/java/com/openosrs/injector/Validator.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector; diff --git a/injector/src/main/java/com/openosrs/injector/injection/InjectData.java b/injector/src/main/java/com/openosrs/injector/injection/InjectData.java index b201d83c06..378bc42d38 100644 --- a/injector/src/main/java/com/openosrs/injector/injection/InjectData.java +++ b/injector/src/main/java/com/openosrs/injector/injection/InjectData.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injection; diff --git a/injector/src/main/java/com/openosrs/injector/injection/InjectTaskHandler.java b/injector/src/main/java/com/openosrs/injector/injection/InjectTaskHandler.java index a6f29b6f3e..f391d0c321 100644 --- a/injector/src/main/java/com/openosrs/injector/injection/InjectTaskHandler.java +++ b/injector/src/main/java/com/openosrs/injector/injection/InjectTaskHandler.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injection; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/AbstractInjector.java b/injector/src/main/java/com/openosrs/injector/injectors/AbstractInjector.java index b1c645c544..3554dd3b91 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/AbstractInjector.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/AbstractInjector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/CreateAnnotations.java b/injector/src/main/java/com/openosrs/injector/injectors/CreateAnnotations.java index 4bdc647cf3..f29ff939f9 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/CreateAnnotations.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/CreateAnnotations.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/InjectConstruct.java b/injector/src/main/java/com/openosrs/injector/injectors/InjectConstruct.java index 0dde556645..9fcb6085b4 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/InjectConstruct.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/InjectConstruct.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/InjectHook.java b/injector/src/main/java/com/openosrs/injector/injectors/InjectHook.java index a7e74e848d..ff8a533cd5 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/InjectHook.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/InjectHook.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2017, Adam * All rights reserved. * diff --git a/injector/src/main/java/com/openosrs/injector/injectors/InjectHookMethod.java b/injector/src/main/java/com/openosrs/injector/injectors/InjectHookMethod.java index b4ca566c3d..0023b4d3e6 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/InjectHookMethod.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/InjectHookMethod.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/Injector.java b/injector/src/main/java/com/openosrs/injector/injectors/Injector.java index a3d9048e47..e1e497b9e9 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/Injector.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/Injector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/InterfaceInjector.java b/injector/src/main/java/com/openosrs/injector/injectors/InterfaceInjector.java index 5e12bd4c00..19fa8d8033 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/InterfaceInjector.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/InterfaceInjector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/MixinInjector.java b/injector/src/main/java/com/openosrs/injector/injectors/MixinInjector.java index f9d0b8e472..de042f5514 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/MixinInjector.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/MixinInjector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/RSApiInjector.java b/injector/src/main/java/com/openosrs/injector/injectors/RSApiInjector.java index d904d3cb26..aaa6e21493 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/RSApiInjector.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/RSApiInjector.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/raw/AddPlayerToMenu.java b/injector/src/main/java/com/openosrs/injector/injectors/raw/AddPlayerToMenu.java index 578b30b005..672a6d10b1 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/raw/AddPlayerToMenu.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/raw/AddPlayerToMenu.java @@ -4,7 +4,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java b/injector/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java index 762e269613..6f81ecd8c8 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/raw/ClearColorBuffer.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java b/injector/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java index 5f6d8c2d40..60a2a900fb 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/raw/DrawAfterWidgets.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/raw/DrawMenu.java b/injector/src/main/java/com/openosrs/injector/injectors/raw/DrawMenu.java index b54c568be1..8208405f16 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/raw/DrawMenu.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/raw/DrawMenu.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/raw/Occluder.java b/injector/src/main/java/com/openosrs/injector/injectors/raw/Occluder.java index 14d44773c5..0425462438 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/raw/Occluder.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/raw/Occluder.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java b/injector/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java index bf38cd42d5..96e5bb4712 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/raw/RasterizerAlpha.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/raw/RenderDraw.java b/injector/src/main/java/com/openosrs/injector/injectors/raw/RenderDraw.java index c31802a391..a36feabad8 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/raw/RenderDraw.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/raw/RenderDraw.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.injectors.raw; diff --git a/injector/src/main/java/com/openosrs/injector/injectors/raw/ScriptVM.java b/injector/src/main/java/com/openosrs/injector/injectors/raw/ScriptVM.java index 1d9c463828..e103bc50ab 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/raw/ScriptVM.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/raw/ScriptVM.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2018 Abex * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectGetter.java b/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectGetter.java index 696753b2b0..29efa77920 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectGetter.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectGetter.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java b/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java index b87e6385ad..3b98e2d856 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectInvoke.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2016-2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectSetter.java b/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectSetter.java index 9eb137d2b7..59eda4e2e4 100644 --- a/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectSetter.java +++ b/injector/src/main/java/com/openosrs/injector/injectors/rsapi/InjectSetter.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. * * Copyright (c) 2017, Adam * All rights reserved. diff --git a/injector/src/main/java/com/openosrs/injector/rsapi/RSApi.java b/injector/src/main/java/com/openosrs/injector/rsapi/RSApi.java index 103e1619ad..def5997c75 100644 --- a/injector/src/main/java/com/openosrs/injector/rsapi/RSApi.java +++ b/injector/src/main/java/com/openosrs/injector/rsapi/RSApi.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.rsapi; diff --git a/injector/src/main/java/com/openosrs/injector/rsapi/RSApiClass.java b/injector/src/main/java/com/openosrs/injector/rsapi/RSApiClass.java index 5283095a72..84a1d55036 100644 --- a/injector/src/main/java/com/openosrs/injector/rsapi/RSApiClass.java +++ b/injector/src/main/java/com/openosrs/injector/rsapi/RSApiClass.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.rsapi; diff --git a/injector/src/main/java/com/openosrs/injector/rsapi/RSApiMethod.java b/injector/src/main/java/com/openosrs/injector/rsapi/RSApiMethod.java index 9700f25330..4d512f2105 100644 --- a/injector/src/main/java/com/openosrs/injector/rsapi/RSApiMethod.java +++ b/injector/src/main/java/com/openosrs/injector/rsapi/RSApiMethod.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.rsapi; diff --git a/injector/src/main/java/com/openosrs/injector/transformers/InjectTransformer.java b/injector/src/main/java/com/openosrs/injector/transformers/InjectTransformer.java index 270b8b7b33..798947c832 100644 --- a/injector/src/main/java/com/openosrs/injector/transformers/InjectTransformer.java +++ b/injector/src/main/java/com/openosrs/injector/transformers/InjectTransformer.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.transformers; diff --git a/injector/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java b/injector/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java index 9d5e981d3f..7d37c48368 100644 --- a/injector/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java +++ b/injector/src/main/java/com/openosrs/injector/transformers/Java8Ifier.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.transformers; diff --git a/injector/src/main/java/com/openosrs/injector/transformers/SourceChanger.java b/injector/src/main/java/com/openosrs/injector/transformers/SourceChanger.java index 7e56e46cb3..a45a8aedde 100644 --- a/injector/src/main/java/com/openosrs/injector/transformers/SourceChanger.java +++ b/injector/src/main/java/com/openosrs/injector/transformers/SourceChanger.java @@ -3,7 +3,7 @@ * All rights reserved. * * This code is licensed under GPL3, see the complete license in - * the LICENSE file in the root directory of this source tree. + * the LICENSE file in the root directory of this submodule. */ package com.openosrs.injector.transformers; From 8ea338dace6745f60e90cd3e7038d580d3bbc79e Mon Sep 17 00:00:00 2001 From: Owain van Brakel Date: Fri, 19 Feb 2021 16:06:56 +0100 Subject: [PATCH 129/133] externals: Filter out available plugins that do not constraint to the API version --- .../client/plugins/OPRSExternalPluginManager.java | 1 + .../plugins/openosrs/externals/PluginsPanel.java | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java index e1fc79ce71..2a8bf5159a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/OPRSExternalPluginManager.java @@ -98,6 +98,7 @@ public class OPRSExternalPluginManager public static ArrayList pluginClassLoaders = new ArrayList<>(); private final PluginManager runelitePluginManager; + @Getter(AccessLevel.PUBLIC) private org.pf4j.PluginManager externalPluginManager; @Getter(AccessLevel.PUBLIC) private final List repositories = new ArrayList<>(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java index 6989e2ae19..5ce1dc4400 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/openosrs/externals/PluginsPanel.java @@ -45,6 +45,7 @@ import net.runelite.client.ui.components.IconTextField; import net.runelite.client.ui.components.shadowlabel.JShadowedLabel; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.SwingUtil; +import org.pf4j.VersionManager; import org.pf4j.update.PluginInfo; import org.pf4j.update.UpdateManager; import org.pf4j.update.UpdateRepository; @@ -83,6 +84,7 @@ public class PluginsPanel extends JPanel } private final OPRSExternalPluginManager externalPluginManager; + private final VersionManager versionManager; private final UpdateManager updateManager; private final ScheduledExecutorService executor; private final EventBus eventBus; @@ -100,6 +102,7 @@ public class PluginsPanel extends JPanel PluginsPanel(OPRSExternalPluginManager externalPluginManager, ScheduledExecutorService executor, EventBus eventBus) { this.externalPluginManager = externalPluginManager; + this.versionManager = externalPluginManager.getExternalPluginManager().getVersionManager(); this.updateManager = externalPluginManager.getUpdateManager(); this.executor = executor; this.eventBus = eventBus; @@ -410,6 +413,13 @@ public class PluginsPanel extends JPanel for (PluginInfo pluginInfo : availablePluginsList) { + if (pluginInfo.releases + .stream() + .noneMatch((pluginRelease) -> versionManager.checkVersionConstraint(externalPluginManager.getExternalPluginManager().getSystemVersion(), pluginRelease.requires))) + { + continue; + } + if (!search.equals("") && mismatchesSearchTerms(search, pluginInfo)) { continue; From 6523f7299b7085aafa657272835d54737f69c361 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 19 Feb 2021 13:41:55 -0500 Subject: [PATCH 130/133] client: use value from properties --- runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java b/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java index 00a5521144..4d64898bce 100644 --- a/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java +++ b/runelite-client/src/main/java/com/openosrs/client/OpenOSRS.java @@ -26,7 +26,7 @@ public class OpenOSRS e.printStackTrace(); } SYSTEM_VERSION = properties.getProperty("oprs.version", "0.0.0"); - SYSTEM_API_VERSION = properties.getProperty("oprs.api.version", "1.0.0"); + SYSTEM_API_VERSION = properties.getProperty("oprs.api.version"); } public static void preload() From 6e0e7d01a4fc80897e30fe62e765e67cdf6db851 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 19 Feb 2021 13:57:56 -0500 Subject: [PATCH 131/133] api: ahem what happened here lol --- runelite-api/src/main/java/net/runelite/api/events/Event.java | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 runelite-api/src/main/java/net/runelite/api/events/Event.java diff --git a/runelite-api/src/main/java/net/runelite/api/events/Event.java b/runelite-api/src/main/java/net/runelite/api/events/Event.java new file mode 100644 index 0000000000..39070dd8a2 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/events/Event.java @@ -0,0 +1,3 @@ +package net.runelite.api.events; + +public interface Event {} \ No newline at end of file From 75a4e568000800ba9cd57c71eb40bf7a5d8ab0cb Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 19 Feb 2021 15:16:19 -0500 Subject: [PATCH 132/133] bootstrap: add jocl --- buildSrc/src/main/kotlin/BootstrapTask.kt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/buildSrc/src/main/kotlin/BootstrapTask.kt b/buildSrc/src/main/kotlin/BootstrapTask.kt index 0e141ee23d..2a022557cc 100644 --- a/buildSrc/src/main/kotlin/BootstrapTask.kt +++ b/buildSrc/src/main/kotlin/BootstrapTask.kt @@ -72,6 +72,10 @@ open class BootstrapTask @Inject constructor(@Input val type: String) : DefaultT { path = "http://repo.runelite.net/net/runelite/jogl/jogl-all/" + version + "/" + it.file.name } + else if (it.file.name.contains("jocl")) + { + path = "http://repo.runelite.net/net/runelite/jocl/jocl/" + version + "/" + it.file.name + } else { println("ERROR: " + it.file.name + " has no download path!") From 1790f10b27cfe82704b44559c3f502d2b9639e39 Mon Sep 17 00:00:00 2001 From: zeruth Date: Fri, 19 Feb 2021 15:36:11 -0500 Subject: [PATCH 133/133] Revert "api: ahem what happened here lol" This reverts commit 6e0e7d01a4fc80897e30fe62e765e67cdf6db851. --- runelite-api/src/main/java/net/runelite/api/events/Event.java | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 runelite-api/src/main/java/net/runelite/api/events/Event.java diff --git a/runelite-api/src/main/java/net/runelite/api/events/Event.java b/runelite-api/src/main/java/net/runelite/api/events/Event.java deleted file mode 100644 index 39070dd8a2..0000000000 --- a/runelite-api/src/main/java/net/runelite/api/events/Event.java +++ /dev/null @@ -1,3 +0,0 @@ -package net.runelite.api.events; - -public interface Event {} \ No newline at end of file