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/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"; } 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/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/definitions/loaders/WorldMapLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapLoader.java index 3ce0bf4c3d..371b6491c1 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 @@ -61,14 +61,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; @@ -110,16 +110,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; } @@ -128,16 +128,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; } @@ -146,12 +146,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; } @@ -160,20 +160,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; } 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); + } +} 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/plugins/chatcommands/ChatCommandsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatcommands/ChatCommandsPlugin.java index a0d6d3d50a..3f7c821000 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 @@ -94,6 +94,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:]+)"); @@ -452,6 +453,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; } @@ -513,29 +525,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/main/java/net/runelite/client/ui/ClientUI.java b/runelite-client/src/main/java/net/runelite/client/ui/ClientUI.java index e68855b68e..3a2704f30a 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 @@ -612,21 +612,17 @@ 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()) { - if ((frame.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) + SwingUtilities.invokeLater(() -> { - SwingUtilities.invokeLater(() -> + if ((frame.getExtendedState() & JFrame.MAXIMIZED_BOTH) == JFrame.MAXIMIZED_BOTH) { frame.setExtendedState(JFrame.ICONIFIED); frame.setExtendedState(JFrame.MAXIMIZED_BOTH); - }); - } - else - { - SwingUtilities.invokeLater(() -> + } + 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. @@ -639,8 +635,8 @@ public class ClientUI frame.setExtendedState(JFrame.NORMAL); frame.setLocation(x, y); frame.setSize(width, height); - }); - } + } + }); } frame.requestFocus(); 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 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 2774f5cad8..d3e5d0a562 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 @@ -39,8 +39,12 @@ import net.runelite.client.config.OpenOSRSConfig; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import org.mockito.Mock; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -361,4 +365,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()); + } }