From d84caf7db2b98444de3171f7efc064a2c0ed534d Mon Sep 17 00:00:00 2001 From: leejt Date: Mon, 13 Jan 2020 17:02:11 -0800 Subject: [PATCH 01/14] Update WorldMapDefinition attributes Changed the WorldMapType field names from fieldxxx to values that represent what they actually mean (mostly x, y, z coordinates) --- .../cache/definitions/WorldMapDefinition.java | 6 +- .../cache/definitions/WorldMapType0.java | 20 ++--- .../cache/definitions/WorldMapType1.java | 20 ++--- .../cache/definitions/WorldMapType2.java | 12 +-- .../cache/definitions/WorldMapType3.java | 28 +++--- .../definitions/loaders/WorldMapLoader.java | 88 +++++++++---------- 6 files changed, 87 insertions(+), 87 deletions(-) diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java index a8333263e5..30c61f73b4 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapDefinition.java @@ -33,13 +33,13 @@ public class WorldMapDefinition { public String name; public int field450; - public int field451; + public int defaultZoom; public int fileId; public int field453; public int field454; public int field456; - public boolean field457; - public List field458; + public boolean isSurface; + public List regionList; public String safeName; public Position position; public int field463; diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapType0.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapType0.java index 1294d0eb4d..cecc41f524 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/WorldMapType0.java +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapType0.java @@ -29,14 +29,14 @@ import lombok.Data; @Data public class WorldMapType0 implements WorldMapTypeBase { - public int field600; - public int field601; - public int field602; - public int field603; - public int field604; - public int field605; - public int field606; - public int field607; - public int field608; - public int field609; + public int chunk_xHigh; + public int xLow; + public int chunk_xLow; + public int yLow; + public int xHigh; + public int numberOfPlanes; + public int plane; + public int chunk_yLow; + public int yHigh; + public int chunk_yHigh; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapType1.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapType1.java index 15a4f3e28d..4d4cd50d21 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/WorldMapType1.java +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapType1.java @@ -29,14 +29,14 @@ import lombok.Data; @Data public class WorldMapType1 implements WorldMapTypeBase { - public int field424; - public int field425; - public int field426; - public int field427; - public int field428; - public int field429; - public int field431; - public int field433; - public int field434; - public int field435; + public int numberOfPlanes; + public int xLowerLeft; + public int yLowerLeft; + public int xLowerRight; + public int yLowerRight; + public int xUpperLeft; + public int yUpperLeft; + public int xUpperRight; + public int plane; + public int yUpperRight; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapType2.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapType2.java index 7b5326814d..3670f99dd0 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/WorldMapType2.java +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapType2.java @@ -29,10 +29,10 @@ import lombok.Data; @Data public class WorldMapType2 implements WorldMapTypeBase { - public int field510; - public int field511; - public int field512; - public int field514; - public int field515; - public int field519; + public int xLow; + public int numberOfPlanes; + public int yLow; + public int xHigh; + public int yHigh; + public int plane; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapType3.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapType3.java index 433c8a17fa..10d55418e0 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/WorldMapType3.java +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapType3.java @@ -29,18 +29,18 @@ import lombok.Data; @Data public class WorldMapType3 implements WorldMapTypeBase { - public int field376; - public int field377; - public int field378; - public int field379; - public int field380; - public int field381; - public int field382; - public int field383; - public int field384; - public int field385; - public int field386; - public int field387; - public int field388; - public int field389; + public int chunk_oldXHigh; + public int numberOfPlanes; + public int oldX; + public int chunk_oldYHigh; + public int newX; + public int newY; + public int chunk_oldXLow; + public int oldY; + public int chunk_newYLow; + public int chunk_oldYLow; + public int chunk_newXLow; + public int oldPlane; + public int chunk_newXHigh; + public int chunk_newYHigh; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java index 15ec975d6d..55bc6af71e 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java @@ -60,14 +60,14 @@ public class WorldMapLoader def.field450 = in.readInt(); in.readUnsignedByte(); - def.field457 = in.readUnsignedByte() == 1; - def.field451 = in.readUnsignedByte(); + def.isSurface = in.readUnsignedByte() == 1; + def.defaultZoom = in.readUnsignedByte(); int var3 = in.readUnsignedByte(); - def.field458 = new LinkedList(); + def.regionList = new LinkedList(); for (int var4 = 0; var4 < var3; ++var4) { - def.field458.add(this.loadType(in)); + def.regionList.add(this.loadType(in)); } return def; @@ -109,16 +109,16 @@ public class WorldMapLoader { WorldMapType0 wm = new WorldMapType0(); - wm.field606 = in.readUnsignedByte(); - wm.field605 = in.readUnsignedByte(); - wm.field601 = in.readUnsignedShort(); - wm.field602 = in.readUnsignedByte(); - wm.field603 = in.readUnsignedShort(); - wm.field607 = in.readUnsignedByte(); - wm.field604 = in.readUnsignedShort(); - wm.field600 = in.readUnsignedByte(); - wm.field608 = in.readUnsignedShort(); - wm.field609 = in.readUnsignedByte(); + wm.plane = in.readUnsignedByte(); + wm.numberOfPlanes = in.readUnsignedByte(); + wm.xLow = in.readUnsignedShort(); + wm.chunk_xLow = in.readUnsignedByte(); + wm.yLow = in.readUnsignedShort(); + wm.chunk_yLow = in.readUnsignedByte(); + wm.xHigh = in.readUnsignedShort(); + wm.chunk_xHigh = in.readUnsignedByte(); + wm.yHigh = in.readUnsignedShort(); + wm.chunk_yHigh = in.readUnsignedByte(); return wm; } @@ -127,16 +127,16 @@ public class WorldMapLoader { WorldMapType1 wm = new WorldMapType1(); - wm.field434 = in.readUnsignedByte(); - wm.field424 = in.readUnsignedByte(); - wm.field425 = in.readUnsignedShort(); - wm.field426 = in.readUnsignedShort(); - wm.field427 = in.readUnsignedShort(); - wm.field431 = in.readUnsignedShort(); - wm.field429 = in.readUnsignedShort(); - wm.field428 = in.readUnsignedShort(); - wm.field433 = in.readUnsignedShort(); - wm.field435 = in.readUnsignedShort(); + wm.plane = in.readUnsignedByte(); + wm.numberOfPlanes = in.readUnsignedByte(); + wm.xLowerLeft = in.readUnsignedShort(); + wm.yLowerLeft = in.readUnsignedShort(); + wm.xLowerRight = in.readUnsignedShort(); + wm.yUpperLeft = in.readUnsignedShort(); + wm.xUpperLeft = in.readUnsignedShort(); + wm.yLowerRight = in.readUnsignedShort(); + wm.xUpperRight = in.readUnsignedShort(); + wm.yUpperRight = in.readUnsignedShort(); return wm; } @@ -145,12 +145,12 @@ public class WorldMapLoader { WorldMapType2 wm = new WorldMapType2(); - wm.field519 = in.readUnsignedByte(); - wm.field511 = in.readUnsignedByte(); - wm.field510 = in.readUnsignedShort(); - wm.field512 = in.readUnsignedShort(); - wm.field514 = in.readUnsignedShort(); - wm.field515 = in.readUnsignedShort(); + wm.plane = in.readUnsignedByte(); + wm.numberOfPlanes = in.readUnsignedByte(); + wm.xLow = in.readUnsignedShort(); + wm.yLow = in.readUnsignedShort(); + wm.xHigh = in.readUnsignedShort(); + wm.yHigh = in.readUnsignedShort(); return wm; } @@ -159,20 +159,20 @@ public class WorldMapLoader { WorldMapType3 wm = new WorldMapType3(); - wm.field387 = in.readUnsignedByte(); - wm.field377 = in.readUnsignedByte(); - wm.field378 = in.readUnsignedShort(); - wm.field382 = in.readUnsignedByte(); - wm.field376 = in.readUnsignedByte(); - wm.field383 = in.readUnsignedShort(); - wm.field385 = in.readUnsignedByte(); - wm.field379 = in.readUnsignedByte(); - wm.field380 = in.readUnsignedShort(); - wm.field386 = in.readUnsignedByte(); - wm.field388 = in.readUnsignedByte(); - wm.field381 = in.readUnsignedShort(); - wm.field384 = in.readUnsignedByte(); - wm.field389 = in.readUnsignedByte(); + wm.oldPlane = in.readUnsignedByte(); + wm.numberOfPlanes = in.readUnsignedByte(); + wm.oldX = in.readUnsignedShort(); + wm.chunk_oldXLow = in.readUnsignedByte(); + wm.chunk_oldXHigh = in.readUnsignedByte(); + wm.oldY = in.readUnsignedShort(); + wm.chunk_oldYLow = in.readUnsignedByte(); + wm.chunk_oldYHigh = in.readUnsignedByte(); + wm.newX = in.readUnsignedShort(); + wm.chunk_newXLow = in.readUnsignedByte(); + wm.chunk_newXHigh = in.readUnsignedByte(); + wm.newY = in.readUnsignedShort(); + wm.chunk_newYLow = in.readUnsignedByte(); + wm.chunk_newYHigh = in.readUnsignedByte(); return wm; } From 9e7705e617aa213810a9e2e6839f68de387b037c Mon Sep 17 00:00:00 2001 From: Gamma91 <7499230+Bram91@users.noreply.github.com> Date: Fri, 17 Jan 2020 09:03:33 +0100 Subject: [PATCH 02/14] Fix typo in 'Swap Bank Op' description --- .../client/plugins/menuentryswapper/MenuEntrySwapperConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 70d8c36f43..da98cfb9d4 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 @@ -385,7 +385,7 @@ public interface MenuEntrySwapperConfig extends Config @ConfigItem( keyName = "swapBankOp", name = "Swap Bank Op", - description = "Swaps the extra menu option in banks (Weild, Eat, etc.) when holding shift" + description = "Swaps the extra menu option in banks (Wield, Eat, etc.) when holding shift" ) default boolean swapBankOp() { From 97b4f0d56f02acc8edea6027ee6e179de5032838 Mon Sep 17 00:00:00 2001 From: Adam Date: Thu, 9 Jan 2020 17:35:17 -0500 Subject: [PATCH 03/14] loot tracker: store aggregated drops This modifies both the service and the client plugin to no longer store loot per-kill, with the exception of the current sessions kill log. The amount of loot data returned now from the service defaults to the last 1024 unique events --- .../http/api/loottracker/LootAggregate.java | 44 +++++++ .../api/loottracker/LootTrackerClient.java | 4 +- .../http/service/loottracker/LootResult.java | 4 +- .../loottracker/LootTrackerController.java | 3 +- .../loottracker/LootTrackerService.java | 88 ++++++-------- .../plugins/loottracker/LootTrackerBox.java | 115 +++++++----------- .../plugins/loottracker/LootTrackerItem.java | 23 ++-- .../plugins/loottracker/LootTrackerPanel.java | 69 +++++++---- .../loottracker/LootTrackerPlugin.java | 14 +-- .../loottracker/LootTrackerRecord.java | 2 + 10 files changed, 203 insertions(+), 163 deletions(-) create mode 100644 http-api/src/main/java/net/runelite/http/api/loottracker/LootAggregate.java diff --git a/http-api/src/main/java/net/runelite/http/api/loottracker/LootAggregate.java b/http-api/src/main/java/net/runelite/http/api/loottracker/LootAggregate.java new file mode 100644 index 0000000000..a5437226cc --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/loottracker/LootAggregate.java @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.http.api.loottracker; + +import java.time.Instant; +import java.util.Collection; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class LootAggregate +{ + private String eventId; + private LootRecordType type; + private Collection drops; + private Instant first_time; + private Instant last_time; + private int amount; +} diff --git a/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java b/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java index 71eb151364..793b661fdd 100644 --- a/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java +++ b/http-api/src/main/java/net/runelite/http/api/loottracker/LootTrackerClient.java @@ -81,7 +81,7 @@ public class LootTrackerClient }); } - public Collection get() throws IOException + public Collection get() throws IOException { HttpUrl url = RuneLiteAPI.getApiBase().newBuilder() .addPathSegment("loottracker") @@ -101,7 +101,7 @@ public class LootTrackerClient } InputStream in = response.body().byteStream(); - return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), new TypeToken>() + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), new TypeToken>() { }.getType()); } diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootResult.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootResult.java index 5bfd7afee0..4cb3375154 100644 --- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootResult.java +++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootResult.java @@ -32,9 +32,11 @@ import net.runelite.http.api.loottracker.LootRecordType; class LootResult { private int killId; - private Instant time; + private Instant first_time; + private Instant last_time; private LootRecordType type; private String eventId; + private int amount; private int itemId; private int itemQuantity; } diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java index 1033d62187..ac4da593c6 100644 --- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java +++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java @@ -30,6 +30,7 @@ import java.io.IOException; import java.util.Collection; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import net.runelite.http.api.loottracker.LootAggregate; import net.runelite.http.api.loottracker.LootRecord; import net.runelite.http.service.account.AuthFilter; import net.runelite.http.service.account.beans.SessionEntry; @@ -67,7 +68,7 @@ public class LootTrackerController } @GetMapping - public Collection getLootRecords(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "count", defaultValue = "1024") int count, @RequestParam(value = "start", defaultValue = "0") int start) throws IOException + public Collection getLootAggregate(HttpServletRequest request, HttpServletResponse response, @RequestParam(value = "count", defaultValue = "1024") int count, @RequestParam(value = "start", defaultValue = "0") int start) throws IOException { SessionEntry e = auth.handle(request, response); if (e == null) diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java index 8f97ce144d..f7f9ac5004 100644 --- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java +++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerService.java @@ -29,6 +29,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.List; import net.runelite.http.api.loottracker.GameItem; +import net.runelite.http.api.loottracker.LootAggregate; import net.runelite.http.api.loottracker.LootRecord; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; @@ -41,35 +42,37 @@ import org.sql2o.Sql2o; @Service public class LootTrackerService { - // Table for storing individual LootRecords - private static final String CREATE_KILLS = "CREATE TABLE IF NOT EXISTS `kills` (\n" - + " `id` INT AUTO_INCREMENT UNIQUE,\n" - + " `time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),\n" - + " `accountId` INT NOT NULL,\n" - + " `type` enum('NPC', 'PLAYER', 'EVENT', 'UNKNOWN') NOT NULL,\n" - + " `eventId` VARCHAR(255) NOT NULL,\n" - + " PRIMARY KEY (id),\n" - + " FOREIGN KEY (accountId) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,\n" - + " INDEX idx_acc (accountId, time)," - + " INDEX idx_time (time)" - + ") ENGINE=InnoDB"; + private static final String CREATE_KILLS = "CREATE TABLE IF NOT EXISTS `loottracker_kills` (\n" + + " `id` int(11) NOT NULL AUTO_INCREMENT,\n" + + " `first_time` timestamp NOT NULL DEFAULT current_timestamp(),\n" + + " `last_time` timestamp NOT NULL DEFAULT current_timestamp() ON UPDATE current_timestamp(),\n" + + " `accountId` int(11) NOT NULL,\n" + + " `type` enum('NPC','PLAYER','EVENT','UNKNOWN') NOT NULL,\n" + + " `eventId` varchar(255) NOT NULL,\n" + + " `amount` int(11) NOT NULL,\n" + + " PRIMARY KEY (`id`),\n" + + " FOREIGN KEY (accountId) REFERENCES users(id) ON DELETE CASCADE ON UPDATE CASCADE,\n" + + " INDEX idx_acc_lasttime (`accountId` ,`last_time`),\n" + + " UNIQUE INDEX idx_acc_type_event (`accountId`, `type`, `eventId`),\n" + + " INDEX idx_time (last_time)" + + ") ENGINE=InnoDB;"; - // Table for storing Items received as loot for individual LootRecords - private static final String CREATE_DROPS = "CREATE TABLE IF NOT EXISTS `drops` (\n" - + " `killId` INT NOT NULL,\n" - + " `itemId` INT NOT NULL,\n" - + " `itemQuantity` INT NOT NULL,\n" - + " FOREIGN KEY (killId) REFERENCES kills(id) ON DELETE CASCADE\n" - + ") ENGINE=InnoDB"; + private static final String CREATE_DROPS = "CREATE TABLE IF NOT EXISTS `loottracker_drops` (\n" + + " `killId` int(11),\n" + + " `itemId` int(11) NOT NULL,\n" + + " `itemQuantity` int(11) NOT NULL,\n" + + " UNIQUE INDEX idx_kill_item (`killId`, `itemId`),\n" + + " FOREIGN KEY (killId) REFERENCES loottracker_kills(id) ON DELETE CASCADE\n" + + ") ENGINE=InnoDB;\n"; // Queries for inserting kills - private static final String INSERT_KILL_QUERY = "INSERT INTO kills (accountId, type, eventId) VALUES (:accountId, :type, :eventId)"; - private static final String INSERT_DROP_QUERY = "INSERT INTO drops (killId, itemId, itemQuantity) VALUES (:killId, :itemId, :itemQuantity)"; + private static final String INSERT_KILL_QUERY = "INSERT INTO loottracker_kills (accountId, type, eventId, amount) VALUES (:accountId, :type, :eventId, 1) ON DUPLICATE KEY UPDATE amount = amount + 1"; + private static final String INSERT_DROP_QUERY = "INSERT INTO loottracker_drops (killId, itemId, itemQuantity) VALUES (:killId, :itemId, :itemQuantity) ON DUPLICATE KEY UPDATE itemQuantity = itemQuantity + :itemQuantity"; - private static final String SELECT_LOOT_QUERY = "SELECT killId,time,type,eventId,itemId,itemQuantity FROM kills JOIN drops ON drops.killId = kills.id WHERE accountId = :accountId ORDER BY TIME DESC LIMIT :limit OFFSET :offset"; + private static final String SELECT_LOOT_QUERY = "SELECT killId,first_time,last_time,type,eventId,amount,itemId,itemQuantity FROM loottracker_kills JOIN loottracker_drops ON loottracker_drops.killId = loottracker_kills.id WHERE accountId = :accountId ORDER BY last_time DESC LIMIT :limit OFFSET :offset"; - private static final String DELETE_LOOT_ACCOUNT = "DELETE FROM kills WHERE accountId = :accountId"; - private static final String DELETE_LOOT_ACCOUNT_EVENTID = "DELETE FROM kills WHERE accountId = :accountId AND eventId = :eventId"; + private static final String DELETE_LOOT_ACCOUNT = "DELETE FROM loottracker_kills WHERE accountId = :accountId"; + private static final String DELETE_LOOT_ACCOUNT_EVENTID = "DELETE FROM loottracker_kills WHERE accountId = :accountId AND eventId = :eventId"; private final Sql2o sql2o; @@ -96,8 +99,8 @@ public class LootTrackerService { try (Connection con = sql2o.beginTransaction()) { - // Kill Entry Query Query killQuery = con.createQuery(INSERT_KILL_QUERY, true); + Query insertDrop = con.createQuery(INSERT_DROP_QUERY); for (LootRecord record : records) { @@ -105,41 +108,26 @@ public class LootTrackerService .addParameter("accountId", accountId) .addParameter("type", record.getType()) .addParameter("eventId", record.getEventId()) - .addToBatch(); - } + .executeUpdate(); + Object[] keys = con.getKeys(); - killQuery.executeBatch(); - Object[] keys = con.getKeys(); - - if (keys.length != records.size()) - { - throw new RuntimeException("Mismatch in keys vs records size"); - } - - Query insertDrop = con.createQuery(INSERT_DROP_QUERY); - - // Append all queries for inserting drops - int idx = 0; - for (LootRecord record : records) - { for (GameItem drop : record.getDrops()) { insertDrop - .addParameter("killId", keys[idx]) + .addParameter("killId", keys[0]) .addParameter("itemId", drop.getId()) .addParameter("itemQuantity", drop.getQty()) .addToBatch(); } - ++idx; + insertDrop.executeBatch(); } - insertDrop.executeBatch(); con.commit(false); } } - public Collection get(int accountId, int limit, int offset) + public Collection get(int accountId, int limit, int offset) { List lootResults; @@ -153,7 +141,7 @@ public class LootTrackerService } LootResult current = null; - List lootRecords = new ArrayList<>(); + List lootRecords = new ArrayList<>(); List gameItems = new ArrayList<>(); for (LootResult lootResult : lootResults) @@ -162,7 +150,7 @@ public class LootTrackerService { if (!gameItems.isEmpty()) { - LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems, current.getTime()); + LootAggregate lootRecord = new LootAggregate(current.getEventId(), current.getType(), gameItems, current.getFirst_time(), current.getLast_time(), current.getAmount()); lootRecords.add(lootRecord); gameItems = new ArrayList<>(); @@ -177,7 +165,7 @@ public class LootTrackerService if (!gameItems.isEmpty()) { - LootRecord lootRecord = new LootRecord(current.getEventId(), current.getType(), gameItems, current.getTime()); + LootAggregate lootRecord = new LootAggregate(current.getEventId(), current.getType(), gameItems, current.getFirst_time(), current.getLast_time(), current.getAmount()); lootRecords.add(lootRecord); } @@ -204,12 +192,12 @@ public class LootTrackerService } } - @Scheduled(fixedDelay = 15 * 60 * 1000) + @Scheduled(fixedDelay = 60 * 60 * 1000) public void expire() { try (Connection con = sql2o.open()) { - con.createQuery("delete from kills where time < current_timestamp() - interval 30 day") + con.createQuery("delete from loottracker_kills where last_time < current_timestamp() - interval 30 day") .executeUpdate(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java index 24e521d3be..4bfb960917 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerBox.java @@ -33,9 +33,11 @@ import java.awt.Dimension; import java.awt.GridLayout; import java.awt.image.BufferedImage; import java.util.ArrayList; -import java.util.Arrays; +import java.util.Comparator; import java.util.List; import java.util.function.BiConsumer; +import java.util.function.ToLongFunction; +import java.util.stream.Collectors; import javax.annotation.Nullable; import javax.swing.Box; import javax.swing.BoxLayout; @@ -48,10 +50,10 @@ import javax.swing.SwingConstants; import javax.swing.border.EmptyBorder; import lombok.AccessLevel; import lombok.Getter; -import net.runelite.client.util.AsyncBufferedImage; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.FontManager; +import net.runelite.client.util.AsyncBufferedImage; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.QuantityFormatter; import net.runelite.client.util.Text; @@ -65,15 +67,15 @@ class LootTrackerBox extends JPanel private final JLabel priceLabel = new JLabel(); private final JLabel subTitleLabel = new JLabel(); private final JPanel logTitle = new JPanel(); - private final JLabel titleLabel = new JLabel(); private final ItemManager itemManager; @Getter(AccessLevel.PACKAGE) private final String id; private final LootTrackerPriceType priceType; private final boolean showPriceType; + private int kills; @Getter - private final List records = new ArrayList<>(); + private final List items = new ArrayList<>(); private long totalPrice; private boolean hideIgnoredItems; @@ -102,6 +104,7 @@ class LootTrackerBox extends JPanel logTitle.setBorder(new EmptyBorder(7, 7, 7, 7)); logTitle.setBackground(ColorScheme.DARKER_GRAY_COLOR.darker()); + JLabel titleLabel = new JLabel(); titleLabel.setText(Text.removeTags(id)); titleLabel.setFont(FontManager.getRunescapeSmallFont()); titleLabel.setForeground(Color.WHITE); @@ -131,15 +134,13 @@ class LootTrackerBox extends JPanel } /** - * Returns total amount of kills, removing ignored kills when necessary + * Returns total amount of kills * * @return total amount of kills */ - private long getTotalKills() + private int getTotalKills() { - return hideIgnoredItems - ? records.stream().filter(r -> !Arrays.stream(r.getItems()).allMatch(LootTrackerItem::isIgnored)).count() - : records.size(); + return kills; } /** @@ -171,16 +172,32 @@ class LootTrackerBox extends JPanel /** * Adds an record's data into a loot box. - * This will add new items to the list, re-calculating price and kill count. */ - void combine(final LootTrackerRecord record) + void addKill(final LootTrackerRecord record) { if (!matches(record)) { throw new IllegalArgumentException(record.toString()); } - records.add(record); + kills += record.getKills(); + + outer: + for (LootTrackerItem item : record.getItems()) + { + // Combine it into an existing item if one already exists + for (int idx = 0; idx < items.size(); ++idx) + { + LootTrackerItem i = items.get(idx); + if (item.getId() == i.getId()) + { + items.set(idx, new LootTrackerItem(i.getId(), i.getName(), i.getQuantity() + item.getQuantity(), i.getGePrice(), i.getHaPrice(), i.isIgnored())); + continue outer; + } + } + + items.add(item); + } } void rebuild() @@ -245,69 +262,31 @@ class LootTrackerBox extends JPanel */ private void buildItems() { - final List allItems = new ArrayList<>(); - final List items = new ArrayList<>(); totalPrice = 0; - for (LootTrackerRecord record : records) - { - allItems.addAll(Arrays.asList(record.getItems())); - } - + List items = this.items; if (hideIgnoredItems) { - /* If all the items in this box are ignored */ - boolean hideBox = allItems.stream().allMatch(LootTrackerItem::isIgnored); - setVisible(!hideBox); - - if (hideBox) - { - return; - } + items = items.stream().filter(item -> !item.isIgnored()).collect(Collectors.toList()); } - for (final LootTrackerItem entry : allItems) + boolean isHidden = items.isEmpty(); + setVisible(!isHidden); + + if (isHidden) { - if (entry.isIgnored() && hideIgnoredItems) - { - continue; - } - - totalPrice += priceType == LootTrackerPriceType.HIGH_ALCHEMY ? entry.getHaPrice() : entry.getGePrice(); - - int quantity = 0; - for (final LootTrackerItem i : items) - { - if (i.getId() == entry.getId()) - { - quantity = i.getQuantity(); - items.remove(i); - break; - } - } - - if (quantity > 0) - { - int newQuantity = entry.getQuantity() + quantity; - long gePricePerItem = entry.getGePrice() == 0 ? 0 : (entry.getGePrice() / entry.getQuantity()); - long haPricePerItem = entry.getHaPrice() == 0 ? 0 : (entry.getHaPrice() / entry.getQuantity()); - - items.add(new LootTrackerItem(entry.getId(), entry.getName(), newQuantity, gePricePerItem * newQuantity, haPricePerItem * newQuantity, entry.isIgnored())); - } - else - { - items.add(entry); - } + return; } - if (priceType == LootTrackerPriceType.HIGH_ALCHEMY) - { - items.sort((i1, i2) -> Long.compare(i2.getHaPrice(), i1.getHaPrice())); - } - else - { - items.sort((i1, i2) -> Long.compare(i2.getGePrice(), i1.getGePrice())); - } + ToLongFunction getPrice = priceType == LootTrackerPriceType.HIGH_ALCHEMY + ? LootTrackerItem::getTotalHaPrice + : LootTrackerItem::getTotalGePrice; + + totalPrice = items.stream() + .mapToLong(getPrice) + .sum(); + + items.sort(Comparator.comparingLong(getPrice).reversed()); // Calculates how many rows need to be display to fit all items final int rowSize = ((items.size() % ITEMS_PER_ROW == 0) ? 0 : 1) + items.size() / ITEMS_PER_ROW; @@ -372,8 +351,8 @@ class LootTrackerBox extends JPanel { final String name = item.getName(); final int quantity = item.getQuantity(); - final long gePrice = item.getGePrice(); - final long haPrice = item.getHaPrice(); + final long gePrice = item.getTotalGePrice(); + final long haPrice = item.getTotalHaPrice(); final String ignoredLabel = item.isIgnored() ? " - Ignored" : ""; return "" + name + " x " + quantity + ignoredLabel + "
GE: " + QuantityFormatter.quantityToStackSize(gePrice) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java index 81aedb3a4a..eb969bbac9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerItem.java @@ -29,19 +29,24 @@ import lombok.Getter; import lombok.Setter; @AllArgsConstructor +@Getter class LootTrackerItem { - @Getter private final int id; - @Getter private final String name; - @Getter - private final int quantity; - @Getter - private final long gePrice; - @Getter - private final long haPrice; - @Getter + private int quantity; + private final int gePrice; + private final int haPrice; @Setter private boolean ignored; + + long getTotalGePrice() + { + return (long) gePrice * quantity; + } + + long getTotalHaPrice() + { + return (long) haPrice * quantity; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java index 519559807a..22ce4561fa 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerPanel.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.loottracker; +import static com.google.common.collect.Iterables.concat; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -35,6 +36,7 @@ import java.awt.image.BufferedImage; import java.util.ArrayList; import java.util.Collection; import java.util.List; +import java.util.function.Predicate; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ImageIcon; @@ -101,8 +103,10 @@ class LootTrackerPanel extends PluginPanel private final JLabel groupedLootBtn = new JLabel(); private final JLabel collapseBtn = new JLabel(); - // Log collection - private final List records = new ArrayList<>(); + // Aggregate of all kills + private final List aggregateRecords = new ArrayList<>(); + // Individual records for the individual kills this session + private final List sessionRecords = new ArrayList<>(); private final List boxes = new ArrayList<>(); private final ItemManager itemManager; @@ -330,7 +334,8 @@ class LootTrackerPanel extends PluginPanel } // If not in detailed view, remove all, otherwise only remove for the currently detailed title - records.removeIf(r -> r.matches(currentView)); + sessionRecords.removeIf(r -> r.matches(currentView)); + aggregateRecords.removeIf(r -> r.matches(currentView)); boxes.removeIf(b -> b.matches(currentView)); updateOverall(); logsContainer.removeAll(); @@ -377,7 +382,7 @@ class LootTrackerPanel extends PluginPanel private boolean isAllCollapsed() { return boxes.stream() - .filter(i -> i.isCollapsed()) + .filter(LootTrackerBox::isCollapsed) .count() == boxes.size(); } @@ -394,8 +399,9 @@ class LootTrackerPanel extends PluginPanel void add(final String eventName, final int actorLevel, LootTrackerItem[] items) { final String subTitle = actorLevel > -1 ? "(lvl-" + actorLevel + ")" : ""; - final LootTrackerRecord record = new LootTrackerRecord(eventName, subTitle, items); - records.add(record); + final LootTrackerRecord record = new LootTrackerRecord(eventName, subTitle, items, 1); + sessionRecords.add(record); + LootTrackerBox box = buildBox(record); if (box != null) { @@ -409,7 +415,7 @@ class LootTrackerPanel extends PluginPanel */ void addRecords(Collection recs) { - records.addAll(recs); + aggregateRecords.addAll(recs); rebuild(); } @@ -466,14 +472,11 @@ class LootTrackerPanel extends PluginPanel */ void updateIgnoredRecords() { - for (LootTrackerRecord r : records) + for (LootTrackerRecord record : concat(aggregateRecords, sessionRecords)) { - for (LootTrackerItem item : r.getItems()) + for (LootTrackerItem item : record.getItems()) { - if (plugin.isIgnored(item.getName()) != item.isIgnored()) - { - item.setIgnored(plugin.isIgnored(item.getName())); - } + item.setIgnored(plugin.isIgnored(item.getName())); } } @@ -487,15 +490,25 @@ class LootTrackerPanel extends PluginPanel { logsContainer.removeAll(); boxes.clear(); - int start = 0; - if (!groupLoot && records.size() > MAX_LOOT_BOXES) + + if (groupLoot) { - start = records.size() - MAX_LOOT_BOXES; + aggregateRecords.forEach(this::buildBox); + sessionRecords.forEach(this::buildBox); } - for (int i = start; i < records.size(); i++) + else { - buildBox(records.get(i)); + int start = 0; + if (sessionRecords.size() > MAX_LOOT_BOXES) + { + start = sessionRecords.size() - MAX_LOOT_BOXES; + } + for (int i = start; i < sessionRecords.size(); i++) + { + buildBox(sessionRecords.get(i)); + } } + boxes.forEach(LootTrackerBox::rebuild); updateOverall(); logsContainer.revalidate(); @@ -522,7 +535,7 @@ class LootTrackerPanel extends PluginPanel { if (box.matches(record)) { - box.combine(record); + box.addKill(record); return box; } } @@ -536,7 +549,7 @@ class LootTrackerPanel extends PluginPanel // Create box final LootTrackerBox box = new LootTrackerBox(itemManager, record.getTitle(), record.getSubTitle(), hideIgnoredItems, config.priceType(), config.showPriceType(), plugin::toggleItem); - box.combine(record); + box.addKill(record); // Create popup menu final JPopupMenu popupMenu = new JPopupMenu(); @@ -568,7 +581,13 @@ class LootTrackerPanel extends PluginPanel final JMenuItem reset = new JMenuItem("Reset"); reset.addActionListener(e -> { - records.removeAll(box.getRecords()); + Predicate match = groupLoot + // With grouped loot, remove any record with this title + ? r -> r.matches(record.getTitle()) + // Otherwise remove specifically this entry + : r -> r.equals(record); + sessionRecords.removeIf(match); + aggregateRecords.removeIf(match); boxes.remove(box); updateOverall(); logsContainer.remove(box); @@ -614,7 +633,7 @@ class LootTrackerPanel extends PluginPanel long overallGe = 0; long overallHa = 0; - for (LootTrackerRecord record : records) + for (LootTrackerRecord record : concat(aggregateRecords, sessionRecords)) { if (!record.matches(currentView)) { @@ -631,13 +650,13 @@ class LootTrackerPanel extends PluginPanel continue; } - overallGe += item.getGePrice(); - overallHa += item.getHaPrice(); + overallGe += item.getTotalGePrice(); + overallHa += item.getTotalHaPrice(); } if (present > 0) { - overallKills++; + overallKills += record.getKills(); } } 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 8a15bad789..adb5217a4e 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 @@ -90,6 +90,7 @@ import net.runelite.client.ui.NavigationButton; import net.runelite.client.util.ImageUtil; import net.runelite.client.util.Text; import net.runelite.http.api.loottracker.GameItem; +import net.runelite.http.api.loottracker.LootAggregate; import net.runelite.http.api.loottracker.LootRecord; import net.runelite.http.api.loottracker.LootRecordType; import net.runelite.http.api.loottracker.LootTrackerClient; @@ -276,13 +277,12 @@ public class LootTrackerPlugin extends Plugin executor.submit(() -> { - Collection lootRecords; - if (!config.syncPanel()) { return; } + Collection lootRecords; try { lootRecords = lootTrackerClient.get(); @@ -624,8 +624,8 @@ public class LootTrackerPlugin extends Plugin { final ItemComposition itemComposition = itemManager.getItemComposition(itemId); final int realItemId = itemComposition.getNote() != -1 ? itemComposition.getLinkedNoteId() : itemId; - final long gePrice = (long) itemManager.getItemPrice(realItemId) * (long) quantity; - final long haPrice = (long) Math.round(itemComposition.getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER) * (long) quantity; + final int gePrice = itemManager.getItemPrice(realItemId); + final int haPrice = Math.round(itemComposition.getPrice() * Constants.HIGH_ALCHEMY_MULTIPLIER); final boolean ignored = ignoredItems.contains(itemComposition.getName()); return new LootTrackerItem( @@ -651,17 +651,17 @@ public class LootTrackerPlugin extends Plugin .collect(Collectors.toList()); } - private Collection convertToLootTrackerRecord(final Collection records) + private Collection convertToLootTrackerRecord(final Collection records) { return records.stream() - .sorted(Comparator.comparing(LootRecord::getTime)) + .sorted(Comparator.comparing(LootAggregate::getLast_time)) .map(record -> { LootTrackerItem[] drops = record.getDrops().stream().map(itemStack -> buildLootTrackerItem(itemStack.getId(), itemStack.getQty()) ).toArray(LootTrackerItem[]::new); - return new LootTrackerRecord(record.getEventId(), "", drops); + return new LootTrackerRecord(record.getEventId(), "", drops, record.getAmount()); }) .collect(Collectors.toCollection(ArrayList::new)); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerRecord.java b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerRecord.java index da69caff7d..502401a968 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerRecord.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loottracker/LootTrackerRecord.java @@ -32,9 +32,11 @@ class LootTrackerRecord private final String title; private final String subTitle; private final LootTrackerItem[] items; + private final int kills; /** * Checks if this record matches specified id + * * @param id other record id * @return true if match is made */ From 2310349a44c9798790408b955556d7a4227802e5 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 11 Jan 2020 17:12:18 -0500 Subject: [PATCH 04/14] loot controller: publish loot to redis --- .../service/loottracker/LootTrackerController.java | 14 ++++++++++++++ .../loottracker/LootTrackerControllerTest.java | 7 +++++++ 2 files changed, 21 insertions(+) diff --git a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java index ac4da593c6..9b810f672f 100644 --- a/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java +++ b/http-service/src/main/java/net/runelite/http/service/loottracker/LootTrackerController.java @@ -26,14 +26,17 @@ package net.runelite.http.service.loottracker; import com.google.api.client.http.HttpStatusCodes; +import com.google.gson.Gson; import java.io.IOException; import java.util.Collection; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import net.runelite.http.api.RuneLiteAPI; import net.runelite.http.api.loottracker.LootAggregate; import net.runelite.http.api.loottracker.LootRecord; import net.runelite.http.service.account.AuthFilter; import net.runelite.http.service.account.beans.SessionEntry; +import net.runelite.http.service.util.redis.RedisPool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -42,14 +45,20 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import redis.clients.jedis.Jedis; @RestController @RequestMapping("/loottracker") public class LootTrackerController { + private static final Gson GSON = RuneLiteAPI.GSON; + @Autowired private LootTrackerService service; + @Autowired + private RedisPool redisPool; + @Autowired private AuthFilter auth; @@ -65,6 +74,11 @@ public class LootTrackerController service.store(records, e.getUser()); response.setStatus(HttpStatusCodes.STATUS_CODE_OK); + + try (Jedis jedis = redisPool.getResource()) + { + jedis.publish("drops", GSON.toJson(records)); + } } @GetMapping diff --git a/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java b/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java index ccf7ab7603..ddfac3e375 100644 --- a/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java +++ b/http-service/src/test/java/net/runelite/http/service/loottracker/LootTrackerControllerTest.java @@ -35,6 +35,7 @@ import net.runelite.http.api.loottracker.LootRecord; import net.runelite.http.api.loottracker.LootRecordType; import net.runelite.http.service.account.AuthFilter; import net.runelite.http.service.account.beans.SessionEntry; +import net.runelite.http.service.util.redis.RedisPool; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -53,6 +54,7 @@ import org.springframework.test.context.junit4.SpringRunner; import org.springframework.test.web.servlet.MockMvc; import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import redis.clients.jedis.Jedis; @RunWith(SpringRunner.class) @WebMvcTest(LootTrackerController.class) @@ -68,11 +70,16 @@ public class LootTrackerControllerTest @MockBean private AuthFilter authFilter; + @MockBean + private RedisPool redisPool; + @Before public void before() throws IOException { when(authFilter.handle(any(HttpServletRequest.class), any(HttpServletResponse.class))) .thenReturn(mock(SessionEntry.class)); + + when(redisPool.getResource()).thenReturn(mock(Jedis.class)); } @Test From 7328db24702e526d3addef1042820cb6e3fc2812 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Mon, 20 Jan 2020 01:27:04 +0000 Subject: [PATCH 05/14] Release 1.6.3 --- 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 ++-- protocol-api/pom.xml | 2 +- protocol/pom.xml | 2 +- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 64bd3b0fc9..6d392c62ac 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index 8626e9a770..c7b1f02f21 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index 428ce77705..c708458a13 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 cache diff --git a/http-api/pom.xml b/http-api/pom.xml index 958551f83e..ee4fabf992 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 9065f056d4..3be44a75ab 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 Web Service diff --git a/pom.xml b/pom.xml index 3920af5028..aaebf7ccda 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 pom RuneLite @@ -59,7 +59,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.3 diff --git a/protocol-api/pom.xml b/protocol-api/pom.xml index 6fb5e93ec8..5c240507d3 100644 --- a/protocol-api/pom.xml +++ b/protocol-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 protocol-api diff --git a/protocol/pom.xml b/protocol/pom.xml index 2dda884fbf..96f15c8d32 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 protocol diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index a4c6e4ee13..22777985dd 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 986cc02b88..d0802cfadf 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3-SNAPSHOT + 1.6.3 client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 18d438c7f2..98e457c4cb 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.3-SNAPSHOT + 1.6.3 script-assembler-plugin From d49388b5b7b40085809719ebe6db62520365d387 Mon Sep 17 00:00:00 2001 From: Runelite auto updater Date: Mon, 20 Jan 2020 01:27:14 +0000 Subject: [PATCH 06/14] Bump for 1.6.4-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 ++-- protocol-api/pom.xml | 2 +- protocol/pom.xml | 2 +- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-script-assembler-plugin/pom.xml | 2 +- 11 files changed, 12 insertions(+), 12 deletions(-) diff --git a/cache-client/pom.xml b/cache-client/pom.xml index 6d392c62ac..a04d7f132a 100644 --- a/cache-client/pom.xml +++ b/cache-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT cache-client diff --git a/cache-updater/pom.xml b/cache-updater/pom.xml index c7b1f02f21..4a096a473a 100644 --- a/cache-updater/pom.xml +++ b/cache-updater/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT Cache Updater diff --git a/cache/pom.xml b/cache/pom.xml index c708458a13..eb3a3a1f55 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT cache diff --git a/http-api/pom.xml b/http-api/pom.xml index ee4fabf992..d533adbbff 100644 --- a/http-api/pom.xml +++ b/http-api/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT Web API diff --git a/http-service/pom.xml b/http-service/pom.xml index 3be44a75ab..b23e066da3 100644 --- a/http-service/pom.xml +++ b/http-service/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT Web Service diff --git a/pom.xml b/pom.xml index aaebf7ccda..1e15c0e57a 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT pom RuneLite @@ -59,7 +59,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - runelite-parent-1.6.3 + HEAD diff --git a/protocol-api/pom.xml b/protocol-api/pom.xml index 5c240507d3..923f1c878a 100644 --- a/protocol-api/pom.xml +++ b/protocol-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT protocol-api diff --git a/protocol/pom.xml b/protocol/pom.xml index 96f15c8d32..b54f3b0423 100644 --- a/protocol/pom.xml +++ b/protocol/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT protocol diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 22777985dd..d6dc613fbb 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index d0802cfadf..cf481a0310 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.6.3 + 1.6.4-SNAPSHOT client diff --git a/runelite-script-assembler-plugin/pom.xml b/runelite-script-assembler-plugin/pom.xml index 98e457c4cb..810f989532 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.3 + 1.6.4-SNAPSHOT script-assembler-plugin From 9a9df86e855c7108b814aa5aeb8b98f96b9394aa Mon Sep 17 00:00:00 2001 From: Wessel Radstok Date: Mon, 20 Jan 2020 17:50:19 +0100 Subject: [PATCH 07/14] Add reanimated monsters to npc_health.json --- .../src/main/resources/npc_health.json | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/resources/npc_health.json b/runelite-client/src/main/resources/npc_health.json index cd1a1637a4..7e3248ff72 100644 --- a/runelite-client/src/main/resources/npc_health.json +++ b/runelite-client/src/main/resources/npc_health.json @@ -1169,5 +1169,27 @@ "Typhor_194" : 280, "The Jormungand_363" : 600, "Basilisk Knight_204" : 300, - "Basilisk Sentinel_358" : 520 + "Basilisk Sentinel_358" : 520, + "Reanimated goblin_0" : 5, + "Reanimated monkey_0" : 5, + "Reanimated imp_0" : 5, + "Reanimated minotaur_0" : 10, + "Reanimated scorpion_0" : 15, + "Reanimated bear_0" : 15, + "Reanimated unicorn_0" : 15, + "Reanimated dog_0" : 35, + "Reanimated chaos druid_0" : 35, + "Reanimated giant_0" : 35, + "Reanimated ogre_0" : 35, + "Reanimated elf_0" : 35, + "Reanimated troll_0" : 35, + "Reanimated horror_0" : 35, + "Reanimated kalphite_0" : 35, + "Reanimated dagganoth_0" : 35, + "Reanimated bloodveld_0" : 35, + "Reanimated Tzhaar_0" : 35, + "Reanimated demon_0" : 35, + "Reanimated aviansie_0" : 35, + "Reanimated abyssal_0" : 35, + "Reanimated dragon_0" : 35 } From 2dad909112fddf7dc16f212278783f70aed4df8a Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 18 Jan 2020 11:28:26 -0500 Subject: [PATCH 08/14] cache: add field initializers to interface def --- .../definitions/InterfaceDefinition.java | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/cache/src/main/java/net/runelite/cache/definitions/InterfaceDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/InterfaceDefinition.java index aede6d6d7b..4ba97725d4 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/InterfaceDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/InterfaceDefinition.java @@ -46,7 +46,7 @@ public class InterfaceDefinition public int scrollWidth; public int scrollHeight; public boolean noClickThrough; - public int spriteId; + public int spriteId = -1; public int textureId; public boolean spriteTiling; public int opacity; @@ -54,34 +54,34 @@ public class InterfaceDefinition public int shadowColor; public boolean flippedVertically; public boolean flippedHorizontally; - public int modelType; - public int modelId; + public int modelType = 1; + public int modelId = -1; public int offsetX2d; public int offsetY2d; public int rotationX; public int rotationY; public int rotationZ; - public int modelZoom; - public int animation; + public int modelZoom = 100; + public int animation = -1; public boolean orthogonal; public int modelHeightOverride; - public int fontId; - public String text; + public int fontId = -1; + public String text = ""; public int lineHeight; public int xTextAlignment; public int yTextAlignment; public boolean textShadowed; public int textColor; public boolean filled; - public int lineWidth; + public int lineWidth = 1; public boolean lineDirection; public int clickMask; - public String name; + public String name = ""; public String[] actions; public int dragDeadZone; public int dragDeadTime; public boolean dragRenderBehavior; - public String targetVerb; + public String targetVerb = ""; public Object[] onLoadListener; public Object[] onMouseOverListener; public Object[] onMouseLeaveListener; @@ -119,13 +119,13 @@ public class InterfaceDefinition public int[] yOffsets; public int[] sprites; public String[] configActions; - public String alternateText; + public String alternateText = ""; public int alternateTextColor; public int hoveredTextColor; public int alternateHoveredTextColor; - public int alternateSpriteId; - public int alternateModelId; - public int alternateAnimation; - public String spellName; - public String tooltip; + public int alternateSpriteId = -1; + public int alternateModelId = -1; + public int alternateAnimation = -1; + public String spellName = ""; + public String tooltip = "Ok"; } From 2eaf57187d796c47ea872ab0eca76dac2c24b380 Mon Sep 17 00:00:00 2001 From: Hexagon Date: Mon, 20 Jan 2020 17:42:15 -0300 Subject: [PATCH 09/14] cache: Add hitsplat definition --- .../java/net/runelite/cache/ConfigType.java | 1 + .../cache/definitions/HitSplatDefinition.java | 48 +++++++ .../definitions/loaders/HitSplatLoader.java | 133 ++++++++++++++++++ .../net/runelite/cache/io/InputStream.java | 12 ++ .../net/runelite/cache/HitSplatDumper.java | 88 ++++++++++++ 5 files changed, 282 insertions(+) create mode 100644 cache/src/main/java/net/runelite/cache/definitions/HitSplatDefinition.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/HitSplatLoader.java create mode 100644 cache/src/test/java/net/runelite/cache/HitSplatDumper.java diff --git a/cache/src/main/java/net/runelite/cache/ConfigType.java b/cache/src/main/java/net/runelite/cache/ConfigType.java index 13056e2e33..4ca51a0b33 100644 --- a/cache/src/main/java/net/runelite/cache/ConfigType.java +++ b/cache/src/main/java/net/runelite/cache/ConfigType.java @@ -43,6 +43,7 @@ public enum ConfigType VARCLIENT(19), VARCLIENTSTRING(15), VARPLAYER(16), + HITSPLAT(32), STRUCT(34), AREA(35); diff --git a/cache/src/main/java/net/runelite/cache/definitions/HitSplatDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/HitSplatDefinition.java new file mode 100644 index 0000000000..2f9a0d1359 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/HitSplatDefinition.java @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Hexagon + * 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.cache.definitions; + +import lombok.Data; + +@Data +public class HitSplatDefinition +{ + private String stringFormat = ""; + private int varbitID = -1; + private int leftSprite = -1; + private int leftSprite2 = -1; + private int rightSpriteId = -1; + private int fontType = -1; + private int backgroundSprite = -1; + private int varpID = -1; + private int useDamage = -1; + private int textColor = 0xFFFFFF; + private int displayCycles = 70; + private int[] multihitsplats; + private int scrollToOffsetX = 0; + private int fadeStartCycle = -1; + private int scrollToOffsetY = 0; + private int textOffsetY = 0; +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/HitSplatLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/HitSplatLoader.java new file mode 100644 index 0000000000..adf8c37ffc --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/HitSplatLoader.java @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2020, Hexagon + * 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.cache.definitions.loaders; + +import net.runelite.cache.definitions.HitSplatDefinition; +import net.runelite.cache.io.InputStream; + +public class HitSplatLoader +{ + public HitSplatDefinition load(byte[] data) + { + HitSplatDefinition def = new HitSplatDefinition(); + InputStream stream = new InputStream(data); + + for (; ; ) + { + int opcode = stream.readUnsignedByte(); + + switch (opcode) + { + case 0: + return def; + case 1: + def.setFontType(stream.readBigSmart2()); + break; + case 2: + def.setTextColor(stream.read24BitInt()); + break; + case 3: + def.setLeftSprite(stream.readBigSmart2()); + break; + case 4: + def.setLeftSprite2(stream.readBigSmart2()); + break; + case 5: + def.setBackgroundSprite(stream.readBigSmart2()); + break; + case 6: + def.setRightSpriteId(stream.readBigSmart2()); + break; + case 7: + def.setScrollToOffsetX(stream.readShort()); + break; + case 8: + def.setStringFormat(stream.readString2()); + break; + case 9: + def.setDisplayCycles(stream.readUnsignedShort()); + break; + case 10: + def.setScrollToOffsetY(stream.readShort()); + break; + case 11: + def.setFadeStartCycle(0); + break; + case 12: + def.setUseDamage(stream.readUnsignedByte()); + break; + case 13: + def.setTextOffsetY(stream.readShort()); + break; + case 14: + def.setFadeStartCycle(stream.readUnsignedShort()); + break; + case 17: + case 18: + int varbitId = stream.readUnsignedShort(); + + if (varbitId == 0xFFFF) + { + varbitId = -1; + } + def.setVarbitID(varbitId); + + int varp = stream.readUnsignedShort(); + if (varp == 0xFFFF) + { + varp = -1; + } + def.setVarpID(varp); + + int id = -1; + if (opcode == 18) + { + id = stream.readUnsignedShort(); + if (id == 0xFFFF) + { + id = -1; + } + } + + int length = stream.readUnsignedByte(); + int[] multihitsplats = new int[length + 2]; + + for (int i = 0; i <= length; i++) + { + multihitsplats[i] = stream.readUnsignedShort(); + if (multihitsplats[i] == 0xFFFF) + { + multihitsplats[i] = -1; + } + } + + multihitsplats[length + 1] = id; + + def.setMultihitsplats(multihitsplats); + break; + } + } + } +} diff --git a/cache/src/main/java/net/runelite/cache/io/InputStream.java b/cache/src/main/java/net/runelite/cache/io/InputStream.java index 1fde0d97a3..33c2cd484c 100644 --- a/cache/src/main/java/net/runelite/cache/io/InputStream.java +++ b/cache/src/main/java/net/runelite/cache/io/InputStream.java @@ -200,6 +200,18 @@ public class InputStream extends java.io.InputStream return sb.toString(); } + public String readString2() + { + if (this.readByte() != 0) + { + throw new IllegalStateException("Invalid jstr2"); + } + else + { + return readString(); + } + } + public String readStringOrNull() { if (this.peek() != 0) diff --git a/cache/src/test/java/net/runelite/cache/HitSplatDumper.java b/cache/src/test/java/net/runelite/cache/HitSplatDumper.java new file mode 100644 index 0000000000..09d3fa2b61 --- /dev/null +++ b/cache/src/test/java/net/runelite/cache/HitSplatDumper.java @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2020, Hexagon + * 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.cache; + +import com.google.common.io.Files; +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.io.File; +import java.io.IOException; +import java.nio.charset.Charset; +import lombok.extern.slf4j.Slf4j; +import net.runelite.cache.definitions.HitSplatDefinition; +import net.runelite.cache.definitions.loaders.HitSplatLoader; +import net.runelite.cache.fs.Archive; +import net.runelite.cache.fs.ArchiveFiles; +import net.runelite.cache.fs.FSFile; +import net.runelite.cache.fs.Index; +import net.runelite.cache.fs.Storage; +import net.runelite.cache.fs.Store; +import org.junit.Ignore; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.TemporaryFolder; + +@Slf4j +public class HitSplatDumper +{ + private Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + @Rule + public TemporaryFolder folder = StoreLocation.getTemporaryFolder(); + + @Test + @Ignore + public void test() throws IOException + { + File dumpDir = folder.newFolder(); + int count = 0; + + try (Store store = new Store(StoreLocation.LOCATION)) + { + store.load(); + + Storage storage = store.getStorage(); + Index index = store.getIndex(IndexType.CONFIGS); + Archive archive = index.getArchive(ConfigType.HITSPLAT.getId()); + + HitSplatLoader loader = new HitSplatLoader(); + + byte[] archiveData = storage.loadArchive(archive); + ArchiveFiles files = archive.getFiles(archiveData); + + for (FSFile file : files.getFiles()) + { + byte[] b = file.getContents(); + + HitSplatDefinition def = loader.load(b); + + Files.asCharSink(new File(dumpDir, file.getFileId() + ".json"), Charset.defaultCharset()).write(gson.toJson(def)); + ++count; + } + } + + log.info("Dumped {} hitsplats to {}", count, dumpDir); + } +} From 10e5a1f1814aa0c0a0c47e635e816318db2a86b4 Mon Sep 17 00:00:00 2001 From: Matt Capone Date: Tue, 14 Jan 2020 22:15:33 -0500 Subject: [PATCH 10/14] chatcommands: add CoX pb tracking --- .../chatcommands/ChatCommandsPlugin.java | 61 +++++++++++++------ .../chatcommands/ChatCommandsPluginTest.java | 35 +++++++++++ 2 files changed, 79 insertions(+), 17 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 e771a649a8..669d7c775f 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 @@ -88,6 +88,7 @@ public class ChatCommandsPlugin extends Plugin { private static final Pattern KILLCOUNT_PATTERN = Pattern.compile("Your (.+) (?:kill|harvest|lap|completion) count is: (\\d+)"); private static final Pattern RAIDS_PATTERN = Pattern.compile("Your completed (.+) count is: (\\d+)"); + private static final Pattern RAIDS_DURATION_PATTERN = Pattern.compile("Congratulations - your raid is complete! Duration ([0-9:]+)"); private static final Pattern WINTERTODT_PATTERN = Pattern.compile("Your subdued Wintertodt count is: (\\d+)"); private static final Pattern BARROWS_PATTERN = Pattern.compile("Your Barrows chest count is: (\\d+)"); private static final Pattern KILL_DURATION_PATTERN = Pattern.compile("(?i)^(?:Fight |Lap |Challenge |Corrupted challenge )?duration: [0-9:]+\\. Personal best: ([0-9:]+)"); @@ -256,6 +257,17 @@ public class ChatCommandsPlugin extends Plugin int kc = Integer.parseInt(matcher.group(2)); setKc(boss, kc); + if (lastPb > -1) + { + // lastPb contains the last raid duration and not the personal best, because the raid + // complete message does not include the pb. We have to check if it is a new pb: + int currentPb = getPb(boss); + if (currentPb <= 0 || lastPb < currentPb) + { + setPb(boss, lastPb); + } + lastPb = -1; + } lastBossKill = boss; return; } @@ -317,29 +329,44 @@ public class ChatCommandsPlugin extends Plugin matchPb(matcher); } + matcher = RAIDS_DURATION_PATTERN.matcher(message); + if (matcher.find()) + { + matchPb(matcher); + } + lastBossKill = null; } + private static int timeStringToSeconds(String timeString) + { + String[] s = timeString.split(":"); + if (s.length == 2) // mm:ss + { + return Integer.parseInt(s[0]) * 60 + Integer.parseInt(s[1]); + } + else if (s.length == 3) // h:mm:ss + { + return Integer.parseInt(s[0]) * 60 * 60 + Integer.parseInt(s[1]) * 60 + Integer.parseInt(s[2]); + } + return Integer.parseInt(timeString); + } + private void matchPb(Matcher matcher) { - String personalBest = matcher.group(1); - String[] s = personalBest.split(":"); - if (s.length == 2) + int seconds = timeStringToSeconds(matcher.group(1)); + if (lastBossKill != null) { - int seconds = Integer.parseInt(s[0]) * 60 + Integer.parseInt(s[1]); - if (lastBossKill != null) - { - // Most bosses sent boss kill message, and then pb message, so we - // use the remembered lastBossKill - log.debug("Got personal best for {}: {}", lastBossKill, seconds); - setPb(lastBossKill, seconds); - lastPb = -1; - } - else - { - // Some bosses send the pb message, and then the kill message! - lastPb = seconds; - } + // Most bosses sent boss kill message, and then pb message, so we + // use the remembered lastBossKill + log.debug("Got personal best for {}: {}", lastBossKill, seconds); + setPb(lastBossKill, seconds); + lastPb = -1; + } + else + { + // Some bosses send the pb message, and then the kill message! + lastPb = seconds; } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java index 175fd22a41..69e3b01397 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/chatcommands/ChatCommandsPluginTest.java @@ -40,6 +40,10 @@ import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.eq; import org.mockito.Mock; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.anyInt; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -356,4 +360,35 @@ public class ChatCommandsPluginTest verify(configManager).setConfiguration(eq("personalbest.adam"), eq("gauntlet"), eq(10 * 60 + 24)); verify(configManager).setConfiguration(eq("killcount.adam"), eq("gauntlet"), eq(124)); } + + @Test + public void testCoXKill() + { + when(client.getUsername()).thenReturn("Adam"); + + ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Congratulations - your raid is complete! Duration 37:04", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Chambers of Xeric count is: 51.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + verify(configManager).setConfiguration(eq("killcount.adam"), eq("chambers of xeric"), eq(51)); + verify(configManager).setConfiguration(eq("personalbest.adam"), eq("chambers of xeric"), eq(37 * 60 + 4)); + } + + @Test + public void testCoXKillNoPb() + { + when(client.getUsername()).thenReturn("Adam"); + when(configManager.getConfiguration(anyString(), anyString(), any())).thenReturn(2224); + + ChatMessage chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Congratulations - your raid is complete! Duration 1:45:04", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + chatMessage = new ChatMessage(null, GAMEMESSAGE, "", "Your completed Chambers of Xeric count is: 52.", null, 0); + chatCommandsPlugin.onChatMessage(chatMessage); + + verify(configManager).setConfiguration(eq("killcount.adam"), eq("chambers of xeric"), eq(52)); + verify(configManager, never()).setConfiguration(eq("personalbest.adam"), eq("chambers of xeric"), anyInt()); + } } From ed86df8539c6983fcde40eecade177a9d843db88 Mon Sep 17 00:00:00 2001 From: Alex Germann <17389577+alexgermann@users.noreply.github.com> Date: Mon, 20 Jan 2020 14:21:42 -0700 Subject: [PATCH 11/14] menu swapper: add Brimhaven cart to Travel option --- .../plugins/menuentryswapper/MenuEntrySwapperPlugin.java | 4 ++++ 1 file changed, 4 insertions(+) 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 0335555256..e5d3c227d9 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 @@ -526,6 +526,10 @@ public class MenuEntrySwapperPlugin extends Plugin { swap("travel", option, target, index); } + else if (config.swapTravel() && option.equals("board") && target.equals("travel cart")) + { + swap("pay-fare", option, target, index); + } else if (config.swapHarpoon() && option.equals("cage")) { swap("harpoon", option, target, index); From 1549e38fa76dab0659b97b3dbf0da5fb699e3323 Mon Sep 17 00:00:00 2001 From: melkypie <5113962+melkypie@users.noreply.github.com> Date: Sat, 4 Jan 2020 20:46:47 +0200 Subject: [PATCH 12/14] clanchat: add ability to recolor ignored players Adds the ability to configure whether or not to mark ignored players inside clanchat with a chosen color Co-Authored-By: Adam --- .../plugins/clanchat/ClanChatConfig.java | 23 + .../plugins/clanchat/ClanChatPlugin.java | 40 + .../main/scripts/ClanChatChannelRebuild.hash | 1 + .../scripts/ClanChatChannelRebuild.rs2asm | 802 ++++++++++++++++++ 4 files changed, 866 insertions(+) create mode 100644 runelite-client/src/main/scripts/ClanChatChannelRebuild.hash create mode 100644 runelite-client/src/main/scripts/ClanChatChannelRebuild.rs2asm diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChatConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChatConfig.java index 9a99c5654f..3a7dbfa20d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChatConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChatConfig.java @@ -24,6 +24,7 @@ */ package net.runelite.client.plugins.clanchat; +import java.awt.Color; import net.runelite.api.ClanMemberRank; import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; @@ -148,4 +149,26 @@ public interface ClanChatConfig extends Config { return false; } + + @ConfigItem( + keyName = "showIgnores", + name = "Recolor ignored players", + description = "Recolors players that are on your ignore list", + position = 10 + ) + default boolean showIgnores() + { + return true; + } + + @ConfigItem( + keyName = "showIgnoresColor", + name = "Ignored color", + description = "Allows you to change the color of the ignored players in your clan chat", + position = 11 + ) + default Color showIgnoresColor() + { + return Color.RED; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChatPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChatPlugin.java index d632ae490a..cfc1447b50 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChatPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/clanchat/ClanChatPlugin.java @@ -47,7 +47,9 @@ import net.runelite.api.ClanMemberManager; import net.runelite.api.ClanMemberRank; import net.runelite.api.Client; import net.runelite.api.GameState; +import net.runelite.api.Ignore; import net.runelite.api.MessageNode; +import net.runelite.api.NameableContainer; import net.runelite.api.Player; import net.runelite.api.ScriptID; import net.runelite.api.SpriteID; @@ -139,11 +141,17 @@ public class ClanChatPlugin extends Plugin public void startUp() { chats = new ArrayList<>(Text.fromCSV(config.chatsData())); + + if (config.showIgnores()) + { + clientThread.invoke(() -> colorIgnoredPlayers(config.showIgnoresColor())); + } } @Override public void shutDown() { + clientThread.invoke(() -> colorIgnoredPlayers(Color.WHITE)); clanMembers.clear(); removeClanCounter(); resetClanChats(); @@ -167,6 +175,9 @@ public class ClanChatPlugin extends Plugin { removeClanCounter(); } + + Color ignoreColor = config.showIgnores() ? config.showIgnoresColor() : Color.WHITE; + clientThread.invoke(() -> colorIgnoredPlayers(ignoreColor)); } } @@ -541,6 +552,12 @@ public class ClanChatPlugin extends Plugin clientThread.invokeLater(() -> confirmKickPlayer(kickPlayerName)); break; } + case "clanChatChannelRebuild": + if (config.showIgnores()) + { + clientThread.invoke(() -> colorIgnoredPlayers(config.showIgnoresColor())); + } + break; } } @@ -668,4 +685,27 @@ public class ClanChatPlugin extends Plugin .option("2. Cancel", Runnables::doNothing) .build(); } + + private void colorIgnoredPlayers(Color ignoreColor) + { + Widget clanChatList = client.getWidget(WidgetInfo.CLAN_CHAT_LIST); + if (clanChatList == null || clanChatList.getChildren() == null) + { + return; + } + + NameableContainer ignoreContainer = client.getIgnoreContainer(); + // Iterate every 3 widgets, since the order of widgets is name, world, icon + for (int i = 0; i < clanChatList.getChildren().length; i += 3) + { + Widget listWidget = clanChatList.getChild(i); + String clanMemberName = listWidget.getText(); + if (clanMemberName.isEmpty() || ignoreContainer.findByName(clanMemberName) == null) + { + continue; + } + + listWidget.setTextColor(ignoreColor.getRGB()); + } + } } diff --git a/runelite-client/src/main/scripts/ClanChatChannelRebuild.hash b/runelite-client/src/main/scripts/ClanChatChannelRebuild.hash new file mode 100644 index 0000000000..e9601457f1 --- /dev/null +++ b/runelite-client/src/main/scripts/ClanChatChannelRebuild.hash @@ -0,0 +1 @@ +C70BDF62710D9FC0EB6707BD94FC0C4335B428DEDD1B52DD51776F811F32C9CF \ No newline at end of file diff --git a/runelite-client/src/main/scripts/ClanChatChannelRebuild.rs2asm b/runelite-client/src/main/scripts/ClanChatChannelRebuild.rs2asm new file mode 100644 index 0000000000..0724ae0048 --- /dev/null +++ b/runelite-client/src/main/scripts/ClanChatChannelRebuild.rs2asm @@ -0,0 +1,802 @@ +.id 1658 +.int_stack_count 15 +.string_stack_count 0 +.int_var_count 25 +.string_var_count 1 +; Callback "clanChatChannelRebuild" for whenever clan chat is done being built, used inside ClanChatPlugin for ignores + iload 3 + iconst 6 + iconst 7 + iconst 6 + sconst "Sort by rank" + iload 0 + iload 1 + iload 2 + iload 3 + iload 4 + iload 5 + iload 6 + iload 7 + iload 8 + iload 9 + iload 10 + iload 11 + iload 12 + iload 13 + iload 14 + invoke 1659 + iload 4 + iconst 2 + iconst 3 + iconst 2 + sconst "Sort by name" + iload 0 + iload 1 + iload 2 + iload 3 + iload 4 + iload 5 + iload 6 + iload 7 + iload 8 + iload 9 + iload 10 + iload 11 + iload 12 + iload 13 + iload 14 + invoke 1659 + iload 5 + iconst 8 + iconst 9 + iconst 9 + sconst "Sort by last world change" + iload 0 + iload 1 + iload 2 + iload 3 + iload 4 + iload 5 + iload 6 + iload 7 + iload 8 + iload 9 + iload 10 + iload 11 + iload 12 + iload 13 + iload 14 + invoke 1659 + iload 6 + iconst 4 + iconst 5 + iconst 4 + sconst "Sort by world" + iload 0 + iload 1 + iload 2 + iload 3 + iload 4 + iload 5 + iload 6 + iload 7 + iload 8 + iload 9 + iload 10 + iload 11 + iload 12 + iload 13 + iload 14 + invoke 1659 + iload 7 + iconst 0 + iconst 1 + iconst 0 + sconst "Legacy sort" + iload 0 + iload 1 + iload 2 + iload 3 + iload 4 + iload 5 + iload 6 + iload 7 + iload 8 + iload 9 + iload 10 + iload 11 + iload 12 + iload 13 + iload 14 + invoke 1659 + 3644 + get_varc_int 185 + switch + 1: LABEL109 + 2: LABEL112 + 3: LABEL115 + 4: LABEL184 + 5: LABEL212 + 6: LABEL118 + 7: LABEL148 + 8: LABEL178 + 9: LABEL181 + jump LABEL239 +LABEL109: + iconst 0 + 3645 + jump LABEL239 +LABEL112: + iconst 1 + 3646 + jump LABEL239 +LABEL115: + iconst 0 + 3646 + jump LABEL239 +LABEL118: + iconst 1 + 3657 + get_varc_int 206 + switch + 3: LABEL125 + 4: LABEL128 + 5: LABEL135 + 8: LABEL142 + 9: LABEL145 + iconst 1 + 3646 + jump LABEL147 +LABEL125: + iconst 0 + 3646 + jump LABEL147 +LABEL128: + iconst 1 + 3652 + iconst 1 + 3647 + iconst 1 + 3646 + jump LABEL147 +LABEL135: + iconst 1 + 3652 + iconst 0 + 3647 + iconst 1 + 3646 + jump LABEL147 +LABEL142: + iconst 1 + 3648 + jump LABEL147 +LABEL145: + iconst 0 + 3648 +LABEL147: + jump LABEL239 +LABEL148: + iconst 0 + 3657 + get_varc_int 206 + switch + 3: LABEL155 + 4: LABEL158 + 5: LABEL165 + 8: LABEL172 + 9: LABEL175 + iconst 1 + 3646 + jump LABEL177 +LABEL155: + iconst 0 + 3646 + jump LABEL177 +LABEL158: + iconst 1 + 3652 + iconst 1 + 3647 + iconst 1 + 3646 + jump LABEL177 +LABEL165: + iconst 1 + 3652 + iconst 0 + 3647 + iconst 1 + 3646 + jump LABEL177 +LABEL172: + iconst 1 + 3648 + jump LABEL177 +LABEL175: + iconst 0 + 3648 +LABEL177: + jump LABEL239 +LABEL178: + iconst 1 + 3648 + jump LABEL239 +LABEL181: + iconst 0 + 3648 + jump LABEL239 +LABEL184: + iconst 1 + 3652 + iconst 1 + 3647 + get_varc_int 206 + switch + 3: LABEL193 + 6: LABEL196 + 7: LABEL201 + 8: LABEL206 + 9: LABEL209 + iconst 1 + 3646 + jump LABEL211 +LABEL193: + iconst 0 + 3646 + jump LABEL211 +LABEL196: + iconst 1 + 3657 + iconst 1 + 3646 + jump LABEL211 +LABEL201: + iconst 0 + 3657 + iconst 1 + 3646 + jump LABEL211 +LABEL206: + iconst 1 + 3648 + jump LABEL211 +LABEL209: + iconst 0 + 3648 +LABEL211: + jump LABEL239 +LABEL212: + iconst 1 + 3652 + iconst 0 + 3647 + get_varc_int 206 + switch + 3: LABEL221 + 6: LABEL224 + 7: LABEL229 + 8: LABEL234 + 9: LABEL237 + iconst 1 + 3646 + jump LABEL239 +LABEL221: + iconst 0 + 3646 + jump LABEL239 +LABEL224: + iconst 1 + 3657 + iconst 1 + 3646 + jump LABEL239 +LABEL229: + iconst 0 + 3657 + iconst 1 + 3646 + jump LABEL239 +LABEL234: + iconst 1 + 3648 + jump LABEL239 +LABEL237: + iconst 0 + 3648 +LABEL239: + 3655 + iload 8 + cc_deleteall + clan_getchatcount + istore 15 + get_varbit 6363 + iconst 1 + if_icmpeq LABEL248 + jump LABEL296 +LABEL248: + iload 15 + iconst 0 + if_icmpgt LABEL252 + jump LABEL253 +LABEL252: + clan_leavechat +LABEL253: + iconst 0 + istore 15 + iconst 0 + iload 2 + if_sethide + iconst 1 + iload 9 + if_sethide + iload 11 + invoke 2067 + pop_int + iconst -1 + sconst "" + iload 11 + if_setonmouserepeat + iconst -1 + sconst "" + iload 11 + if_setonmouseleave + iload 13 + invoke 2067 + pop_int + iconst -1 + sconst "" + iload 13 + if_setonmouserepeat + iconst -1 + sconst "" + iload 13 + if_setonmouseleave + sconst "" + sconst "---" + sconst "" + join_string 3 + iload 12 + if_settext + iload 12 + if_clearops + iconst -1 + sconst "" + iload 12 + if_setonop + jump LABEL341 +LABEL296: + iconst 1 + iload 2 + if_sethide + iconst 0 + iload 9 + if_sethide + iload 11 + invoke 486 + pop_int + iconst 94 + iconst -2147483645 + sconst "I" + iload 11 + if_setonmouserepeat + iconst 92 + iconst -2147483645 + sconst "I" + iload 11 + if_setonmouseleave + iload 13 + invoke 486 + pop_int + iconst 94 + iconst -2147483645 + sconst "I" + iload 13 + if_setonmouserepeat + iconst 92 + iconst -2147483645 + sconst "I" + iload 13 + if_setonmouseleave + sconst "Clan Setup" + iload 12 + if_settext + iconst 1 + sconst "Clan Setup" + iload 12 + if_setop + iconst 489 + iconst -2147483644 + iconst 1 + sconst "ii" + iload 12 + if_setonop +LABEL341: + sconst "" + sstore 0 + iconst -1 + istore 16 + iconst -1 + istore 17 + clan_getchatrank + istore 18 + clan_getchatminkick + istore 19 + iload 3 + if_getwidth + istore 20 + iconst 0 + istore 21 + iconst 0 + istore 22 + iconst 15 + istore 23 + invoke 1972 + iconst 1 + if_icmpeq LABEL364 + jump LABEL369 +LABEL364: + iconst 8 + iconst 5 + iload 23 + scale + istore 23 +LABEL369: + iconst 0 + istore 24 +LABEL371: + iload 24 + iload 15 + if_icmplt LABEL375 + jump LABEL572 +LABEL375: + iload 24 + clan_getchatusername + iload 24 + clan_getchatuserworld + iload 24 + clan_getchatuserrank + istore 17 + istore 16 + sstore 0 + iload 8 + iconst 4 + iload 21 + cc_create + iload 21 + iconst 1 + add + istore 21 + iload 20 + iload 23 + iconst 1 + iconst 0 + cc_setsize + iconst 0 + iload 22 + iconst 2 + iconst 0 + cc_setposition + iconst 494 + cc_settextfont + iconst 0 + iconst 1 + iconst 0 + cc_settextalign + sload 0 + cc_settext + iconst 16777215 + cc_setcolour + iconst 0 + cc_settextshadow + iload 8 + iconst 4 + iload 21 + cc_create 1 + iload 21 + iconst 1 + add + istore 21 + iload 20 + iload 23 + iconst 1 + iconst 0 + cc_setsize 1 + iconst 0 + iload 22 + iconst 2 + iconst 0 + cc_setposition 1 + iconst 494 + cc_settextfont 1 + iconst 2 + iconst 1 + iconst 0 + cc_settextalign 1 + sconst "World " + iload 16 + tostring + join_string 2 + cc_settext 1 + iload 16 + map_world + if_icmpeq LABEL447 + jump LABEL450 +LABEL447: + iconst 901389 + cc_setcolour 1 + jump LABEL452 +LABEL450: + iconst 16777060 + cc_setcolour 1 +LABEL452: + iconst 0 + cc_settextshadow 1 + iload 8 + iconst 5 + iload 21 + cc_create 1 + iload 21 + iconst 1 + add + istore 21 + iconst 9 + iconst 9 + iconst 0 + iconst 0 + cc_setsize 1 + iconst 1 + iload 22 + iload 23 + iconst 9 + sub + iconst 2 + div + add + iconst 0 + iconst 0 + cc_setposition 1 + iconst 105 + iconst 100 + iconst 706 + iload 17 + enum + cc_setgraphic 1 + iload 24 + clan_isself + iconst 0 + if_icmpeq LABEL489 + jump LABEL525 +LABEL489: + iload 24 + clan_isfriend + iconst 1 + if_icmpeq LABEL494 + jump LABEL501 +LABEL494: + iconst 9 + sconst "Remove friend" + cc_setop + iconst 9 + sconst "Remove friend" + cc_setop 1 + jump LABEL525 +LABEL501: + iload 24 + clan_isignore + iconst 1 + if_icmpeq LABEL506 + jump LABEL513 +LABEL506: + iconst 10 + sconst "Remove ignore" + cc_setop + iconst 10 + sconst "Remove ignore" + cc_setop 1 + jump LABEL525 +LABEL513: + iconst 7 + sconst "Add friend" + cc_setop + iconst 7 + sconst "Add friend" + cc_setop 1 + iconst 8 + sconst "Add ignore" + cc_setop + iconst 8 + sconst "Add ignore" + cc_setop 1 +LABEL525: + invoke 1942 + iconst 0 + if_icmpeq LABEL529 + jump LABEL543 +LABEL529: + iload 18 + iload 19 + if_icmpge LABEL533 + jump LABEL543 +LABEL533: + iload 18 + iload 17 + if_icmpgt LABEL537 + jump LABEL543 +LABEL537: + iconst 6 + sconst "Kick user" + cc_setop + iconst 6 + sconst "Kick user" + cc_setop 1 +LABEL543: + sconst "" + sload 0 + sconst "" + join_string 3 + cc_setopbase + sconst "" + sload 0 + sconst "" + join_string 3 + cc_setopbase 1 + iconst 214 + sconst "event_opbase" + iconst -2147483644 + sconst "si" + cc_setonop + iconst 214 + sconst "event_opbase" + iconst -2147483644 + sconst "si" + cc_setonop 1 + iload 24 + iconst 1 + add + iload 22 + iload 23 + add + istore 22 + istore 24 + jump LABEL371 +LABEL572: + iload 15 + iconst 1 + if_icmpge LABEL576 + jump LABEL580 +LABEL576: + iload 22 + iconst 5 + add + istore 22 +LABEL580: + iload 10 + if_clearops + get_varbit 6363 + iconst 1 + if_icmpeq LABEL586 + jump LABEL605 +LABEL586: + sconst "" + iload 0 + if_settext + sconst "" + iload 1 + if_settext + sconst "" + sconst "---" + sconst "" + join_string 3 + iload 10 + if_settext + iload 10 + if_clearops + iconst -1 + sconst "" + iload 10 + if_setonop + jump LABEL672 +LABEL605: + iload 15 + iconst 0 + if_icmpgt LABEL609 + jump LABEL653 +LABEL609: + sconst "" + clan_getchatdisplayname + sconst "" + join_string 3 + iload 0 + if_settext + sconst "" + clan_getchatownername + sconst "" + join_string 3 + iload 1 + if_settext + sconst "Leave Chat" + iload 10 + if_settext + get_varbit 5432 + iconst 1 + if_icmpeq LABEL631 + get_varbit 4289 + iconst 0 + if_icmpne LABEL631 + jump LABEL642 +LABEL631: + iconst 6 + sconst "Leave Chat" + iload 10 + if_setop + iconst 194 + iconst -2147483644 + iconst 6 + sconst "ii" + iload 10 + if_setonop + jump LABEL652 +LABEL642: + iconst 1 + sconst "Leave Chat" + iload 10 + if_setop + iconst 194 + iconst -2147483644 + iconst 1 + sconst "ii" + iload 10 + if_setonop +LABEL652: + jump LABEL672 +LABEL653: + sconst "Not in chat" + iload 0 + if_settext + sconst "None" + iload 1 + if_settext + sconst "Join Chat" + iload 10 + if_settext + iconst 1 + sconst "Join Chat" + iload 10 + if_setop + iconst 194 + iconst -2147483644 + iconst 1 + sconst "ii" + iload 10 + if_setonop +LABEL672: + iload 22 + iload 8 + if_getheight + if_icmpgt LABEL677 + jump LABEL687 +LABEL677: + iconst 0 + iload 22 + iload 8 + if_setscrollsize + iload 9 + iload 8 + iload 8 + if_getscrolly + invoke 72 + jump LABEL695 +LABEL687: + iconst 0 + iconst 0 + iload 8 + if_setscrollsize + iload 9 + iload 8 + iconst 0 + invoke 72 +LABEL695: + sconst "clanChatChannelRebuild" + runelite_callback + return From f6640844b5d58475a6bf07a8842c7b3522c807b7 Mon Sep 17 00:00:00 2001 From: Adam Date: Mon, 20 Jan 2020 13:28:17 -0500 Subject: [PATCH 13/14] Use wiki scraped npcs for npc healths --- .../net/runelite/http/api/npc/NpcInfo.java | 35 + .../runelite/http/api/npc/NpcInfoClient.java | 81 ++ .../net/runelite/client/game/NPCManager.java | 56 +- .../opponentinfo/OpponentInfoOverlay.java | 2 +- .../plugins/slayer/TargetWeaknessOverlay.java | 4 +- .../plugins/xptracker/XpTrackerPlugin.java | 4 +- .../src/main/resources/npc_health.json | 1195 ----------------- 7 files changed, 154 insertions(+), 1223 deletions(-) create mode 100644 http-api/src/main/java/net/runelite/http/api/npc/NpcInfo.java create mode 100644 http-api/src/main/java/net/runelite/http/api/npc/NpcInfoClient.java delete mode 100644 runelite-client/src/main/resources/npc_health.json diff --git a/http-api/src/main/java/net/runelite/http/api/npc/NpcInfo.java b/http-api/src/main/java/net/runelite/http/api/npc/NpcInfo.java new file mode 100644 index 0000000000..285169a32d --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/npc/NpcInfo.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2020, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.http.api.npc; + +import lombok.Data; + +@Data +public class NpcInfo +{ + private String name; + private int combat; + private int hitpoints; +} diff --git a/http-api/src/main/java/net/runelite/http/api/npc/NpcInfoClient.java b/http-api/src/main/java/net/runelite/http/api/npc/NpcInfoClient.java new file mode 100644 index 0000000000..075b7b186a --- /dev/null +++ b/http-api/src/main/java/net/runelite/http/api/npc/NpcInfoClient.java @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2020, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.http.api.npc; + +import com.google.gson.JsonParseException; +import com.google.gson.reflect.TypeToken; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.lang.reflect.Type; +import java.util.Map; +import lombok.Value; +import lombok.extern.slf4j.Slf4j; +import net.runelite.http.api.RuneLiteAPI; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; + +@Slf4j +@Value +public class NpcInfoClient +{ + private final OkHttpClient client; + + public Map getNpcs() throws IOException + { + HttpUrl.Builder urlBuilder = RuneLiteAPI.getStaticBase().newBuilder() + .addPathSegment("npcs") + .addPathSegment("npcs.min.json"); + + HttpUrl url = urlBuilder.build(); + + log.debug("Built URI: {}", url); + + Request request = new Request.Builder() + .url(url) + .build(); + + try (Response response = client.newCall(request).execute()) + { + if (!response.isSuccessful()) + { + log.warn("Error looking up npcs: {}", response); + return null; + } + + InputStream in = response.body().byteStream(); + final Type typeToken = new TypeToken>() + { + }.getType(); + return RuneLiteAPI.GSON.fromJson(new InputStreamReader(in), typeToken); + } + catch (JsonParseException ex) + { + throw new IOException(ex); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/game/NPCManager.java b/runelite-client/src/main/java/net/runelite/client/game/NPCManager.java index f34d2c13ee..1a51afea3f 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/NPCManager.java +++ b/runelite-client/src/main/java/net/runelite/client/game/NPCManager.java @@ -24,42 +24,54 @@ */ package net.runelite.client.game; -import com.google.gson.Gson; -import com.google.gson.reflect.TypeToken; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.lang.reflect.Type; +import java.io.IOException; +import java.util.Collections; import java.util.Map; +import java.util.concurrent.ScheduledExecutorService; import javax.annotation.Nullable; import javax.inject.Inject; import javax.inject.Singleton; +import lombok.extern.slf4j.Slf4j; +import net.runelite.http.api.npc.NpcInfo; +import net.runelite.http.api.npc.NpcInfoClient; +import okhttp3.OkHttpClient; @Singleton +@Slf4j public class NPCManager { - private final Map healthMap; + private final OkHttpClient okHttpClient; + private Map npcMap = Collections.emptyMap(); @Inject - private NPCManager() + private NPCManager(OkHttpClient okHttpClient, ScheduledExecutorService scheduledExecutorService) { - final Gson gson = new Gson(); - final Type typeToken = new TypeToken>() - { - }.getType(); - - final InputStream healthFile = getClass().getResourceAsStream("/npc_health.json"); - healthMap = gson.fromJson(new InputStreamReader(healthFile), typeToken); + this.okHttpClient = okHttpClient; + scheduledExecutorService.execute(this::loadNpcs); } - /** - * Returns health for target NPC based on it's combat level and name - * @param name npc name - * @param combatLevel npc combat level - * @return health or null if HP is unknown - */ @Nullable - public Integer getHealth(final String name, final int combatLevel) + public NpcInfo getNpcInfo(int npcId) { - return healthMap.get(name + "_" + combatLevel); + return npcMap.get(npcId); + } + + @Nullable + public Integer getHealth(int npcId) + { + NpcInfo npcInfo = npcMap.get(npcId); + return npcInfo == null ? null : npcInfo.getHitpoints(); + } + + private void loadNpcs() + { + try + { + npcMap = new NpcInfoClient(okHttpClient).getNpcs(); + } + catch (IOException e) + { + log.warn("error loading npc stats", e); + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java index 45c40cf939..c20e33244a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/opponentinfo/OpponentInfoOverlay.java @@ -112,7 +112,7 @@ class OpponentInfoOverlay extends Overlay lastMaxHealth = null; if (opponent instanceof NPC) { - lastMaxHealth = npcManager.getHealth(opponentName, opponent.getCombatLevel()); + lastMaxHealth = npcManager.getHealth(((NPC) opponent).getId()); } else if (opponent instanceof Player) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java index ada3f3a727..8a036eff83 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/TargetWeaknessOverlay.java @@ -40,7 +40,6 @@ import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.OverlayUtil; -import net.runelite.client.util.Text; class TargetWeaknessOverlay extends Overlay { @@ -109,8 +108,7 @@ class TargetWeaknessOverlay extends Overlay final int healthScale = target.getHealth(); final int healthRatio = target.getHealthRatio(); - final String targetName = Text.removeTags(target.getName()); - final Integer maxHealth = npcManager.getHealth(targetName, target.getCombatLevel()); + final Integer maxHealth = npcManager.getHealth(target.getId()); if (healthRatio < 0 || healthScale <= 0 || maxHealth == null) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java index 40cc7cd66e..9431eaf5a1 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/xptracker/XpTrackerPlugin.java @@ -369,7 +369,7 @@ public class XpTrackerPlugin extends Plugin if (interacting instanceof NPC && COMBAT.contains(skill)) { final NPC npc = (NPC) interacting; - xpState.updateNpcExperience(skill, npc, npcManager.getHealth(npc.getName(), npc.getCombatLevel())); + xpState.updateNpcExperience(skill, npc, npcManager.getHealth(npc.getId())); } final XpUpdateResult updateResult = xpState.updateSkill(skill, currentXp, startGoalXp, endGoalXp); @@ -392,7 +392,7 @@ public class XpTrackerPlugin extends Plugin for (Skill skill : COMBAT) { - final XpUpdateResult updateResult = xpState.updateNpcKills(skill, npc, npcManager.getHealth(npc.getName(), npc.getCombatLevel())); + final XpUpdateResult updateResult = xpState.updateNpcKills(skill, npc, npcManager.getHealth(npc.getId())); final boolean updated = XpUpdateResult.UPDATED.equals(updateResult); xpPanel.updateSkillExperience(updated, xpPauseState.isPaused(skill), skill, xpState.getSkillSnapshot(skill)); } diff --git a/runelite-client/src/main/resources/npc_health.json b/runelite-client/src/main/resources/npc_health.json deleted file mode 100644 index 7e3248ff72..0000000000 --- a/runelite-client/src/main/resources/npc_health.json +++ /dev/null @@ -1,1195 +0,0 @@ -{ - "Molanisk_51": 52, - "Aberrant spectre_96": 90, - "Nechryael_115": 105, - "Death spawn_46": 60, - "Zombie_13": 22, - "Zombie_18": 24, - "Zombie_24": 30, - "Zombie_44": 40, - "Summoned Zombie_13": 22, - "Skeleton_22": 29, - "Skeleton_21": 24, - "Skeleton_25": 17, - "Skeleton_45": 59, - "Skeleton Mage_16": 17, - "Ghost_19": 25, - "Soulless_18": 24, - "Death wing_83": 80, - "Rock Crab_13": 50, - "Hellhound_122": 116, - "Wolf_64": 69, - "White wolf_25": 35, - "White wolf_38": 44, - "Big Wolf_73": 74, - "Wolf_25": 34, - "Wild dog_63": 62, - "Guard dog_44": 49, - "Hobgoblin_54": 62, - "Troll_91": 120, - "Huge spider_81": 90, - "Ogre_53": 60, - "Baby red dragon_65": 65, - "Kalphite Soldier_85": 90, - "Steel dragon_246": 210, - "Dagannoth_135": 142, - "Tok-Xil_135": 60, - "Rocnar_97": 100, - "Jungle Wolf_64": 69, - "King Black Dragon_276": 240, - "Black demon_172": 157, - "Baby dragon_48": 50, - "Red dragon_152": 140, - "Black dragon_227": 190, - "Green dragon_79": 75, - "Blue dragon_111": 105, - "Bronze dragon_131": 122, - "Iron dragon_189": 165, - "Ghoul_42": 50, - "Dwarf_10": 16, - "Chaos dwarf_48": 61, - "Dwarf_20": 16, - "Dwarf_9": 16, - "Dwarf_11": 16, - "Gunthor the brave_29": 35, - "Jailer_47": 47, - "Black Heather_34": 37, - "Donny the lad_34": 37, - "Speedy Keith_34": 37, - "Salarin the twisted_70": 70, - "Corporeal Beast_785": 2000, - "Dark energy core_75": 25, - "Pheasant_3": 5, - "Cave crawler_23": 22, - "Kurask_106": 97, - "Gargoyle_111": 105, - "Banshee_23": 22, - "Abyssal demon_124": 150, - "Basilisk_61": 75, - "Cockatrice_37": 37, - "Rockslug_29": 27, - "Dust devil_93": 105, - "Turoth_86": 76, - "Turoth_89": 81, - "Turoth_87": 79, - "Turoth_85": 77, - "Turoth_83": 76, - "Turoth_88": 76, - "Pyrefiend_43": 45, - "Jelly_78": 75, - "Infernal Mage_66": 60, - "Crawling Hand_8": 16, - "Crawling Hand_7": 16, - "Crawling Hand_12": 19, - "Crawling Hand_11": 16, - "Lizard_42": 40, - "Desert Lizard_24": 25, - "Small Lizard_12": 15, - "Harpie Bug Swarm_46": 25, - "Skeletal Wyvern_140": 210, - "Killerwatt_55": 51, - "Zygomite_74": 65, - "Zygomite_86": 75, - "Wall beast_49": 105, - "Giant frog_99": 100, - "Big frog_24": 25, - "Cave slime_23": 25, - "Cave bug_6": 5, - "Cave bug_96": 93, - "Bloodveld_76": 120, - "Cave kraken_127": 125, - "Kraken_291": 255, - "Smoke devil_160": 185, - "Thermonuclear smoke devil_301": 240, - "Sorebones_57": 52, - "Zombie pirate_57": 52, - "Barrelchest_190": 134, - "Zombie swab_55": 50, - "Evil spirit_150": 90, - "Fever spider_49": 40, - "Tyras guard_110": 110, - "Arrg_113": 140, - "Ice wolf_96": 70, - "Ice wolf_132": 70, - "Ice Troll_124": 80, - "Ice Troll_123": 80, - "Ice Troll_120": 100, - "Ice Troll_121": 80, - "Goblin_5": 12, - "Giant skeleton_80": 70, - "Damis_103": 90, - "Damis_174": 200, - "Stranger_95": 80, - "Bandit_74": 65, - "Bandit_57": 50, - "Ice troll_124": 80, - "Ice troll_123": 80, - "Ice troll_120": 100, - "Ice troll_121": 80, - "Mummy_96": 86, - "Mummy_103": 91, - "Scarabs_92": 25, - "Bandit champion_70": 50, - "Baby Roc_75": 50, - "Giant Roc_172": 250, - "Me_79": 45, - "Suqah_111": 106, - "Scarab mage_93": 50, - "Locust rider_106": 90, - "Locust rider_98": 90, - "Giant scarab_191": 130, - "Scarab mage_66": 50, - "Locust rider_68": 90, - "Wormbrain_2": 5, - "Melzar the Mad_43": 44, - "Icelord_51": 60, - "Zogre_44": 71, - "Skogre_44": 71, - "Slash Bash_111": 100, - "Moss giant_84": 120, - "Agrith Naar_100": 100, - "Skeleton_13": 18, - "Rock_111": 140, - "Stick_104": 135, - "Pee Hat_91": 120, - "Kraka_91": 120, - "Thrower Troll_67": 95, - "Mountain troll_69": 90, - "Ghast_30": 45, - "Mummy_84": 68, - "Kalphite Worker_28": 40, - "Kalphite Guardian_141": 171, - "Kalphite Queen_333": 255, - "Dagannoth_74": 70, - "Dagannoth_92": 120, - "Dagannoth mother_100": 120, - "Sigmund_50": 70, - "Angry unicorn_45": 200, - "Angry giant rat_45": 200, - "Angry goblin_45": 200, - "Fear reaper_42": 25, - "Confusion beast_43": 64, - "Hopeless creature_40": 25, - "Tolna_46": 45, - "Sea Snake Young_90": 85, - "Sea Snake Hatchling_62": 50, - "Giant Sea Snake_149": 100, - "Mourner_11": 19, - "Mourner_24": 25, - "Man_4": 7, - "Woman_3": 7, - "Barrelchest (hard)_380": 255, - "Giant scarab (hard)_316": 255, - "Dessous (hard)_217": 255, - "Kamil (hard)_273": 255, - "Woman_4": 7, - "Damis (hard)_200": 198, - "Damis (hard)_272": 255, - "Mourner_18": 13, - "Woman_12": 7, - "Woman_14": 7, - "Paladin_59": 66, - "Mourner_12": 13, - "Ogre_63": 60, - "Tree spirit_101": 85, - "Alomone_13": 25, - "Clivet_13": 25, - "Hazeel Cultist_13": 25, - "Khazard Guard_23": 25, - "General Khazard_112": 171, - "Bouncer_137": 116, - "Khazard Ogre_63": 60, - "Khazard Scorpion_44": 40, - "Arzinian Avatar of Strength_125": 100, - "Arzinian Avatar of Strength_75": 70, - "Arzinian Avatar of Ranging_125": 100, - "Arzinian Avatar of Ranging_75": 70, - "Arzinian Avatar of Magic_125": 100, - "Arzinian Avatar of Magic_75": 70, - "Ram_2": 8, - "Vulture_31": 10, - "Experiment_51": 40, - "Experiment_25": 100, - "Loar Shadow_40": 38, - "Loar Shade_40": 38, - "Phrin Shadow_60": 56, - "Phrin Shade_60": 56, - "Riyl Shade_80": 76, - "Asyn Shadow_100": 90, - "Asyn Shade_100": 90, - "Fiyr Shadow_120": 110, - "Fiyr Shade_120": 110, - "Afflicted_37": 30, - "Afflicted_34": 28, - "Afflicted_32": 26, - "Afflicted_30": 24, - "Seagull_2": 6, - "Seagull_3": 10, - "Dwarf gang member_44": 40, - "Dwarf gang member_48": 25, - "Dwarf gang member_49": 25, - "Slagilith_92": 60, - "Fire elemental_35": 30, - "Earth elemental_35": 35, - "Air elemental_34": 30, - "Water elemental_34": 30, - "The Kendal_70": 50, - "Camp dweller_31": 30, - "Camp dweller_25": 25, - "Dwarf_7": 16, - "Black Guard_25": 30, - "Foreman_23": 20, - "Jungle Demon_195": 170, - "Pirate_23": 20, - "Thief_16": 17, - "Mugger_6": 8, - "Chompy bird_6": 10, - "Kebbit_13": 50, - "Skeleton hero_149": 124, - "Skeleton brute_132": 124, - "Skeleton warlord_132": 124, - "Skeleton heavy_132": 124, - "Skeleton thug_132": 124, - "Black knight_33": 42, - "Guard_19": 22, - "Guard_20": 22, - "Guard_21": 22, - "Guard_22": 22, - "Fire wizard_13": 25, - "Water wizard_13": 25, - "Earth wizard_13": 25, - "Air wizard_13": 25, - "Kolodion_112": 107, - "Battle mage_54": 120, - "Ahrim the Blighted_98": 100, - "Dharok the Wretched_115": 100, - "Guthan the Infested_115": 100, - "Karil the Tainted_98": 100, - "Torag the Corrupted_115": 100, - "Verac the Defiled_115": 100, - "Bloodworm_52": 45, - "Crypt rat_43": 35, - "Giant crypt rat_76": 70, - "Crypt spider_56": 60, - "Giant crypt spider_79": 80, - "Skeleton_77": 50, - "Splatter_22": 13, - "Splatter_33": 23, - "Splatter_44": 33, - "Splatter_54": 43, - "Splatter_65": 53, - "Shifter_38": 23, - "Shifter_57": 38, - "Shifter_76": 53, - "Shifter_90": 68, - "Shifter_104": 83, - "Ravager_36": 23, - "Ravager_53": 38, - "Ravager_71": 53, - "Ravager_89": 68, - "Ravager_106": 83, - "Spinner_36": 33, - "Spinner_55": 53, - "Spinner_74": 73, - "Spinner_92": 101, - "Spinner_88": 93, - "Torcher_33": 18, - "Torcher_49": 30, - "Torcher_66": 45, - "Torcher_79": 57, - "Torcher_92": 71, - "Defiler_33": 27, - "Defiler_50": 45, - "Defiler_66": 62, - "Defiler_67": 62, - "Defiler_80": 78, - "Defiler_97": 97, - "Brawler_51": 53, - "Brawler_76": 83, - "Brawler_101": 113, - "Brawler_129": 143, - "Double agent_65": 80, - "Double agent_108": 120, - "Scarab swarm_98": 25, - "Goat_23": 21, - "Billy Goat_33": 28, - "White Knight_36": 52, - "White Knight_38": 52, - "White Knight_39": 52, - "White Knight_42": 55, - "Gorak_145": 112, - "Duck_1": 3, - "Stag_15": 19, - "Rabbit_1": 5, - "Tree spirit_14": 50, - "Tree spirit_79": 86, - "Tree spirit_120": 120, - "Tree spirit_159": 170, - "Evil Chicken_159": 120, - "Baby dragon_83": 80, - "Ice troll runt_74": 60, - "Ice troll male_82": 80, - "Ice troll female_82": 80, - "Ice troll grunt_100": 80, - "Duckling_1": 3, - "Lesser demon_82": 81, - "Greater demon_92": 89, - "Zulrah_725": 500, - "Snakeling_90": 1, - "Chaos Elemental_305": 250, - "Dark wizard_23": 24, - "Dark wizard_22": 24, - "Dark wizard_11": 24, - "Oomlie bird_46": 40, - "Terrorbird_28": 34, - "Mounted terrorbird gnome_31": 36, - "Mounted terrorbird gnome_49": 36, - "Fire giant_86": 111, - "Ice giant_53": 70, - "Moss giant_42": 60, - "Jogre_53": 60, - "Cyclops_56": 75, - "Hill Giant_28": 35, - "Cyclops_106": 150, - "Giant frog_13": 23, - "Big frog_10": 18, - "Frog_5": 8, - "TzHaar-Hur_74": 80, - "TzHaar-Xil_133": 120, - "TzHaar-Ket_149": 140, - "Tz-Kih_22": 10, - "Tz-Kek_45": 20, - "Tok-Xil_90": 40, - "Commander Zilyana_596": 255, - "Starlight_149": 160, - "Growler_139": 146, - "Bree_146": 162, - "Saradomin priest_113": 89, - "Spiritual warrior_125": 110, - "Spiritual ranger_122": 106, - "Spiritual mage_120": 85, - "Knight of Saradomin_103": 135, - "Knight of Saradomin_101": 108, - "General Graardor_624": 255, - "Sergeant Strongstack_141": 128, - "Sergeant Steelwill_142": 127, - "Sergeant Grimspike_142": 146, - "Ogre_58": 70, - "Jogre_58": 70, - "Cyclops_81": 110, - "Ork_107": 110, - "Hobgoblin_47": 52, - "Spiritual ranger_115": 131, - "Spiritual warrior_134": 131, - "Spiritual mage_121": 75, - "Goblin_17": 18, - "Goblin_12": 15, - "Goblin_15": 16, - "Goblin_13": 16, - "Dagannoth_88": 85, - "Giant Rock Crab_137": 180, - "Bardur_94": 99, - "Dagannoth fledgeling_70": 100, - "Dagannoth Supreme_303": 255, - "Dagannoth Prime_303": 255, - "Dagannoth Rex_303": 255, - "Animated Bronze Armour_11": 10, - "Animated Iron Armour_23": 20, - "Animated Steel Armour_46": 40, - "Animated Black Armour_69": 60, - "Animated Mithril Armour_92": 80, - "Animated Adamant Armour_113": 99, - "Animated Rune Armour_138": 120, - "Cyclops_76": 100, - "Catablepon_49": 40, - "Catablepon_64": 70, - "Catablepon_68": 50, - "Giant spider_50": 50, - "Spider_24": 22, - "Scorpion_59": 55, - "Scorpion_37": 37, - "Minotaur_12": 10, - "Minotaur_19": 10, - "Minotaur_27": 22, - "Goblin_11": 7, - "Goblin_16": 22, - "Goblin_25": 26, - "Wolf_14": 15, - "Wolf_11": 10, - "Rat_1": 2, - "Flesh Crawler_28": 25, - "Flesh Crawler_35": 25, - "Flesh Crawler_41": 25, - "Giant rat_26": 25, - "Ankou_75": 60, - "Ankou_82": 65, - "Ankou_86": 70, - "Skeleton_68": 70, - "Skeleton_60": 70, - "Skeleton_85": 77, - "Ghost_77": 80, - "Ghost_76": 75, - "H.A.M. Guard_12": 15, - "H.A.M. Guard_18": 20, - "H.A.M. Guard_22": 30, - "Monk_5": 15, - "Abyssal leech_41": 10, - "Abyssal guardian_59": 55, - "Abyssal walker_81": 95, - "Mogre_60": 48, - "Werewolf_88": 98, - "Boris_24": 60, - "Imre_24": 60, - "Yuri_24": 60, - "Joseph_24": 60, - "Nikolai_24": 60, - "Eduard_24": 60, - "Lev_24": 60, - "Georgy_24": 60, - "Svetlana_24": 60, - "Irina_24": 60, - "Alexis_24": 60, - "Milla_24": 60, - "Galina_24": 60, - "Sofiya_24": 60, - "Ksenia_24": 60, - "Yadviga_24": 60, - "Nikita_24": 60, - "Vera_24": 60, - "Zoja_24": 60, - "Liliya_24": 60, - "Myre Blamish Snail_9": 8, - "Blood Blamish Snail_20": 13, - "Ochre Blamish Snail_10": 10, - "Bruise Blamish Snail_20": 12, - "Bark Blamish Snail_15": 22, - "Ochre Blamish Snail_15": 20, - "Chicken_1": 3, - "Rooster_3": 5, - "Cow_2": 8, - "Cow calf_2": 6, - "Bat_6": 8, - "Troll_69": 90, - "Giant bat_27": 32, - "Unicorn_15": 19, - "Grizzly bear_21": 27, - "Black bear_19": 25, - "Earth warrior_51": 54, - "Ice warrior_57": 59, - "Otherworldly being_64": 66, - "Magic axe_42": 45, - "Snake_5": 6, - "Black unicorn_27": 29, - "Shadow warrior_48": 67, - "Giant rat_3": 5, - "Giant rat_6": 10, - "Dark wizard_20": 24, - "Invrigar the Necromancer_20": 24, - "Dark wizard_7": 12, - "Black Knight_33": 42, - "Highwayman_5": 13, - "Chaos druid_13": 20, - "Pirate_26": 23, - "Thug_10": 18, - "Rogue_15": 17, - "Monk of Zamorak_22": 20, - "Monk of Zamorak_17": 10, - "Monk of Zamorak_45": 40, - "Tribesman_32": 40, - "Dark warrior_8": 17, - "Chaos druid warrior_37": 40, - "Necromancer_26": 40, - "Guard Bandit_22": 27, - "Waterfiend_115": 130, - "Brutal green dragon_227": 175, - "Mithril dragon_304": 255, - "Confused barbarian_132": 124, - "Lost barbarian_132": 124, - "Nail beast_69": 55, - "Nail beast_98": 65, - "Nail beast_141": 75, - "Zamorak wizard_65": 73, - "Saradomin wizard_108": 120, - "Big Snake_84": 120, - "Undead cow_2": 8, - "Undead chicken_1": 3, - "Giant lobster_32": 32, - "Tortured soul_59": 51, - "Man_2": 7, - "Woman_2": 7, - "Shadow spider_52": 55, - "Giant spider_2": 5, - "Giant spider_27": 33, - "Spider_1": 2, - "Jungle spider_44": 51, - "Deadly red spider_34": 35, - "Ice spider_61": 65, - "Poison spider_64": 64, - "Scorpion_14": 17, - "Poison Scorpion_20": 23, - "Pit Scorpion_28": 32, - "King Scorpion_32": 30, - "Goblin_2": 5, - "Hobgoblin_28": 29, - "Hobgoblin_42": 49, - "Barbarian_17": 25, - "Barbarian_10": 25, - "Barbarian_15": 25, - "Barbarian_9": 18, - "Farmer_7": 12, - "Wizard_9": 14, - "Druid_33": 30, - "Warrior woman_24": 20, - "Al-Kharid warrior_9": 19, - "Paladin_62": 66, - "Hero_69": 82, - "Forester_15": 17, - "Knight of Ardougne_46": 52, - "Tz-Kek_22": 10, - "Yt-MejKot_180": 80, - "Ket-Zek_360": 160, - "TzTok-Jad_702": 250, - "Yt-HurKot_108": 60, - "K'ril Tsutsaroth_650": 255, - "Tstanon Karlak_145": 142, - "Zakl'n Gritch_142": 150, - "Balfrug Kreeyath_151": 161, - "Hellhound_127": 116, - "Imp_7": 10, - "Werewolf_93": 92, - "Feral Vampyre_77": 60, - "Bloodveld_81": 134, - "Pyrefiend_48": 45, - "Icefiend_18": 20, - "Gorak_149": 128, - "Spiritual warrior_115": 100, - "Spiritual ranger_118": 120, - "Kree'arra_580": 255, - "Wingman Skree_143": 121, - "Flockleader Geerin_149": 132, - "Flight Kilisa_159": 159, - "Spiritual warrior_123": 98, - "Spiritual ranger_127": 89, - "Spiritual mage_122": 75, - "Aviansie_69": 70, - "Aviansie_79": 83, - "Aviansie_84": 86, - "Aviansie_83": 86, - "Aviansie_92": 95, - "Aviansie_97": 98, - "Aviansie_137": 124, - "Aviansie_148": 139, - "Aviansie_71": 63, - "Aviansie_73": 67, - "Aviansie_89": 69, - "Aviansie_94": 75, - "Aviansie_131": 115, - "Dagannoth spawn_42": 35, - "Dagannoth_90": 95, - "Snake_35": 25, - "Albino bat_52": 33, - "Giant mosquito_13": 15, - "Jungle horror_70": 45, - "Cave horror_80": 55, - "Leech_52": 45, - "Feral Vampyre_72": 50, - "Feral Vampyre_61": 40, - "Watchman_33": 22, - "Soldier_28": 22, - "Shipyard worker_11": 10, - "Drunken man_3": 7, - "Gardener_4": 8, - "Gardener_3": 7, - "Cuffs_3": 7, - "Narf_2": 7, - "Rusty_2": 7, - "Jeff_2": 7, - "Hengel_2": 7, - "Anja_2": 7, - "Chicken_3": 3, - "Earth Warrior Champion_102": 108, - "Giant Champion_56": 70, - "Ghoul Champion_85": 100, - "Goblin Champion_24": 32, - "Hobgoblin Champion_56": 58, - "Imp Champion_14": 40, - "Jogre Champion_107": 120, - "Lesser Demon Champion_162": 148, - "Skeleton Champion_40": 58, - "Zombies Champion_51": 60, - "Leon d'Cour_141": 123, - "Rabbit_2": 5, - "Grizzly bear_42": 35, - "Grizzly bear cub_33": 35, - "Dire Wolf_88": 85, - "Elf warrior_90": 105, - "Elf warrior_108": 105, - "Lucien_14": 17, - "Guardian of Armadyl_45": 49, - "Guardian of Armadyl_43": 49, - "Fire Warrior of Lesarkus_84": 59, - "Shadow Hound_63": 62, - "Fareed_167": 130, - "Kamil_154": 130, - "Dessous_139": 200, - "The Inadequacy_343": 180, - "The Everlasting_223": 230, - "The Untouchable_274": 90, - "The Illusive_108": 140, - "A Doubt_78": 50, - "Count Draynor_34": 35, - "Monk of Zamorak_30": 25, - "Bouncer_160": 116, - "Renegade Knight_37": 48, - "Thrantax the Mighty_92": 80, - "Sir Mordred_39": 38, - "Desert snake_5": 6, - "Menaphite Thug_55": 60, - "Tough Guy_75": 75, - "Frogeel_103": 90, - "Unicow_25": 24, - "Spidine_42": 35, - "Swordchick_46": 35, - "Jubster_87": 60, - "Newtroost_19": 18, - "Possessed pickaxe_50": 40, - "Skeletal miner_42": 39, - "Treus Dayth_95": 100, - "Ghost_29": 27, - "Rooster_2": 5, - "Einar_1": 1, - "Alrik_1": 1, - "Thorhild_1": 1, - "Rannveig_2": 1, - "Valgerd_2": 1, - "Broddi_2": 1, - "Ragnvald_2": 1, - "Vampyre Juvenile_45": 60, - "Vampyre Juvinate_54": 65, - "Feral Vampyre_64": 80, - "Vyrewatch_105": 90, - "Vyrewatch_110": 90, - "Vyrewatch_120": 105, - "Vyrewatch_125": 110, - "Vanstrom Klause_169": 155, - "Moss giant_48": 85, - "Jake_37": 50, - "Wilson_37": 50, - "Palmer_37": 50, - "Fox_19": 30, - "Bunny_2": 5, - "Bear Cub_15": 20, - "Unicorn Foal_12": 15, - "Black unicorn Foal_22": 25, - "The Draugen_69": 60, - "Freidir_48": 50, - "Borrokar_48": 50, - "Lanzig_48": 50, - "Jennella_48": 50, - "Market Guard_48": 50, - "Ungadulu_70": 65, - "Ungadulu_169": 150, - "Nezikchened_187": 150, - "San Tojalon_106": 120, - "Irvig Senay_100": 125, - "Ranalph Devere_92": 130, - "Zombie rat_3": 5, - "Witch's experiment_19": 21, - "Witch's experiment (second form)_30": 31, - "Witch's experiment (third form)_42": 41, - "Witch's experiment (fourth form)_53": 51, - "Shadow_73": 15, - "Dark beast_182": 220, - "Black Knight Titan_120": 142, - "Soldier_48": 50, - "Ocga_5": 10, - "Penda_5": 10, - "Fareed (hard)_299": 255, - "Troll general_113": 140, - "Troll spectator_71": 90, - "Dad_101": 120, - "Twig_71": 90, - "Berry_71": 90, - "Thrower troll_68": 95, - "Mountain troll_71": 90, - "King Roald_47": 60, - "Outlaw_32": 20, - "Crocodile_63": 62, - "Jackal_21": 27, - "Locust_18": 27, - "Plague frog_11": 10, - "Possessed Priest_91": 90, - "Monk_3": 5, - "Thief_14": 17, - "Head Thief_26": 37, - "Jail guard_26": 32, - "Sea troll_79": 100, - "Sea troll_65": 80, - "Sea troll_87": 80, - "Sea troll_101": 80, - "Sea Troll Queen_170": 200, - "Skeleton Mage_83": 80, - "Sir Lancelot_127": 115, - "Sir Kay_124": 110, - "Sir Gawain_122": 110, - "Sir Lucan_120": 105, - "Sir Palomedes_118": 100, - "Sir Tristram_115": 105, - "Sir Pelleas_112": 99, - "Sir Bedivere_110": 90, - "Ogre chieftain_81": 60, - "Gorad_68": 80, - "City guard_83": 80, - "Enclave guard_83": 80, - "Ogre shaman_113": 1, - "Tower guard_28": 22, - "Colonel Radick_38": 65, - "Vampyre Juvinate_75": 110, - "Vampyre Juvinate_50": 60, - "Gadderanks_35": 20, - "Skeleton fremennik_40": 25, - "Skeleton fremennik_50": 35, - "Skeleton fremennik_60": 40, - "Ulfric_100": 60, - "Brine rat_70": 50, - "Blessed spider_39": 32, - "Blessed giant rat_9": 30, - "Sir Jerro_62": 57, - "Sir Carl_62": 57, - "Sir Harry_62": 57, - "Kalrag_89": 78, - "Othainian_91": 87, - "Doomion_91": 90, - "Holthion_91": 87, - "Disciple of Iban_13": 20, - "Rowdy slave_10": 16, - "Mercenary Captain_47": 80, - "Desert Wolf_27": 34, - "Ugthanki_42": 45, - "Bedabin Nomad Fighter_56": 50, - "Mercenary_45": 60, - "Sir Leye_20": 20, - "Angry unicorn_47": 200, - "Angry giant rat_47": 200, - "Angry goblin_47": 200, - "Angry bear_47": 200, - "Fear reaper_55": 57, - "Confusion beast_63": 64, - "Hopeless creature_71": 71, - "Hopeless beast_71": 71, - "The Shaikahan_83": 100, - "Black golem_75": 80, - "White golem_75": 80, - "Grey golem_75": 80, - "Poltenip_21": 22, - "Radat_21": 22, - "Slug Prince_62": 70, - "Icefiend_13": 15, - "Crab_23": 19, - "Mudskipper_30": 20, - "Mudskipper_31": 20, - "Crab_21": 18, - "Jubbly bird_9": 21, - "Culinaromancer_75": 150, - "Agrith-Na-Na_146": 200, - "Flambeed_149": 210, - "Karamel_136": 250, - "Dessourt_121": 130, - "Gelatinnoth Mother_130": 240, - "Grip_22": 25, - "Ice Queen_111": 105, - "Pirate Guard_19": 25, - "Entrana firebird_2": 5, - "Black Knight_32": 42, - "Khazard trooper_19": 22, - "Khazard commander_48": 22, - "Gnome troop_1": 3, - "Chronozon_170": 60, - "Imp_2": 8, - "Imp_3": 8, - "Suit of armour_19": 29, - "Skeleton Hellhound_97": 55, - "Delrith_27": 71, - "Experiment No.2_109": 95, - "Mouse_95": 70, - "Glod_138": 160, - "Sigmund_64": 70, - "H.A.M. Archer_30": 35, - "H.A.M. Mage_30": 35, - "Weaponsmaster_23": 20, - "Jonny the beard_2": 8, - "Bird_11": 10, - "Bird_5": 5, - "Jungle spider_37": 51, - "Snake_24": 6, - "Padulah_149": 130, - "Monkey Guard_149": 130, - "Monkey Archer_86": 50, - "Monkey Guard_167": 130, - "Monkey Zombie_98": 60, - "Monkey Zombie_129": 90, - "Monkey Zombie_82": 60, - "Mourner_108": 105, - "Cave goblin miner_11": 10, - "Cave goblin guard_26": 26, - "Cave goblin guard_24": 26, - "Undead one_61": 47, - "Nazastarool_91": 70, - "Nazastarool_68": 70, - "Nazastarool_93": 80, - "Goblin guard_42": 43, - "Ghost_24": 20, - "Grave scorpion_12": 7, - "Poison spider_31": 64, - "Enormous Tentacle_112": 120, - "Angry barbarian spirit_166": 190, - "Enraged barbarian spirit_166": 190, - "Berserk barbarian spirit_166": 190, - "Ferocious barbarian spirit_166": 190, - "Swamp snake_80": 120, - "Swamp snake_109": 120, - "Swamp snake_139": 85, - "Ghast_79": 45, - "Ghast_109": 135, - "Ghast_139": 160, - "Giant snail_80": 125, - "Giant snail_109": 150, - "Giant snail_139": 160, - "Vampyre Juvinate_59": 50, - "Vampyre Juvinate_90": 100, - "Vampyre Juvinate_119": 150, - "Feral Vampyre_70": 75, - "Feral Vampyre_100": 135, - "Feral Vampyre_130": 185, - "Tentacle_99": 75, - "Head_140": 150, - "Tentacle_136": 75, - "Undead Lumberjack_30": 12, - "Undead Lumberjack_35": 12, - "Undead Lumberjack_40": 12, - "Undead Lumberjack_45": 13, - "Undead Lumberjack_50": 14, - "Undead Lumberjack_55": 12, - "Undead Lumberjack_60": 12, - "Undead Lumberjack_64": 12, - "Undead Lumberjack_70": 12, - "Penance Fighter_30": 28, - "Penance Fighter_32": 29, - "Penance Fighter_37": 32, - "Penance Fighter_42": 37, - "Penance Fighter_47": 38, - "Penance Fighter_56": 49, - "Penance Fighter_61": 50, - "Penance Fighter_68": 55, - "Penance Fighter_77": 56, - "Penance Ranger_21": 20, - "Penance Ranger_25": 29, - "Penance Ranger_32": 32, - "Penance Ranger_38": 34, - "Penance Ranger_43": 41, - "Penance Ranger_51": 50, - "Penance Ranger_57": 50, - "Penance Ranger_64": 55, - "Penance Ranger_72": 58, - "Penance Queen_209": 250, - "Queen spawn_63": 45, - "Giant Mole_230": 200, - "Yak_22": 50, - "Ice Troll King_122": 150, - "Ice troll grunt_102": 80, - "Tanglefoot_111": 102, - "Baby tanglefoot_45": 40, - "Cerberus_318": 600, - "Abyssal Sire_350": 400, - "Spawn_60": 15, - "Scion_100": 50, - "Sand Crab_15": 60, - "Wallasalki_98": 120, - "Rock lobster_127": 150, - "Spinolyp_76": 100, - "Gnome troop_3": 2, - "Black Guard_48": 40, - "Black Guard Berserker_66": 50, - "Tortoise_79": 100, - "Tortoise_92": 120, - "Gnome child_1": 2, - "Gnome guard_23": 31, - "Gnome woman_1": 2, - "Gnome Archer_5": 10, - "Gnome Driver_5": 10, - "Gnome Mage_5": 10, - "Bush snake_35": 25, - "Elvarg (hard)_214": 240, - "The Inadequacy (hard)_600": 255, - "The Untouchable (hard)_440": 180, - "Large mosquito_13": 3, - "Mosquito swarm_17": 9, - "Tanglefoot (hard)_199": 204, - "Chronozon (hard)_297": 120, - "Bouncer (hard)_244": 232, - "Ice Troll King (hard)_213": 255, - "Black demon (hard)_292": 157, - "Glod (hard)_276": 255, - "Treus Dayth (hard)_194": 240, - "Black Knight Titan (hard)_210": 255, - "Dagannoth mother (hard)_201": 240, - "Evil Chicken (hard)_286": 240, - "Culinaromancer (hard)_209": 255, - "Agrith-Na-Na (hard)_235": 255, - "Flambeed (hard)_238": 255, - "Karamel (hard)_186": 255, - "Dessourt (hard)_217": 255, - "Gelatinnoth Mother (hard)_201": 240, - "Nezikchened (hard)_295": 150, - "Tree spirit (hard)_199": 187, - "Jungle Demon (hard)_327": 255, - "The Kendal (hard)_210": 150, - "Giant Roc (hard)_257": 255, - "Slagilith (hard)_202": 150, - "Moss giant (hard)_182": 240, - "Skeleton Hellhound (hard)_198": 132, - "Agrith Naar (hard)_196": 209, - "King Roald (hard)_188": 150, - "Khazard warlord (hard)_192": 255, - "Dad (hard)_201": 240, - "Arrg (hard)_210": 255, - "Count Draynor (hard)_177": 210, - "Witch's experiment (hard)_47": 63, - "Witch's experiment (second form) (hard)_77": 93, - "Witch's experiment (third form) (hard)_90": 103, - "Witch's experiment (fourth form) (hard)_103": 113, - "Nazastarool (hard)_176": 154, - "Nazastarool (hard)_153": 180, - "Nazastarool (hard)_181": 176, - "Elvarg_83": 80, - "Khazard warlord_112": 170, - "Mosquito swarm_20": 15, - "Broodoo victim_60": 100, - "Animated steel armour_53": 50, - "Animated spade_50": 40, - "Terror dog_110": 87, - "Terror dog_100": 82, - "Tarn_69": 80, - "Mutant tarn_69": 80, - "Callisto_470": 255, - "Venenatis_464": 255, - "Gnome guard_1337": 31, - "Armadylian guard_97": 132, - "Bandosian guard_125": 130, - "Lava dragon_252": 230, - "Ent_101": 105, - "Runite Golem_178": 170, - "Rogue_135": 125, - "Mammoth_80": 130, - "Dark warrior_145": 137, - "Elder Chaos druid_129": 150, - "Vet'ion_454": 255, - "Vet'ion Reborn_454": 255, - "Skeleton Hellhound_214": 55, - "Greater Skeleton Hellhound_281": 190, - "Scorpia_225": 200, - "Scorpia's guardian_47": 70, - "Crazy archaeologist_204": 225, - "Chaos Fanatic_202": 225, - "Chaotic death spawn_215": 50, - "Rock Golem_120": 120, - "Rock Golem_159": 170, - "River troll_120": 120, - "River troll_159": 170, - "Lizardman shaman_150": 150, - "Maniacal monkey_48": 65, - "Kruk_149": 210, - "Gangster_45": 40, - "Gangster_50": 50, - "Gang boss_83": 80, - "Gang boss_76": 80, - "Soldier (tier 1)_39": 50, - "Soldier (tier 2)_48": 50, - "Soldier (tier 3)_58": 55, - "Soldier (tier 4)_70": 65, - "Soldier (tier 5)_99": 90, - "Lizardman_53": 60, - "Lizardman_62": 60, - "Lizardman brute_73": 60, - "Kourend guard_21": 22, - "Kourend head guard_84": 86, - "Tortured gorilla_142": 110, - "Glough_378": 575, - "Keef_178": 180, - "Kob_185": 200, - "Maniacal monkey_140": 65, - "Maniacal Monkey Archer_132": 60, - "Demonic gorilla_275": 380, - "Tortured gorilla_141": 210, - "Ent_86": 75, - "Black demon_184": 170, - "Black demon_178": 160, - "Greater demon_101": 120, - "Greater demon_100": 115, - "Greater demon_113": 130, - "Lesser demon_87": 87, - "Lesser demon_94": 98, - "Dust devil_110": 130, - "Fire giant_109": 150, - "Fire giant_104": 130, - "Bronze dragon_143": 122, - "Iron dragon_215": 195, - "Steel dragon_274": 250, - "Ankou_95": 60, - "King Sand Crab_107": 200, - "Twisted Banshee_89": 109, - "Brutal blue dragon_271": 245, - "Brutal red dragon_289": 285, - "Brutal black dragon_318": 315, - "Mutated Bloodveld_123": 170, - "Warped Jelly_112": 140, - "Greater Nechryael_200": 205, - "Deviant spectre_169": 190, - "Skotizo_321": 450, - "Reanimated demon spawn_87": 85, - "Dark Ankou_95": 60, - "Ancient Wizard_98": 80, - "Ancient Wizard_112": 80, - "Brassican Mage_140": 150, - "Double agent_141": 160, - "Crushing hand_45": 55, - "Chasm Crawler_68": 64, - "Screaming banshee_70": 61, - "Screaming twisted banshee_144": 220, - "Giant rockslug_86": 77, - "Cockathrice_89": 95, - "Flaming pyrelord_97": 126, - "Monstrous basilisk_135": 170, - "Malevolent Mage_162": 175, - "Insatiable Bloodveld_202": 380, - "Insatiable mutated Bloodveld_278": 410, - "Vitreous Jelly_206": 190, - "Vitreous warped Jelly_241": 220, - "Cave abomination_206": 130, - "Abhorrent spectre_253": 250, - "Repugnant spectre_335": 390, - "Choke devil_264": 300, - "King kurask_295": 420, - "Nuclear smoke devil_280": 240, - "Marble gargoyle_349": 270, - "Night beast_374": 550, - "Greater abyssal demon_342": 400, - "Nechryarch_300": 320, - "Obor_106": 120, - "Zamorak warrior_84": 45, - "Zamorak warrior_85": 45, - "Zamorak ranger_81": 50, - "Zamorak ranger_82": 50, - "Cave lizard_37": 20, - "Zamorak crafter_19": 25, - "Temple guardian_30": 45, - "TzHaar-Ket_221": 200, - "Jal-Nib_32": 10, - "Jal-MejRah_85": 25, - "Jal-Ak_165": 40, - "Jal-AkRek-Mej_70": 15, - "Jal-AkRek-Xil_70": 15, - "Jal-AkRek-Ket_70": 15, - "Jal-ImKot_240": 75, - "Jal-Xil_370": 130, - "Jal-Zek_490": 220, - "JalTok-Jad_900": 350, - "Yt-HurKot_141": 90, - "TzKal-Zuk_1400": 1200, - "Jal-MejJak_250": 80, - "Long-tailed Wyvern_152": 200, - "Taloned Wyvern_147": 200, - "Spitting Wyvern_139": 200, - "Ancient Wyvern_210": 300, - "Lobstrosity_68": 50, - "Ancient Zygomite_109": 150, - "Ammonite Crab_25": 100, - "Hoop Snake_19": 25, - "Tar Monster_132": 200, - "Deranged archaeologist_276": 200, - "Dusk_248": 450, - "Dawn_228": 450, - "Justiciar Zachariah_348": 320, - "Derwen_235": 320, - "Porazdir_235": 320, - "Black dragon_247": 250, - "Ankou_98": 100, - "Green dragon_88": 100, - "Greater demon_104": 120, - "Black demon_188": 200, - "Hellhound_136": 150, - "Ice giant_67": 100, - "Revenant imp_7": 10, - "Dusk_328": 450, - "Sand Snake (hard)_154": 180, - "Sand Snake_36": 60, - "Revenant goblin_15": 14, - "Revenant pyrefiend_52": 48, - "Revenant hobgoblin_60": 72, - "Revenant cyclops_82": 110, - "Revenant hellhound_90": 80, - "Revenant demon_98": 80, - "Revenant ork_105": 105, - "Revenant dark beast_120": 140, - "Revenant knight_126": 143, - "Revenant dragon_135": 155, - "Corsair Traitor (hard)_103": 160, - "Corsair Traitor_35": 55, - "Ithoi the Navigator_35": 55, - "Ogress Warrior_82": 82, - "Ogress Shaman_82": 82, - "Corrupt Lizardman (hard)_152": 150, - "Corrupt Lizardman_46": 50, - "Rune dragon_380": 330, - "Adamant dragon_338": 295, - "Robert the Strong_194": 280, - "Vorkath_392": 460, - "Vorkath_732": 750, - "Zombified Spawn_55": 8, - "Zombified Spawn_64": 38, - "Stone Guardian_124": 62, - "Galvek_608": 1200, - "Growthling_37": 10, - "Bryophyta_128": 115, - "Ranis Drakan_233": 400, - "Vyrewatch_87": 75, - "Abomination_149": 200, - "Swamp Crab_55": 75, - "Respiratory system_0": 50, - "Sulphur Lizard_50": 50, - "Wyrm_99": 130, - "Drake_192": 250, - "Hydra_194": 300, - "Alchemical Hydra_426": 1100, - "Undead Druid_105": 140, - "Temple Spider_75": 70, - "Sarachnis_318": 400, - "Basilisk Youngling_57" : 60, - "Monstrous Basilisk_135" : 170, - "Typhor_194" : 280, - "The Jormungand_363" : 600, - "Basilisk Knight_204" : 300, - "Basilisk Sentinel_358" : 520, - "Reanimated goblin_0" : 5, - "Reanimated monkey_0" : 5, - "Reanimated imp_0" : 5, - "Reanimated minotaur_0" : 10, - "Reanimated scorpion_0" : 15, - "Reanimated bear_0" : 15, - "Reanimated unicorn_0" : 15, - "Reanimated dog_0" : 35, - "Reanimated chaos druid_0" : 35, - "Reanimated giant_0" : 35, - "Reanimated ogre_0" : 35, - "Reanimated elf_0" : 35, - "Reanimated troll_0" : 35, - "Reanimated horror_0" : 35, - "Reanimated kalphite_0" : 35, - "Reanimated dagganoth_0" : 35, - "Reanimated bloodveld_0" : 35, - "Reanimated Tzhaar_0" : 35, - "Reanimated demon_0" : 35, - "Reanimated aviansie_0" : 35, - "Reanimated abyssal_0" : 35, - "Reanimated dragon_0" : 35 -} From 8f5b45ddbe0f0098c22f58b81bcd82924d2e381e Mon Sep 17 00:00:00 2001 From: Twiglet1022 <29353990+Twiglet1022@users.noreply.github.com> Date: Tue, 16 Apr 2019 20:28:35 +0100 Subject: [PATCH 14/14] clientui: forcibly bring client to front on Windows on request focus --- .../java/net/runelite/client/ui/ClientUI.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java index 13595170b2..9d0522392f 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java @@ -591,6 +591,34 @@ public class ClientUI { OSXUtil.requestFocus(); } + // The workaround for Windows is to minimise and then un-minimise the client to bring + // it to the front because java.awt.Window#toFront doesn't work reliably. + // See https://stackoverflow.com/questions/309023/how-to-bring-a-window-to-the-front/7435722#7435722 + else if (OSType.getOSType() == OSType.Windows && !frame.isFocused()) + { + SwingUtilities.invokeLater(() -> + { + if ((frame.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) + { + frame.setExtendedState(JFrame.ICONIFIED); + frame.setExtendedState(JFrame.MAXIMIZED_BOTH); + } + else + { + // If the client is snapped to the top and bottom edges of the screen, setExtendedState will + // will reset it so setSize and setLocation ensure that the client doesn't move or resize. + // It is done this way because Windows does not support JFrame.MAXIMIZED_VERT + int x = frame.getLocation().x; + int y = frame.getLocation().y; + int width = frame.getWidth(); + int height = frame.getHeight(); + frame.setExtendedState(JFrame.ICONIFIED); + frame.setExtendedState(JFrame.NORMAL); + frame.setLocation(x, y); + frame.setSize(width, height); + } + }); + } frame.requestFocus(); giveClientFocus();