From 59956c2cceb035e8a62c7e1605ce05c2c2802fc2 Mon Sep 17 00:00:00 2001 From: Christopher Brown Date: Sun, 7 Apr 2024 19:34:26 -0500 Subject: [PATCH 01/67] cache: render area labels on the map --- .../java/net/runelite/cache/FontManager.java | 75 ++++++++++++++++ .../java/net/runelite/cache/FontName.java | 47 ++++++++++ .../net/runelite/cache/MapImageDumper.java | 87 +++++++++++++++++++ .../net/runelite/cache/SpriteManager.java | 19 +++- .../net/runelite/cache/WorldMapManager.java | 77 ++++++++++++++++ .../AbstractWorldMapDataDefinition.java | 40 +++++++++ .../cache/definitions/AreaDefinition.java | 6 +- .../cache/definitions/FontDefinition.java | 46 ++++++++++ .../definitions/MapSquareDefinition.java | 51 +++++++++++ .../WorldMapCompositeDefinition.java | 39 +++++++++ .../WorldMapElementDefinition.java | 36 ++++++++ .../cache/definitions/ZoneDefinition.java | 57 ++++++++++++ .../cache/definitions/loaders/AreaLoader.java | 6 +- .../cache/definitions/loaders/FontLoader.java | 52 +++++++++++ .../loaders/WorldMapCompositeLoader.java | 60 +++++++++++++ .../loaders/WorldMapDataLoader.java | 80 +++++++++++++++++ .../loaders/WorldMapElementLoader.java | 43 +++++++++ .../net/runelite/cache/region/Position.java | 13 +++ 18 files changed, 827 insertions(+), 7 deletions(-) create mode 100644 cache/src/main/java/net/runelite/cache/FontManager.java create mode 100644 cache/src/main/java/net/runelite/cache/FontName.java create mode 100644 cache/src/main/java/net/runelite/cache/WorldMapManager.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/AbstractWorldMapDataDefinition.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/FontDefinition.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/MapSquareDefinition.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/WorldMapElementDefinition.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/ZoneDefinition.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/FontLoader.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapCompositeLoader.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapDataLoader.java create mode 100644 cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapElementLoader.java diff --git a/cache/src/main/java/net/runelite/cache/FontManager.java b/cache/src/main/java/net/runelite/cache/FontManager.java new file mode 100644 index 0000000000..6af2da4c06 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/FontManager.java @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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 net.runelite.cache.definitions.FontDefinition; +import net.runelite.cache.definitions.loaders.FontLoader; +import net.runelite.cache.fs.Archive; +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 net.runelite.cache.util.Djb2; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +public class FontManager +{ + private final Store store; + private final Map fonts = new HashMap<>(); + + public FontManager(Store store) + { + this.store = store; + } + + public void load() throws IOException + { + Storage storage = store.getStorage(); + Index index = store.getIndex(IndexType.FONTS); + FontLoader fontLoader = new FontLoader(); + + for (Archive archive : index.getArchives()) + { + byte[] data = storage.loadArchive(archive); + FSFile file = archive.getFiles(data).getFiles().get(0); + FontDefinition fontDefinition = fontLoader.load(file.getContents()); + + fonts.put(archive.getNameHash(), fontDefinition); + } + } + + public FontDefinition getFont(int nameHash) + { + return fonts.get(nameHash); + } + + public FontDefinition findFontByName(String name) + { + int nameHash = Djb2.hash(name); + return this.getFont(nameHash); + } +} diff --git a/cache/src/main/java/net/runelite/cache/FontName.java b/cache/src/main/java/net/runelite/cache/FontName.java new file mode 100644 index 0000000000..202d0b1d63 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/FontName.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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; + +public enum FontName +{ + PLAIN_11("p11_full"), + PLAIN_12("p12_full"), + BOLD_12("b12_full"), + VERDANA_11("verdana_11pt_regular"), + VERDANA_13("verdana_13pt_regular"), + VERDANA_15("verdana_15pt_regular"); + + private String name; + + FontName(String name) + { + this.name = name; + } + + public String getName() + { + return this.name; + } +} diff --git a/cache/src/main/java/net/runelite/cache/MapImageDumper.java b/cache/src/main/java/net/runelite/cache/MapImageDumper.java index 83b7d5a426..302bd1721e 100644 --- a/cache/src/main/java/net/runelite/cache/MapImageDumper.java +++ b/cache/src/main/java/net/runelite/cache/MapImageDumper.java @@ -41,10 +41,12 @@ import lombok.experimental.Accessors; import lombok.extern.slf4j.Slf4j; import net.runelite.cache.definitions.AreaDefinition; +import net.runelite.cache.definitions.FontDefinition; import net.runelite.cache.definitions.ObjectDefinition; import net.runelite.cache.definitions.OverlayDefinition; import net.runelite.cache.definitions.SpriteDefinition; import net.runelite.cache.definitions.UnderlayDefinition; +import net.runelite.cache.definitions.WorldMapElementDefinition; import net.runelite.cache.definitions.loaders.OverlayLoader; import net.runelite.cache.definitions.loaders.SpriteLoader; import net.runelite.cache.definitions.loaders.UnderlayLoader; @@ -94,6 +96,8 @@ public class MapImageDumper private final RegionLoader regionLoader; private final AreaManager areas; private final SpriteManager sprites; + private final FontManager fonts; + private final WorldMapManager worldMapManager; private RSTextureProvider rsTextureProvider; private final ObjectManager objectManager; @@ -117,6 +121,10 @@ public class MapImageDumper @Setter private boolean renderIcons = true; + @Getter + @Setter + private boolean renderLabels = true; + @Getter @Setter private boolean transparency = false; @@ -136,6 +144,8 @@ public MapImageDumper(Store store, RegionLoader regionLoader) this.regionLoader = regionLoader; this.areas = new AreaManager(store); this.sprites = new SpriteManager(store); + this.fonts = new FontManager(store); + this.worldMapManager = new WorldMapManager(store); this.objectManager = new ObjectManager(store); } @@ -218,6 +228,8 @@ public MapImageDumper load() throws IOException areas.load(); sprites.load(); loadSprites(); + fonts.load(); + worldMapManager.load(); return this; } @@ -253,6 +265,7 @@ public BufferedImage drawMap(int z) drawMap(image, z); drawObjects(image, z); drawMapIcons(image, z); + drawMapLabels(image, z); return image; } @@ -919,6 +932,57 @@ private void drawMapIcons(BufferedImage image, int z) } } + private void drawMapLabels(BufferedImage image, int z) + { + if (!renderLabels) + { + return; + } + + FontName[] fontSizes = new FontName[] { FontName.VERDANA_11, FontName.VERDANA_13, FontName.VERDANA_15 }; + List elements = worldMapManager.getElements(); + for (WorldMapElementDefinition element : elements) + { + AreaDefinition area = areas.getArea(element.getAreaDefinitionId()); + if (area == null || area.getName() == null || element.getPosition().getZ() != z) + { + continue; + } + + FontName fontSize = fontSizes[area.getTextScale()]; + FontDefinition font = fonts.findFontByName(fontSize.getName()); + String areaLabel = area.getName(); + String[] lines = areaLabel.split("
"); + int ascent = 0; + + for (String line : lines) + { + int advance = 0; + int stringWidth = font.stringWidth(line); + for (int i = 0; i < line.length(); ++i) + { + char c = line.charAt(i); + SpriteDefinition sprite = sprites.findSpriteByArchiveName(fontSize.getName(), c); + if (sprite.getWidth() != 0 && sprite.getHeight() != 0) + { + Position position = element.getPosition(); + int drawX = position.getX() - regionLoader.getLowestX().getBaseX(); + int drawY = regionLoader.getHighestY().getBaseY() - position.getY() + Region.Y - 2; + blitGlyph(image, + (drawX * MAP_SCALE) + advance - (stringWidth / 2), + (drawY * MAP_SCALE) + ascent - (font.getAscent() / 2), + area.getTextColor(), + sprite + ); + } + + advance += font.getAdvances()[c]; + } + ascent += font.getAscent() / 2; + } + } + } + private ObjectDefinition findObject(int id) { return objectManager.getObject(id); @@ -1164,4 +1228,27 @@ private void blitIcon(BufferedImage dst, int x, int y, SpriteDefinition sprite) } } } + + private void blitGlyph(BufferedImage dst, int x, int y, int color, SpriteDefinition glyph) + { + int[] pixels = glyph.getPixels(); + int[] shadowPixels = new int[pixels.length]; + for (int i = 0; i < pixels.length; ++i) + { + if (pixels[i] != 0) + { + pixels[i] = color; + shadowPixels[i] = 0xFF000000; + } + } + SpriteDefinition shadow = new SpriteDefinition(); + shadow.setPixels(shadowPixels); + shadow.setOffsetX(glyph.getOffsetX()); + shadow.setOffsetY(glyph.getOffsetY()); + shadow.setWidth(glyph.getWidth()); + shadow.setHeight(glyph.getHeight()); + + blitIcon(dst, x + 1, y + 1, shadow); + blitIcon(dst, x, y, glyph); + } } diff --git a/cache/src/main/java/net/runelite/cache/SpriteManager.java b/cache/src/main/java/net/runelite/cache/SpriteManager.java index a18dd4f6ba..7c49040a87 100644 --- a/cache/src/main/java/net/runelite/cache/SpriteManager.java +++ b/cache/src/main/java/net/runelite/cache/SpriteManager.java @@ -31,6 +31,8 @@ import java.io.IOException; import java.util.Collection; import java.util.Collections; +import java.util.HashMap; +import java.util.Map; import net.runelite.cache.definitions.SpriteDefinition; import net.runelite.cache.definitions.exporters.SpriteExporter; import net.runelite.cache.definitions.loaders.SpriteLoader; @@ -39,11 +41,13 @@ import net.runelite.cache.fs.Index; import net.runelite.cache.fs.Storage; import net.runelite.cache.fs.Store; +import net.runelite.cache.util.Djb2; public class SpriteManager implements SpriteProvider { private final Store store; private final Multimap sprites = LinkedListMultimap.create(); + private final Map spriteIdsByArchiveNameHash = new HashMap<>(); public SpriteManager(Store store) { @@ -65,6 +69,7 @@ public void load() throws IOException for (SpriteDefinition sprite : defs) { sprites.put(sprite.getId(), sprite); + spriteIdsByArchiveNameHash.put(a.getNameHash(), sprite.getId()); } } } @@ -93,11 +98,23 @@ public BufferedImage getSpriteImage(SpriteDefinition sprite) return image; } + public SpriteDefinition findSpriteByArchiveName(String name, int frameId) + { + int nameHash = Djb2.hash(name); + Integer spriteId = spriteIdsByArchiveNameHash.get(nameHash); + if (spriteId != null) + { + return this.findSprite(spriteId, frameId); + } + + return null; + } + public void export(File outDir) throws IOException { for (SpriteDefinition sprite : sprites.values()) { - // I don't know why this happens + // Some sprites like ones for non-printable font characters do not have sizes if (sprite.getHeight() <= 0 || sprite.getWidth() <= 0) { continue; diff --git a/cache/src/main/java/net/runelite/cache/WorldMapManager.java b/cache/src/main/java/net/runelite/cache/WorldMapManager.java new file mode 100644 index 0000000000..d0c4622ac0 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/WorldMapManager.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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 lombok.extern.slf4j.Slf4j; +import net.runelite.cache.definitions.WorldMapCompositeDefinition; +import net.runelite.cache.definitions.WorldMapElementDefinition; +import net.runelite.cache.definitions.loaders.WorldMapCompositeLoader; +import net.runelite.cache.fs.Archive; +import net.runelite.cache.fs.ArchiveFiles; +import net.runelite.cache.fs.FSFile; +import net.runelite.cache.fs.Storage; +import net.runelite.cache.fs.Store; +import net.runelite.cache.fs.Index; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +@Slf4j +public class WorldMapManager +{ + private final Store store; + private final List worldMapCompositeDefinitions = new ArrayList<>(); + + public WorldMapManager(Store store) + { + this.store = store; + } + + public void load() throws IOException + { + Storage storage = store.getStorage(); + Index index = store.getIndex(IndexType.WORLDMAP); + Archive compositeMapArchive = index.findArchiveByName("compositemap"); + WorldMapCompositeLoader worldMapCompositeLoader = new WorldMapCompositeLoader(); + + ArchiveFiles compositeMapFiles = compositeMapArchive.getFiles(storage.loadArchive(compositeMapArchive)); + for (FSFile compositeFile : compositeMapFiles.getFiles()) + { + WorldMapCompositeDefinition composite = worldMapCompositeLoader.load(compositeFile.getContents()); + worldMapCompositeDefinitions.add(composite); + } + } + + public List getElements() + { + List elements = new ArrayList<>(); + for (WorldMapCompositeDefinition compositeDefinition : worldMapCompositeDefinitions) + { + elements.addAll(compositeDefinition.getWorldMapElementDefinitions()); + } + + return elements; + } +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/AbstractWorldMapDataDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/AbstractWorldMapDataDefinition.java new file mode 100644 index 0000000000..d56b2f5d51 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/AbstractWorldMapDataDefinition.java @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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 abstract class AbstractWorldMapDataDefinition +{ + public int minLevel; + public int levels; + public int displaySquareX; + public int displaySquareZ; + public int sourceSquareX; + public int sourceSquareZ; + public int groupId; + public int fileId; +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/AreaDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/AreaDefinition.java index 7abdbdfddd..ca76b3c880 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/AreaDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/AreaDefinition.java @@ -34,12 +34,12 @@ public class AreaDefinition public int spriteId = -1; public int field3294 = -1; public String name; - public int field3296; - public int field3297 = -1; + public int textColor; + public int category = -1; public String[] field3298 = new String[5]; public int[] field3300; public String field3308; public byte[] field3309; - public int field3310; + public int textScale; } diff --git a/cache/src/main/java/net/runelite/cache/definitions/FontDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/FontDefinition.java new file mode 100644 index 0000000000..a7b8a727b6 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/FontDefinition.java @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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 FontDefinition +{ + public int[] advances = new int[256]; + public int ascent; + + public int stringWidth(String s) + { + int width = 0; + for (int i = 0; i < s.length(); ++i) + { + char c = s.charAt(i); + width += this.advances[c]; + } + + return width; + } +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/MapSquareDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/MapSquareDefinition.java new file mode 100644 index 0000000000..57df517717 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/MapSquareDefinition.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class MapSquareDefinition extends AbstractWorldMapDataDefinition +{ + public boolean equals(Object obj) + { + if (!(obj instanceof MapSquareDefinition)) + { + return false; + } + else + { + MapSquareDefinition other = (MapSquareDefinition) obj; + return other.sourceSquareX == this.sourceSquareX && other.sourceSquareZ == this.sourceSquareZ; + } + } + + public int hashCode() + { + return this.sourceSquareX | this.sourceSquareZ << 8; + } +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java new file mode 100644 index 0000000000..70145743c4 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +@Data +public class WorldMapCompositeDefinition +{ + public final Set mapSquareDefinitions = new HashSet<>(); + public final Set zoneDefinitions = new HashSet<>(); + public final List worldMapElementDefinitions = new ArrayList<>(); +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapElementDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapElementDefinition.java new file mode 100644 index 0000000000..7c17b908df --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapElementDefinition.java @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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; +import net.runelite.cache.region.Position; + +@Data +public class WorldMapElementDefinition +{ + public Position position; + public int areaDefinitionId; + public boolean membersOnly; +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/ZoneDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/ZoneDefinition.java new file mode 100644 index 0000000000..b487d546ba --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/ZoneDefinition.java @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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; +import lombok.ToString; + +@Data +@ToString(callSuper = true) +public class ZoneDefinition extends AbstractWorldMapDataDefinition +{ + public int displayZoneX; + public int displayZoneY; + public int sourceZoneX; + public int sourceZoneZ; + + public boolean equals(Object obj) + { + if (!(obj instanceof ZoneDefinition)) + { + return false; + } + else + { + ZoneDefinition other = (ZoneDefinition) obj; + return other.sourceSquareX == this.sourceSquareX && other.sourceSquareZ == this.sourceSquareZ + && other.sourceZoneX == this.sourceZoneX && other.sourceZoneZ == this.sourceZoneZ; + } + } + + public int hashCode() + { + return this.sourceSquareX | this.sourceSquareZ << 8 | this.sourceZoneX << 16 | this.sourceZoneZ << 24; + } +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/AreaLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/AreaLoader.java index 7b47358678..077f8f27d3 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/AreaLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/AreaLoader.java @@ -65,7 +65,7 @@ else if (opcode == 3) } else if (opcode == 4) { - def.field3296 = in.read24BitInt(); + def.textColor = in.read24BitInt(); } else if (opcode == 5) { @@ -73,7 +73,7 @@ else if (opcode == 5) } else if (opcode == 6) { - def.field3310 = in.readUnsignedByte(); + def.textScale = in.readUnsignedByte(); } else if (opcode == 7) { @@ -138,7 +138,7 @@ else if (opcode == 18) } else if (opcode == 19) { - def.field3297 = in.readUnsignedShort(); + def.category = in.readUnsignedShort(); } else if (opcode == 21) { diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/FontLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/FontLoader.java new file mode 100644 index 0000000000..5a6d5ce242 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/FontLoader.java @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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.FontDefinition; + +public class FontLoader +{ + public FontDefinition load(byte[] buffer) + { + FontDefinition fontDefinition = new FontDefinition(); + + if (buffer.length == 257) + { + for (int i = 0; i < fontDefinition.advances.length; ++i) + { + fontDefinition.advances[i] = buffer[i] & 255; + } + + fontDefinition.ascent = buffer[256] & 255; + } + else + { + // buffer length 256 may exist in some version of the cache? + throw new RuntimeException("Font data buffer is the wrong size"); + } + + return fontDefinition; + } +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapCompositeLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapCompositeLoader.java new file mode 100644 index 0000000000..488d4f868d --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapCompositeLoader.java @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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.WorldMapCompositeDefinition; +import net.runelite.cache.io.InputStream; + +public class WorldMapCompositeLoader +{ + public WorldMapCompositeDefinition load(byte[] buffer) + { + WorldMapDataLoader worldMapDataLoader = new WorldMapDataLoader(); + WorldMapElementLoader worldMapElementLoader = new WorldMapElementLoader(); + + WorldMapCompositeDefinition worldMapCompositeDefinition = new WorldMapCompositeDefinition(); + InputStream in = new InputStream(buffer); + + int worldData0Count = in.readUnsignedShort(); + for (int i = 0; i < worldData0Count; ++i) + { + worldMapCompositeDefinition.mapSquareDefinitions.add(worldMapDataLoader.loadMapSquare(in)); + } + + int worldData1Amount = in.readUnsignedShort(); + for (int i = 0; i < worldData1Amount; ++i) + { + worldMapCompositeDefinition.zoneDefinitions.add(worldMapDataLoader.loadZone(in)); + } + + int iconAmount = in.readUnsignedShort(); + for (int i = 0; i < iconAmount; ++i) + { + worldMapCompositeDefinition.worldMapElementDefinitions.add(worldMapElementLoader.load(in)); + } + + return worldMapCompositeDefinition; + } +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapDataLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapDataLoader.java new file mode 100644 index 0000000000..54691e9a16 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapDataLoader.java @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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.MapSquareDefinition; +import net.runelite.cache.definitions.ZoneDefinition; +import net.runelite.cache.io.InputStream; + +public class WorldMapDataLoader +{ + public MapSquareDefinition loadMapSquare(InputStream in) + { + int worldMapDataType = in.readUnsignedByte(); + + if (worldMapDataType != 0) + { + throw new RuntimeException("Expected worldMapDataType 0 got " + worldMapDataType); + } + + MapSquareDefinition mapSquareDefinition = new MapSquareDefinition(); + mapSquareDefinition.minLevel = in.readUnsignedByte(); + mapSquareDefinition.levels = in.readUnsignedByte(); + mapSquareDefinition.displaySquareX = in.readUnsignedShort(); + mapSquareDefinition.displaySquareZ = in.readUnsignedShort(); + mapSquareDefinition.sourceSquareX = in.readUnsignedShort(); + mapSquareDefinition.sourceSquareZ = in.readUnsignedShort(); + mapSquareDefinition.groupId = in.readBigSmart2(); + mapSquareDefinition.fileId = in.readBigSmart2(); + + return mapSquareDefinition; + } + + public ZoneDefinition loadZone(InputStream in) + { + int worldMapDataType = in.readUnsignedByte(); + + if (worldMapDataType != 1) + { + throw new RuntimeException("Expected worldMapDataType 1 got " + worldMapDataType); + } + + ZoneDefinition zoneDefinition = new ZoneDefinition(); + zoneDefinition.minLevel = in.readUnsignedByte(); + zoneDefinition.levels = in.readUnsignedByte(); + zoneDefinition.displaySquareX = in.readUnsignedShort(); + zoneDefinition.displaySquareZ = in.readUnsignedShort(); + zoneDefinition.displayZoneX = in.readUnsignedByte(); + zoneDefinition.displayZoneY = in.readUnsignedByte(); + zoneDefinition.sourceSquareX = in.readUnsignedShort(); + zoneDefinition.sourceSquareZ = in.readUnsignedShort(); + zoneDefinition.sourceZoneX = in.readUnsignedByte(); + zoneDefinition.sourceZoneZ = in.readUnsignedByte(); + zoneDefinition.groupId = in.readBigSmart2(); + zoneDefinition.fileId = in.readBigSmart2(); + + return zoneDefinition; + } +} diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapElementLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapElementLoader.java new file mode 100644 index 0000000000..297d8b3749 --- /dev/null +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapElementLoader.java @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2024, Christopher Brown + * 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.WorldMapElementDefinition; +import net.runelite.cache.io.InputStream; +import net.runelite.cache.region.Position; + +public class WorldMapElementLoader +{ + public WorldMapElementDefinition load(InputStream in) + { + WorldMapElementDefinition worldMapElementDefinition = new WorldMapElementDefinition(); + + worldMapElementDefinition.areaDefinitionId = in.readBigSmart2(); + worldMapElementDefinition.position = Position.fromPacked(in.readInt()); + worldMapElementDefinition.membersOnly = in.readUnsignedByte() == 1; + + return worldMapElementDefinition; + } +} diff --git a/cache/src/main/java/net/runelite/cache/region/Position.java b/cache/src/main/java/net/runelite/cache/region/Position.java index d80144c80a..2509497135 100644 --- a/cache/src/main/java/net/runelite/cache/region/Position.java +++ b/cache/src/main/java/net/runelite/cache/region/Position.java @@ -38,6 +38,19 @@ public Position(int x, int y, int z) this.z = z; } + public static Position fromPacked(int packedPosition) + { + if (packedPosition == -1) + { + return new Position(-1, -1, -1); + } + + int z = packedPosition >> 28 & 3; + int x = packedPosition >> 14 & 16383; + int y = packedPosition & 16383; + return new Position(x, y, z); + } + @Override public String toString() { From 44a0c5f95b5ecd32f476b7f3beb8bc49167be14a Mon Sep 17 00:00:00 2001 From: Macweese <50101641+Macweese@users.noreply.github.com> Date: Mon, 8 Apr 2024 13:18:35 +0200 Subject: [PATCH 02/67] slayer: include zygomites in task weakness config tooltip --- .../java/net/runelite/client/plugins/slayer/SlayerConfig.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java index 55952cb6d2..f8b7551ee8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java @@ -139,7 +139,7 @@ default Color getTargetColor() position = 9, keyName = "weaknessPrompt", name = "Show Monster Weakness", - description = "Show an overlay on a monster when it is weak enough to finish off (Only Lizards, Gargoyles & Rockslugs)" + description = "Show an overlay on a monster when it is weak enough to finish off (Only Lizards, Gargoyles, Rockslugs & Zygomites)" ) default boolean weaknessPrompt() { From 964703a08b7b25d718a0b2e9baf5215b385ba61c Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Tue, 9 Apr 2024 09:06:11 +0200 Subject: [PATCH 03/67] chat commands: add lunar chest aliases (#17700) --- .../client/plugins/chatcommands/ChatCommandsPlugin.java | 7 +++++++ 1 file changed, 7 insertions(+) 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 4343e43705..b837fbd89b 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 @@ -2497,6 +2497,13 @@ private static String longBossName(String boss) case "vard awakened": return "Vardorvis (awakened)"; + // lunar chest variants + case "lunar chests": + case "perilous moons": + case "perilous moon": + case "moons of peril": + return "Lunar Chest"; + default: return WordUtils.capitalize(boss); } From dac426e2ff451e7f95bdbd61fba9433c360142c6 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Tue, 9 Apr 2024 00:15:00 -0700 Subject: [PATCH 04/67] Remove unused Slf4j annotations and imports --- cache/src/main/java/net/runelite/cache/WorldMapManager.java | 2 -- .../runelite/client/plugins/cluescrolls/clues/HotColdClue.java | 2 -- .../java/net/runelite/client/plugins/emojis/EmojiPlugin.java | 2 -- .../java/net/runelite/client/plugins/gpu/regions/Regions.java | 2 -- 4 files changed, 8 deletions(-) diff --git a/cache/src/main/java/net/runelite/cache/WorldMapManager.java b/cache/src/main/java/net/runelite/cache/WorldMapManager.java index d0c4622ac0..1d5b2b1aba 100644 --- a/cache/src/main/java/net/runelite/cache/WorldMapManager.java +++ b/cache/src/main/java/net/runelite/cache/WorldMapManager.java @@ -24,7 +24,6 @@ */ package net.runelite.cache; -import lombok.extern.slf4j.Slf4j; import net.runelite.cache.definitions.WorldMapCompositeDefinition; import net.runelite.cache.definitions.WorldMapElementDefinition; import net.runelite.cache.definitions.loaders.WorldMapCompositeLoader; @@ -38,7 +37,6 @@ import java.util.ArrayList; import java.util.List; -@Slf4j public class WorldMapManager { private final Store store; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/HotColdClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/HotColdClue.java index ef742c847c..88ef383d28 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/HotColdClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/HotColdClue.java @@ -36,7 +36,6 @@ import lombok.AccessLevel; import lombok.EqualsAndHashCode; import lombok.Getter; -import lombok.extern.slf4j.Slf4j; import net.runelite.api.NPC; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; @@ -55,7 +54,6 @@ @EqualsAndHashCode(callSuper = false, exclude = { "hotColdSolver", "location" }) @Getter -@Slf4j public class HotColdClue extends ClueScroll implements LocationClueScroll, LocationsClueScroll, NpcClueScroll { private static final HotColdClue BEGINNER_CLUE = new HotColdClue("Buried beneath the ground, who knows where it's found. Lucky for you, A man called Reldo may have a clue.", diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java index c439916b72..48b526f782 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/emojis/EmojiPlugin.java @@ -29,7 +29,6 @@ import javax.annotation.Nullable; import javax.inject.Inject; import joptsimple.internal.Strings; -import lombok.extern.slf4j.Slf4j; import net.runelite.api.MessageNode; import net.runelite.api.Player; import net.runelite.api.events.ChatMessage; @@ -45,7 +44,6 @@ description = "Replaces common emoticons such as :) with their corresponding emoji in the chat", enabledByDefault = false ) -@Slf4j public class EmojiPlugin extends Plugin { private static final Pattern WHITESPACE_REGEXP = Pattern.compile("[\\s\\u00A0]"); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/regions/Regions.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/regions/Regions.java index 0b6bd81769..99dce1a6cf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/regions/Regions.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/regions/Regions.java @@ -36,9 +36,7 @@ import java.util.regex.Pattern; import lombok.AccessLevel; import lombok.Getter; -import lombok.extern.slf4j.Slf4j; -@Slf4j public class Regions { private static final Pattern PATTERN = Pattern.compile("^[ \\t]*(?" + From 3bba9624f389066ccbe5603aeeaca9e0b055fdbd Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 31 Mar 2024 13:24:24 -0400 Subject: [PATCH 05/67] config: add serializers This allows having the config system serialize and deserialize arbitrary objects by annotating them with @ConfigSerializer --- .../runelite/client/config/ConfigManager.java | 41 +++++++++++++++++++ .../client/config/ConfigSerializer.java | 39 ++++++++++++++++++ .../runelite/client/config/Serializer.java | 32 +++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/config/ConfigSerializer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/config/Serializer.java diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java index bec12ce70a..9a832af122 100644 --- a/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigManager.java @@ -53,6 +53,7 @@ import java.util.Map; import java.util.Objects; import java.util.Set; +import java.util.WeakHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; @@ -130,6 +131,8 @@ public class ConfigManager @Nullable private String rsProfileKey; + private final Map> serializers = Collections.synchronizedMap(new WeakHashMap<>()); + @Inject private ConfigManager( @Nullable @Named("profile") String profile, @@ -1192,6 +1195,27 @@ Object stringToObject(String str, Type type) return gson.fromJson(str, parameterizedType); } } + if (type instanceof Class) + { + Class clazz = (Class) type; + ConfigSerializer configSerializer = clazz.getAnnotation(ConfigSerializer.class); + if (configSerializer != null) + { + Class> serializerClass = configSerializer.value(); + Serializer serializer = serializers.get(type); + if (serializer == null) + { + // Guice holds references to all jitted types. + // To allow class unloading, use a temporary child injector + // and use it to get the instance, and cache it a weak map. + serializer = RuneLite.getInjector() + .createChildInjector() + .getInstance(serializerClass); + serializers.put(type, serializer); + } + return serializer.deserialize(str); + } + } return str; } @@ -1247,6 +1271,23 @@ String objectToString(Object object) { return gson.toJson(object, Set.class); } + if (object != null) + { + ConfigSerializer configSerializer = object.getClass().getAnnotation(ConfigSerializer.class); + if (configSerializer != null) + { + Class> serializerClass = configSerializer.value(); + Serializer serializer = serializers.get(serializerClass); + if (serializer == null) + { + serializer = RuneLite.getInjector() + .createChildInjector() + .getInstance(serializerClass); + serializers.put(serializerClass, serializer); + } + return serializer.serialize(object); + } + } return object == null ? null : object.toString(); } diff --git a/runelite-client/src/main/java/net/runelite/client/config/ConfigSerializer.java b/runelite-client/src/main/java/net/runelite/client/config/ConfigSerializer.java new file mode 100644 index 0000000000..a7e2385d3d --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/config/ConfigSerializer.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.config; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE) +public @interface ConfigSerializer +{ + Class> value(); +} diff --git a/runelite-client/src/main/java/net/runelite/client/config/Serializer.java b/runelite-client/src/main/java/net/runelite/client/config/Serializer.java new file mode 100644 index 0000000000..ef296df8d3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/config/Serializer.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2024, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.config; + +public interface Serializer +{ + String serialize(T value); + + T deserialize(String s); +} From bf335dc399be7a3592ea4a75680341dcbc97d12f Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 31 Mar 2024 13:12:21 -0400 Subject: [PATCH 06/67] config: add notification overrides --- .../java/net/runelite/client/Notifier.java | 103 +++-- .../runelite/client/config/Notification.java | 91 ++++ .../client/events/NotificationFired.java | 2 + .../client/plugins/config/ConfigPanel.java | 60 ++- .../plugins/config/NotificationPanel.java | 402 ++++++++++++++++++ .../client/plugins/config/PluginListItem.java | 7 +- .../client/plugins/config/UnitFormatter.java | 7 +- 7 files changed, 629 insertions(+), 43 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/config/Notification.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/config/NotificationPanel.java diff --git a/runelite-client/src/main/java/net/runelite/client/Notifier.java b/runelite-client/src/main/java/net/runelite/client/Notifier.java index 99a2bbcb97..3e4507357f 100644 --- a/runelite-client/src/main/java/net/runelite/client/Notifier.java +++ b/runelite-client/src/main/java/net/runelite/client/Notifier.java @@ -67,6 +67,7 @@ import net.runelite.client.chat.ChatMessageManager; import net.runelite.client.chat.QueuedMessage; import net.runelite.client.config.FlashNotification; +import net.runelite.client.config.Notification; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.eventbus.EventBus; import net.runelite.client.events.NotificationFired; @@ -117,6 +118,8 @@ public String toString() private final Path notifyIconPath; private boolean terminalNotifierAvailable; private Instant flashStart; + private FlashNotification flashNotification; + private Color flashColor; private long mouseLastPressedMillis; private long lastClipMTime = CLIP_MTIME_UNLOADED; private Clip clip = null; @@ -151,21 +154,53 @@ private Notifier( storeIcon(); } + private Notification defaultNotification(TrayIcon.MessageType trayMessageType) + { + // Create a new notification just using the RuneLite notification settings + return new Notification(true, true, + true, // Not really overriden, but this it to avoid recreating the notification due to !override + runeLiteConfig.enableTrayNotifications(), trayMessageType, runeLiteConfig.notificationRequestFocus(), + runeLiteConfig.notificationSound(), runeLiteConfig.notificationVolume(), runeLiteConfig.notificationTimeout(), + runeLiteConfig.enableGameMessageNotification(), runeLiteConfig.flashNotification(), runeLiteConfig.notificationFlashColor(), + runeLiteConfig.sendNotificationsWhenFocused()); + } + public void notify(String message) { - notify(message, TrayIcon.MessageType.NONE); + var notif = defaultNotification(TrayIcon.MessageType.NONE); + notify(notif, message); } public void notify(String message, TrayIcon.MessageType type) { - eventBus.post(new NotificationFired(message, type)); + var notif = defaultNotification(type); + notify(notif, message); + } + + public void notify(Notification notification, String message) + { + if (!notification.isEnabled()) + { + return; + } + + // Non-overriden notifications use the default notification settings. + if (!notification.isOverride() || !notification.isInitialized()) + { + notification = defaultNotification(notification.getTrayIconType()); + } - if (!runeLiteConfig.sendNotificationsWhenFocused() && clientUI.isFocused()) + assert notification.isInitialized(); + + log.debug("{}", message); + eventBus.post(new NotificationFired(notification, message, notification.getTrayIconType())); + + if (!notification.isSendWhenFocused() && clientUI.isFocused()) { return; } - switch (runeLiteConfig.notificationRequestFocus()) + switch (notification.getRequestFocus()) { case REQUEST: clientUI.requestFocus(); @@ -178,21 +213,22 @@ public void notify(String message, TrayIcon.MessageType type) break; } - if (runeLiteConfig.enableTrayNotifications()) + if (notification.isTray()) { - sendNotification(buildTitle(), message, type); + sendNotification(notification, buildTitle(), message); } - switch (runeLiteConfig.notificationSound()) + switch (notification.getSound()) { case NATIVE: Toolkit.getDefaultToolkit().beep(); break; case CUSTOM: - executorService.submit(this::playCustomSound); + var n = notification; + executorService.submit(() -> playCustomSound(n)); } - if (runeLiteConfig.enableGameMessageNotification() && client.getGameState() == GameState.LOGGED_IN) + if (notification.isGameMessage() && client.getGameState() == GameState.LOGGED_IN) { final String formattedMessage = new ChatMessageBuilder() .append(ChatColorType.HIGHLIGHT) @@ -206,13 +242,13 @@ public void notify(String message, TrayIcon.MessageType type) .build()); } - if (runeLiteConfig.flashNotification() != FlashNotification.DISABLED) + if (notification.getFlash() != FlashNotification.DISABLED) { + flashNotification = notification.getFlash(); + flashColor = notification.getFlashColor(); flashStart = Instant.now(); mouseLastPressedMillis = client.getMouseLastPressedMillis(); } - - log.debug(message); } private String buildTitle() @@ -234,12 +270,13 @@ private String buildTitle() public void processFlash(final Graphics2D graphics) { - FlashNotification flashNotification = runeLiteConfig.flashNotification(); - - if (flashStart == null || client.getGameState() != GameState.LOGGED_IN + if (flashStart == null || flashNotification == null || flashColor == null + || client.getGameState() != GameState.LOGGED_IN || flashNotification == FlashNotification.DISABLED) { flashStart = null; + flashNotification = null; + flashColor = null; return; } @@ -250,6 +287,8 @@ public void processFlash(final Graphics2D graphics) case FLASH_TWO_SECONDS: case SOLID_TWO_SECONDS: flashStart = null; + flashNotification = null; + flashColor = null; return; case SOLID_UNTIL_CANCELLED: case FLASH_UNTIL_CANCELLED: @@ -259,6 +298,8 @@ public void processFlash(final Graphics2D graphics) || client.getMouseLastPressedMillis() > mouseLastPressedMillis) && clientUI.isFocused()) { flashStart = null; + flashNotification = null; + flashColor = null; return; } break; @@ -274,15 +315,15 @@ public void processFlash(final Graphics2D graphics) } final Color color = graphics.getColor(); - graphics.setColor(runeLiteConfig.notificationFlashColor()); + graphics.setColor(flashColor); graphics.fill(new Rectangle(client.getCanvas().getSize())); graphics.setColor(color); } private void sendNotification( + final Notification notification, final String title, - final String message, - final TrayIcon.MessageType type) + final String message) { final String escapedTitle = SHELL_ESCAPE.escape(title); final String escapedMessage = SHELL_ESCAPE.escape(message); @@ -290,31 +331,31 @@ private void sendNotification( switch (OSType.getOSType()) { case Linux: - sendLinuxNotification(escapedTitle, escapedMessage, type); + sendLinuxNotification(notification, escapedTitle, escapedMessage); break; case MacOS: sendMacNotification(escapedTitle, escapedMessage); break; default: - sendTrayNotification(title, message, type); + sendTrayNotification(notification, title, message); } } private void sendTrayNotification( + final Notification notification, final String title, - final String message, - final TrayIcon.MessageType type) + final String message) { if (clientUI.getTrayIcon() != null) { - clientUI.getTrayIcon().displayMessage(title, message, type); + clientUI.getTrayIcon().displayMessage(title, message, notification.getTrayIconType()); } } private void sendLinuxNotification( + final Notification notification, final String title, - final String message, - final TrayIcon.MessageType type) + final String message) { final List commands = new ArrayList<>(); commands.add("notify-send"); @@ -325,11 +366,11 @@ private void sendLinuxNotification( commands.add("-i"); commands.add(SHELL_ESCAPE.escape(notifyIconPath.toAbsolutePath().toString())); commands.add("-u"); - commands.add(toUrgency(type)); - if (runeLiteConfig.notificationTimeout() > 0) + commands.add(toUrgency(notification.getTrayIconType())); + if (notification.getTimeout() > 0) { commands.add("-t"); - commands.add(String.valueOf(runeLiteConfig.notificationTimeout())); + commands.add(String.valueOf(notification.getTimeout())); } executorService.submit(() -> @@ -350,7 +391,7 @@ private void sendLinuxNotification( } // fall back to tray notification - sendTrayNotification(title, message, type); + sendTrayNotification(notification, title, message); }); } @@ -447,7 +488,7 @@ private static String toUrgency(TrayIcon.MessageType type) } } - private synchronized void playCustomSound() + private synchronized void playCustomSound(Notification notification) { long currentMTime = NOTIFICATION_FILE.exists() ? NOTIFICATION_FILE.lastModified() : CLIP_MTIME_BUILTIN; if (clip == null || currentMTime != lastClipMTime || !clip.isOpen()) @@ -479,7 +520,7 @@ private synchronized void playCustomSound() } // converts user controlled linear volume ranging 1-100 to exponential decibel gains - float volume = runeLiteConfig.notificationVolume() / 100f; + float volume = notification.getVolume() / 100f; float gainDB = (float) Math.log10(volume) * 20; FloatControl gainControl = (FloatControl) clip.getControl(FloatControl.Type.MASTER_GAIN); gainControl.setValue(gainDB); diff --git a/runelite-client/src/main/java/net/runelite/client/config/Notification.java b/runelite-client/src/main/java/net/runelite/client/config/Notification.java new file mode 100644 index 0000000000..54b4d4cd57 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/config/Notification.java @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2024, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.config; + +import com.google.gson.Gson; +import com.google.inject.Inject; +import java.awt.Color; +import java.awt.TrayIcon; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.With; +import net.runelite.client.Notifier; + +@ConfigSerializer(NotificationSerializer.class) +@AllArgsConstructor +@NoArgsConstructor(force = true) +@Getter +@With +public class Notification +{ + public static final Notification OFF = new Notification(); + public static final Notification ON = new Notification().withEnabled(true); + + boolean enabled; + // Whether this has been initialized with the RuneLite config notification settings. + // It is used to determine if the settings should be applied to it when first enabled. + boolean initialized; + boolean override; + boolean tray; + transient TrayIcon.MessageType trayIconType = TrayIcon.MessageType.NONE; + RequestFocusType requestFocus; + Notifier.NativeCustomOff sound; + int volume; + int timeout; + boolean gameMessage; + FlashNotification flash; + Color flashColor; + boolean sendWhenFocused; +} + +class NotificationSerializer implements Serializer +{ + private final Gson gson; + + @Inject + private NotificationSerializer(Gson gson) + { + this.gson = gson; + } + + @Override + public String serialize(Notification value) + { + return gson.toJson(value); + } + + @Override + public Notification deserialize(String s) + { + if ("true".equals(s) || "false".equals(s)) + { + return new Notification() + .withEnabled(Boolean.parseBoolean(s)); + } + + return gson.fromJson(s, Notification.class); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/events/NotificationFired.java b/runelite-client/src/main/java/net/runelite/client/events/NotificationFired.java index ecf1ffaf2d..79dd5499bb 100644 --- a/runelite-client/src/main/java/net/runelite/client/events/NotificationFired.java +++ b/runelite-client/src/main/java/net/runelite/client/events/NotificationFired.java @@ -26,10 +26,12 @@ import java.awt.TrayIcon; import lombok.Value; +import net.runelite.client.config.Notification; @Value public class NotificationFired { + final Notification notification; final String message; final TrayIcon.MessageType type; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index 88e683a089..61ee5b0d47 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -47,6 +47,7 @@ import java.util.Set; import java.util.TreeMap; import javax.inject.Inject; +import javax.inject.Provider; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.ImageIcon; @@ -84,6 +85,7 @@ import net.runelite.client.config.ConfigSectionDescriptor; import net.runelite.client.config.Keybind; import net.runelite.client.config.ModifierlessKeybind; +import net.runelite.client.config.Notification; import net.runelite.client.config.Range; import net.runelite.client.config.Units; import net.runelite.client.eventbus.Subscribe; @@ -113,6 +115,7 @@ class ConfigPanel extends PluginPanel private static final int SPINNER_FIELD_WIDTH = 6; private static final ImageIcon SECTION_EXPAND_ICON; private static final ImageIcon SECTION_RETRACT_ICON; + static final ImageIcon CONFIG_ICON; static final ImageIcon BACK_ICON; private static final Map sectionExpandStates = new HashMap<>(); @@ -127,6 +130,8 @@ class ConfigPanel extends PluginPanel SECTION_EXPAND_ICON = new ImageIcon(sectionRetractIcon); final BufferedImage sectionExpandIcon = ImageUtil.rotateImage(sectionRetractIcon, Math.PI / 2); SECTION_RETRACT_ICON = new ImageIcon(sectionExpandIcon); + BufferedImage configIcon = ImageUtil.loadImageResource(ConfigPanel.class, "config_edit_icon.png"); + CONFIG_ICON = new ImageIcon(configIcon); } private final PluginListPanel pluginList; @@ -134,6 +139,7 @@ class ConfigPanel extends PluginPanel private final PluginManager pluginManager; private final ExternalPluginManager externalPluginManager; private final ColorPickerManager colorPickerManager; + private final Provider notificationPanelProvider; private final TitleCaseListCellRenderer listCellRenderer = new TitleCaseListCellRenderer(); @@ -144,8 +150,14 @@ class ConfigPanel extends PluginPanel private PluginConfigurationDescriptor pluginConfig = null; @Inject - private ConfigPanel(PluginListPanel pluginList, ConfigManager configManager, PluginManager pluginManager, - ExternalPluginManager externalPluginManager, ColorPickerManager colorPickerManager) + private ConfigPanel( + PluginListPanel pluginList, + ConfigManager configManager, + PluginManager pluginManager, + ExternalPluginManager externalPluginManager, + ColorPickerManager colorPickerManager, + Provider notificationPanelProvider + ) { super(false); @@ -154,6 +166,7 @@ private ConfigPanel(PluginListPanel pluginList, ConfigManager configManager, Plu this.pluginManager = pluginManager; this.externalPluginManager = externalPluginManager; this.colorPickerManager = colorPickerManager; + this.notificationPanelProvider = notificationPanelProvider; setLayout(new BorderLayout()); setBackground(ColorScheme.DARK_GRAY_COLOR); @@ -372,6 +385,10 @@ else if (cid.getType() == Keybind.class || cid.getType() == ModifierlessKeybind. { item.add(createKeybind(cd, cid), BorderLayout.EAST); } + else if (cid.getType() == Notification.class) + { + item.add(createNotification(cd, cid), BorderLayout.EAST); + } else if (cid.getType() instanceof ParameterizedType) { ParameterizedType parameterizedType = (ParameterizedType) cid.getType(); @@ -457,7 +474,7 @@ private JSpinner createIntSpinner(ConfigDescriptor cd, ConfigItemDescriptor cid) Units units = cid.getUnits(); if (units != null) { - spinnerTextField.setFormatterFactory(new UnitFormatterFactory(units)); + spinnerTextField.setFormatterFactory(new UnitFormatterFactory(units.value())); } return spinner; @@ -638,6 +655,43 @@ public void focusLost(FocusEvent e) return button; } + private JPanel createNotification(ConfigDescriptor cd, ConfigItemDescriptor cid) + { + JPanel panel = new JPanel(); + panel.setLayout(new BorderLayout()); + + JButton button = new JButton(ConfigPanel.CONFIG_ICON); + SwingUtil.removeButtonDecorations(button); + button.setPreferredSize(new Dimension(25, 0)); + button.addActionListener(l -> + { + var muxer = pluginList.getMuxer(); + var notifPanel = notificationPanelProvider.get(); + notifPanel.init(cd, cid); + muxer.pushState(notifPanel); + }); + panel.add(button, BorderLayout.WEST); + + JCheckBox checkbox = new JCheckBox(); + { + Notification notif = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName(), Notification.class); + checkbox.setSelected(notif.isEnabled()); + } + checkbox.addActionListener(ae -> + { + button.setVisible(checkbox.isSelected()); + + Notification notif = configManager.getConfiguration(cd.getGroup().value(), cid.getItem().keyName(), Notification.class); + configManager.setConfiguration(cd.getGroup().value(), cid.getItem().keyName(), notif.withEnabled(checkbox.isSelected())); + }); + checkbox.setBackground(ColorScheme.LIGHT_GRAY_COLOR); + panel.add(checkbox, BorderLayout.EAST); + + // button visibility is tied to the checkbox + button.setVisible(checkbox.isSelected()); + return panel; + } + private JList> createList(ConfigDescriptor cd, ConfigItemDescriptor cid) { ParameterizedType parameterizedType = (ParameterizedType) cid.getType(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/NotificationPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/NotificationPanel.java new file mode 100644 index 0000000000..23a559cf2b --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/NotificationPanel.java @@ -0,0 +1,402 @@ +/* + * Copyright (c) 2024, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.config; + +import com.google.common.primitives.Ints; +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.TrayIcon; +import java.awt.event.ItemEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.function.Consumer; +import javax.inject.Inject; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JFormattedTextField; +import javax.swing.JLabel; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSpinner; +import javax.swing.ScrollPaneConstants; +import javax.swing.SpinnerModel; +import javax.swing.SpinnerNumberModel; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import lombok.extern.slf4j.Slf4j; +import net.runelite.client.Notifier; +import net.runelite.client.config.ConfigDescriptor; +import net.runelite.client.config.ConfigItemDescriptor; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.FlashNotification; +import net.runelite.client.config.Notification; +import net.runelite.client.config.RequestFocusType; +import net.runelite.client.config.RuneLiteConfig; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.ui.components.ColorJButton; +import net.runelite.client.ui.components.TitleCaseListCellRenderer; +import net.runelite.client.ui.components.colorpicker.ColorPickerManager; +import net.runelite.client.ui.components.colorpicker.RuneliteColorPicker; +import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.SwingUtil; +import net.runelite.client.util.Text; + +@Slf4j +class NotificationPanel extends PluginPanel +{ + private final ConfigManager configManager; + private final ColorPickerManager colorPickerManager; + + private final JLabel title; + private final FixedWidthPanel mainPanel; + private final JCheckBox enabled; + + private ConfigDescriptor configDescriptor; + private ConfigItemDescriptor configItemDescriptor; + + @Inject + private NotificationPanel( + ConfigManager configManager, + ColorPickerManager colorPickerManager, + RuneLiteConfig runeLiteConfig, + PluginListPanel pluginList + ) + { + super(false); + + this.configManager = configManager; + this.colorPickerManager = colorPickerManager; + + setLayout(new BorderLayout()); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + JPanel topPanel = new JPanel(); + topPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + topPanel.setLayout(new BorderLayout(0, BORDER_OFFSET)); + add(topPanel, BorderLayout.NORTH); + + mainPanel = new FixedWidthPanel(); + mainPanel.setBorder(new EmptyBorder(8, 10, 10, 10)); + mainPanel.setLayout(new DynamicGridLayout(0, 1, 0, 5)); + mainPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + + JPanel contentsPanel = new FixedWidthPanel(); + contentsPanel.setLayout(new BorderLayout()); + contentsPanel.add(mainPanel, BorderLayout.NORTH); + + JScrollPane scrollPane = new JScrollPane(contentsPanel); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + add(scrollPane, BorderLayout.CENTER); + + JButton topPanelBackButton = new JButton(ConfigPanel.BACK_ICON); + SwingUtil.removeButtonDecorations(topPanelBackButton); + topPanelBackButton.setPreferredSize(new Dimension(22, 0)); + topPanelBackButton.setBorder(new EmptyBorder(0, 0, 0, 5)); + topPanelBackButton.addActionListener(e -> pluginList.getMuxer().popState()); + topPanelBackButton.setToolTipText("Back"); + topPanel.add(topPanelBackButton, BorderLayout.WEST); + + title = new JLabel(); + title.setForeground(Color.WHITE); + topPanel.add(title, BorderLayout.CENTER); + + enabled = new JCheckBox(); + enabled.addActionListener(l -> + { + Notification notif = loadNotification(); + // If the notification has not been initialized yet, apply default config from the RuneLite settings + if (enabled.isSelected() && !notif.isInitialized()) + { + log.debug("Initializing notification {}.{}", configDescriptor.getGroup().value(), configItemDescriptor.getItem().name()); + notif = new Notification(true, true, true, + runeLiteConfig.enableTrayNotifications(), TrayIcon.MessageType.NONE, runeLiteConfig.notificationRequestFocus(), + runeLiteConfig.notificationSound(), runeLiteConfig.notificationVolume(), runeLiteConfig.notificationTimeout(), + runeLiteConfig.enableGameMessageNotification(), runeLiteConfig.flashNotification(), runeLiteConfig.notificationFlashColor(), + runeLiteConfig.sendNotificationsWhenFocused()); + } + else + { + notif = notif.withOverride(enabled.isSelected()); + } + saveNotification(notif); + rebuild(notif); + }); + } + + private void item(String name, String description, Component component) + { + JPanel item = new JPanel(); + item.setLayout(new BorderLayout()); + item.setMinimumSize(new Dimension(PANEL_WIDTH, 0)); + JLabel configEntryName = new JLabel(name); + configEntryName.setForeground(Color.WHITE); + if (!"".equals(description)) + { + configEntryName.setToolTipText("" + name + ":
" + description + ""); + } + item.add(configEntryName, BorderLayout.CENTER); + item.add(component, BorderLayout.EAST); + mainPanel.add(item); + } + + private JCheckBox checkbox(boolean selected) + { + JCheckBox checkbox = new JCheckBox(); + checkbox.setSelected(selected); + return checkbox; + } + + private > JComboBox> combobox(Class clazz, T value) + { + JComboBox> box = new JComboBox<>(clazz.getEnumConstants()); + // set renderer prior to calling box.getPreferredSize(), since it will invoke the renderer + // to build components for each combobox element in order to compute the display size of the + // combobox + box.setRenderer(new TitleCaseListCellRenderer()); + box.setPreferredSize(new Dimension(box.getPreferredSize().width, 22)); + + try + { + Enum selectedItem = Enum.valueOf(clazz, value.name()); + box.setSelectedItem(selectedItem); + box.setToolTipText(Text.titleCase(selectedItem)); + } + catch (IllegalArgumentException ex) + { + log.debug("invalid selected item", ex); + } + box.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + box.setToolTipText(Text.titleCase((Enum) box.getSelectedItem())); + } + }); + + return box; + } + + private JSpinner createIntSpinner(int min, int max, int value, String unit) + { + // Config may previously have been out of range + value = Ints.constrainToRange(value, min, max); + + SpinnerModel model = new SpinnerNumberModel(value, min, max, 1); + JSpinner spinner = new JSpinner(model); + Component editor = spinner.getEditor(); + JFormattedTextField spinnerTextField = ((JSpinner.DefaultEditor) editor).getTextField(); + spinnerTextField.setColumns(6); + spinnerTextField.setFormatterFactory(new UnitFormatterFactory(unit)); + return spinner; + } + + private ColorJButton createColorPicker(String name, Color existing, Consumer onClose) + { + ColorJButton colorPickerBtn; + + if (existing == null) + { + colorPickerBtn = new ColorJButton("Pick a color", Color.BLACK); + } + else + { + String colorHex = "#" + ColorUtil.colorToAlphaHexCode(existing).toUpperCase(); + colorPickerBtn = new ColorJButton(colorHex, existing); + } + + colorPickerBtn.setFocusable(false); + colorPickerBtn.addMouseListener(new MouseAdapter() + { + @Override + public void mouseClicked(MouseEvent e) + { + RuneliteColorPicker colorPicker = colorPickerManager.create( + SwingUtilities.windowForComponent(NotificationPanel.this), + colorPickerBtn.getColor(), + name, + false); + colorPicker.setLocationRelativeTo(colorPickerBtn); + colorPicker.setOnColorChange(c -> + { + colorPickerBtn.setColor(c); + colorPickerBtn.setText("#" + ColorUtil.colorToAlphaHexCode(c).toUpperCase()); + }); + colorPicker.setOnClose(onClose); + colorPicker.setVisible(true); + } + }); + + return colorPickerBtn; + } + + void init(ConfigDescriptor cd, ConfigItemDescriptor cid) + { + title.setText(cid.name()); + + configDescriptor = cd; + configItemDescriptor = cid; + + var notif = loadNotification(); + enabled.setSelected(notif.isOverride()); + + rebuild(notif); + } + + private void rebuild(Notification notif) + { + mainPanel.removeAll(); + + mainPanel.add(enabled); + + item("Customize notification", "", enabled); + + if (notif.isOverride()) + { + var checkboxTray = checkbox(notif.isTray()); + checkboxTray.addActionListener(ae -> + { + var n = loadNotification(); + saveNotification(n.withTray(checkboxTray.isSelected())); + }); + item("Tray notification", "Enables tray notifications", checkboxTray); + + var comboboxRequestFocus = combobox(RequestFocusType.class, notif.getRequestFocus()); + comboboxRequestFocus.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + var n = loadNotification(); + saveNotification(n.withRequestFocus((RequestFocusType) comboboxRequestFocus.getSelectedItem())); + } + }); + item("Request focus", "Configures the window focus request type on notification", comboboxRequestFocus); + + var comboboxSound = combobox(Notifier.NativeCustomOff.class, notif.getSound()); + comboboxSound.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + var n = loadNotification(); + saveNotification(n.withSound((Notifier.NativeCustomOff) comboboxSound.getSelectedItem())); + } + }); + item("Notification sound", "Enables the playing of a beep sound when notifications are displayed", comboboxSound); + + var spinnerVolume = createIntSpinner(0, 100, notif.getVolume(), "%"); + spinnerVolume.addChangeListener(ce -> + { + var n = loadNotification(); + saveNotification(n.withVolume((int) spinnerVolume.getValue())); + }); + item("Notification volume", "Configures the volume of custom notifications (does not control native volume).", spinnerVolume); + + var spinnerTimeout = createIntSpinner(0, Integer.MAX_VALUE, notif.getTimeout(), "ms"); + spinnerVolume.addChangeListener(ce -> + { + var n = loadNotification(); + saveNotification(n.withTimeout((int) spinnerTimeout.getValue())); + }); + item("Notification timeout", "How long notification will be shown in milliseconds. A value of 0 will make it use the system configuration. (Linux only)", spinnerTimeout); + + var checkboxGameMessage = checkbox(notif.isGameMessage()); + checkboxGameMessage.addActionListener(ae -> + { + var n = loadNotification(); + saveNotification(n.withGameMessage(checkboxGameMessage.isSelected())); + }); + item("Game message notification", "Adds a notification message to the chatbox", checkboxGameMessage); + + var comboboxFlash = combobox(FlashNotification.class, notif.getFlash()); + comboboxFlash.addItemListener(e -> + { + if (e.getStateChange() == ItemEvent.SELECTED) + { + var n = loadNotification(); + saveNotification(n.withFlash((FlashNotification) comboboxFlash.getSelectedItem())); + } + }); + item("Flash", "Flashes the game frame as a notification", combobox(FlashNotification.class, notif.getFlash())); + + var colorpickerFlashColor = createColorPicker("Flash color", notif.getFlashColor(), c -> + { + var n = loadNotification(); + saveNotification(n.withFlashColor(c)); + }); + item("Flash color", "Sets the color of the notification flashes.", colorpickerFlashColor); + + var checkboxSendWhenFocused = checkbox(notif.isSendWhenFocused()); + checkboxSendWhenFocused.addActionListener(ae -> + { + var n = loadNotification(); + saveNotification(n.withSendWhenFocused(checkboxSendWhenFocused.isSelected())); + }); + item("Send notifications when focused", "Sends the notification even when the client is focused", checkboxSendWhenFocused); + + JButton resetButton = new JButton("Reset"); + resetButton.addActionListener((e) -> + { + final int result = JOptionPane.showOptionDialog(resetButton, "Are you sure you want to reset this notification configuration?", + "Are you sure?", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, + null, new String[]{"Yes", "No"}, "No"); + + if (result == JOptionPane.YES_OPTION) + { + enabled.setSelected(false); + + // Reset every setting + var n = new Notification().withEnabled(true); + saveNotification(n); + rebuild(n); + } + }); + mainPanel.add(resetButton); + } + else + { + JPanel infoPanel = new JPanel(); + infoPanel.setLayout(new BorderLayout()); + mainPanel.add(infoPanel); + + infoPanel.setBorder(new EmptyBorder(10, 0, 0, 0)); + infoPanel.add(new JLabel("Notification settings can be customized for each type of notification. Notifications without custom settings use the default settings found in the 'RuneLite' configuration under 'Notification Settings'.")); + } + } + + private Notification loadNotification() + { + return configManager.getConfiguration(configDescriptor.getGroup().value(), configItemDescriptor.getItem().keyName(), Notification.class); + } + + private void saveNotification(Notification notification) + { + configManager.setConfiguration(configDescriptor.getGroup().value(), configItemDescriptor.getItem().keyName(), notification); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index 09220a0176..880da7877c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -54,7 +54,6 @@ class PluginListItem extends JPanel implements SearchablePlugin { - private static final ImageIcon CONFIG_ICON; private static final ImageIcon ON_STAR; private static final ImageIcon OFF_STAR; @@ -71,9 +70,7 @@ class PluginListItem extends JPanel implements SearchablePlugin static { - BufferedImage configIcon = ImageUtil.loadImageResource(ConfigPanel.class, "config_edit_icon.png"); BufferedImage onStar = ImageUtil.loadImageResource(ConfigPanel.class, "star_on.png"); - CONFIG_ICON = new ImageIcon(configIcon); ON_STAR = new ImageIcon(onStar); BufferedImage offStar = ImageUtil.luminanceScale( @@ -133,7 +130,7 @@ class PluginListItem extends JPanel implements SearchablePlugin JMenuItem configMenuItem = null; if (pluginConfig.hasConfigurables()) { - JButton configButton = new JButton(CONFIG_ICON); + JButton configButton = new JButton(ConfigPanel.CONFIG_ICON); SwingUtil.removeButtonDecorations(configButton); configButton.setPreferredSize(new Dimension(25, 0)); configButton.setVisible(false); @@ -141,7 +138,7 @@ class PluginListItem extends JPanel implements SearchablePlugin configButton.addActionListener(e -> { - configButton.setIcon(CONFIG_ICON); + configButton.setIcon(ConfigPanel.CONFIG_ICON); openGroupConfigPanel(); }); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/UnitFormatter.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/UnitFormatter.java index efe2181d9b..0f7f477314 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/UnitFormatter.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/UnitFormatter.java @@ -29,15 +29,14 @@ import java.util.Map; import javax.swing.JFormattedTextField; import lombok.RequiredArgsConstructor; -import net.runelite.client.config.Units; final class UnitFormatter extends JFormattedTextField.AbstractFormatter { private final String units; - UnitFormatter(Units units) + UnitFormatter(String units) { - this.units = units.value(); + this.units = units; } @Override @@ -75,7 +74,7 @@ public String valueToString(final Object value) @RequiredArgsConstructor final class UnitFormatterFactory extends JFormattedTextField.AbstractFormatterFactory { - private final Units units; + private final String units; private final Map formatters = new HashMap<>(); @Override From 54f206647da17ae5e1c9944125d719b07bea27df Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 31 Mar 2024 13:19:48 -0400 Subject: [PATCH 07/67] notifier: remove double shell escaping With the exception of tha osascript invocation, all instances of the message and title are passed to ProcessBuilder as a List, which already handles escaping correctly. osascript requires escaping the strings when interpreted by AppleScript, and not due to the shell. --- .../src/main/java/net/runelite/client/Notifier.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/Notifier.java b/runelite-client/src/main/java/net/runelite/client/Notifier.java index 3e4507357f..9c7d37c04d 100644 --- a/runelite-client/src/main/java/net/runelite/client/Notifier.java +++ b/runelite-client/src/main/java/net/runelite/client/Notifier.java @@ -325,16 +325,13 @@ private void sendNotification( final String title, final String message) { - final String escapedTitle = SHELL_ESCAPE.escape(title); - final String escapedMessage = SHELL_ESCAPE.escape(message); - switch (OSType.getOSType()) { case Linux: - sendLinuxNotification(notification, escapedTitle, escapedMessage); + sendLinuxNotification(notification, title, message); break; case MacOS: - sendMacNotification(escapedTitle, escapedMessage); + sendMacNotification(title, message); break; default: sendTrayNotification(notification, title, message); @@ -416,11 +413,11 @@ private void sendMacNotification(final String title, final String message) commands.add("-e"); final String script = "display notification " + DOUBLE_QUOTE + - message + + SHELL_ESCAPE.escape(message) + DOUBLE_QUOTE + " with title " + DOUBLE_QUOTE + - title + + SHELL_ESCAPE.escape(title) + DOUBLE_QUOTE; commands.add(script); From dd3a3f11d0099efe165872ebf030978e34882ab5 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 31 Mar 2024 13:12:31 -0400 Subject: [PATCH 08/67] pluin panel: fix typo --- .../src/main/java/net/runelite/client/ui/PluginPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java index e4a89930ea..b699717142 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/PluginPanel.java @@ -72,7 +72,7 @@ protected PluginPanel(boolean wrap) wrappedPanel = new JPanel(); // Adjust the preferred size to expand to width of scrollbar to - // to preven scrollbar overlapping over contents + // prevent scrollbar overlapping over contents wrappedPanel.setPreferredSize(OUTER_PREFERRED_SIZE); wrappedPanel.setLayout(new BorderLayout()); wrappedPanel.add(scrollPane, BorderLayout.CENTER); From 181529aef1eab4e42bee0e5b1b99cd8c83dc7e75 Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Tue, 9 Apr 2024 15:36:55 -0700 Subject: [PATCH 09/67] skill calculator: Allow multiple bonuses to apply at once Co-authored-by: Jordan Atwood --- .../skillcalculator/SkillCalculator.java | 43 +++-- .../skillcalculator/skills/PrayerAction.java | 165 ++++++++++-------- .../skillcalculator/skills/PrayerBonus.java | 40 ++++- .../skills/RunecraftAction.java | 14 +- .../skillcalculator/skills/SkillAction.java | 9 +- .../skillcalculator/skills/SkillBonus.java | 14 ++ 6 files changed, 185 insertions(+), 100 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java index f029397385..2b0143c103 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java @@ -36,7 +36,11 @@ import java.awt.event.MouseEvent; import java.text.NumberFormat; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.function.Consumer; import java.util.stream.Collectors; import javax.inject.Inject; @@ -84,7 +88,7 @@ class SkillCalculator extends JPanel private final List uiActionSlots = new ArrayList<>(); private final UICombinedActionSlot combinedActionSlot; private final ArrayList combinedActionSlots = new ArrayList<>(); - private final List bonusCheckBoxes = new ArrayList<>(); + private final Map bonusCheckBoxes = new HashMap<>(); private final IconTextField searchBar = new IconTextField(); private CalculatorType currentCalculator; @@ -92,7 +96,7 @@ class SkillCalculator extends JPanel private int currentXP = Experience.getXpForLevel(currentLevel); private int targetLevel = currentLevel + 1; private int targetXP = Experience.getXpForLevel(targetLevel); - private SkillBonus currentBonus = null; + private final Set currentBonuses = new HashSet<>(); @Inject SkillCalculator(Client client, ClientThread clientThread, UICalculatorInputArea uiInput, SpriteManager spriteManager, ItemManager itemManager) @@ -153,7 +157,7 @@ void openCalculator(CalculatorType calculatorType, boolean forceReload) if (forceReload || currentCalculator != calculatorType) { currentCalculator = calculatorType; - currentBonus = null; + currentBonuses.clear(); @Varp int endGoalVarp = endGoalVarpForSkill(calculatorType.getSkill()); int endGoal = client.getVarpValue(endGoalVarp); @@ -281,22 +285,32 @@ private JPanel buildCheckboxPanel(SkillBonus bonus) uiOption.add(uiLabel, BorderLayout.WEST); uiOption.add(uiCheckbox, BorderLayout.EAST); - bonusCheckBoxes.add(uiCheckbox); + bonusCheckBoxes.put(bonus, uiCheckbox); return uiOption; } private void adjustCheckboxes(JCheckBox target, SkillBonus bonus) { - for (JCheckBox otherSelectedCheckbox : bonusCheckBoxes) + // Check if target is stackable with any other bonuses + for (Map.Entry entry : bonusCheckBoxes.entrySet()) { - if (otherSelectedCheckbox != target) + if (entry.getValue() != target && !entry.getKey().getCanBeStackedWith().contains(bonus)) { - otherSelectedCheckbox.setSelected(false); + currentBonuses.remove(entry.getKey()); + entry.getValue().setSelected(false); } } - adjustXPBonus(target.isSelected() ? bonus : null); + if (target.isSelected()) + { + currentBonuses.add(bonus); + } + else + { + currentBonuses.remove(bonus); + } + calculate(); } private void renderActionSlots() @@ -387,9 +401,12 @@ private void calculate() int neededXP = targetXP - currentXP; SkillAction action = slot.getAction(); float bonus = 1f; - if (currentBonus != null && action.isBonusApplicable(currentBonus)) + for (SkillBonus skillBonus : currentBonuses) { - bonus = currentBonus.getValue(); + if (action.isBonusApplicable(skillBonus)) + { + bonus *= skillBonus.getValue(); + } } final int xp = Math.round(action.getXp() * bonus * 10f); @@ -434,12 +451,6 @@ private void updateInputFields() calculate(); } - private void adjustXPBonus(SkillBonus bonus) - { - currentBonus = bonus; - calculate(); - } - private void onFieldCurrentLevelUpdated() { currentLevel = enforceSkillBounds(uiInput.getCurrentLevelInput()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java index 83364b5edc..5a1684ed65 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java @@ -24,92 +24,113 @@ */ package net.runelite.client.plugins.skillcalculator.skills; -import java.util.Arrays; import java.util.EnumSet; +import java.util.Set; +import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.ItemID; +import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.DEMONIC_OFFERING; +import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.MORYTANIA_DIARY_3_SHADES; +@AllArgsConstructor @Getter public enum PrayerAction implements ItemSkillAction { - ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD, 1, 130), - ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD, 1, 182), - ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD, 1, 286), - ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD, 1, 364), - ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD, 1, 454), - ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD, 1, 480), - ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD, 1, 494), - ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD, 1, 520), - ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD, 1, 584), - ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD, 1, 650), - ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD, 1, 716), - ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD, 1, 754), - ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD, 1, 780), - ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD, 1, 832), - ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD, 1, 884), - ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD, 1, 936), - ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD, 1, 1040), - ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD, 1, 1104), - ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD, 1, 1170), - ENSOULED_HELLHOUND_HEAD(ItemID.ENSOULED_HELLHOUND_HEAD, 1, 1200), - ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD, 1, 1234), - ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD, 1, 1300), - ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD, 1, 1560), - FIENDISH_ASHES(ItemID.FIENDISH_ASHES, 1, 10, PrayerBonus.DEMONIC_OFFERING), - VILE_ASHES(ItemID.VILE_ASHES, 1, 25, PrayerBonus.DEMONIC_OFFERING), - MALICIOUS_ASHES(ItemID.MALICIOUS_ASHES, 1, 65, PrayerBonus.DEMONIC_OFFERING), - ABYSSAL_ASHES(ItemID.ABYSSAL_ASHES, 1, 85, PrayerBonus.DEMONIC_OFFERING), - INFERNAL_ASHES(ItemID.INFERNAL_ASHES, 1, 110, PrayerBonus.DEMONIC_OFFERING), - BONES(ItemID.BONES, 1, 4.5f, PrayerBonus.BONE_BONUSES), - WOLF_BONES(ItemID.WOLF_BONES, 1, 4.5f, PrayerBonus.BONE_BONUSES), - LOAR_REMAINS(ItemID.LOAR_REMAINS, 1, 33, PrayerBonus.MORYTANIA_DIARY_3_SHADES), - BURNT_BONES(ItemID.BURNT_BONES, 1, 4.5f, PrayerBonus.BONE_BONUSES), - MONKEY_BONES(ItemID.MONKEY_BONES, 1, 5, PrayerBonus.BONE_BONUSES), - BAT_BONES(ItemID.BAT_BONES, 1, 5.3f, PrayerBonus.BONE_BONUSES), - JOGRE_BONES(ItemID.JOGRE_BONES, 1, 15, PrayerBonus.BONE_BONUSES), - BIG_BONES(ItemID.BIG_BONES, 1, 15, PrayerBonus.BONE_BONUSES), - ZOGRE_BONES(ItemID.ZOGRE_BONES, 1, 22.5f, PrayerBonus.BONE_BONUSES), - SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, 1, 25, PrayerBonus.BONE_BONUSES), - BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, 1, 30, PrayerBonus.BONE_BONUSES), - PHRIN_REMAINS(ItemID.PHRIN_REMAINS, 1, 46.5f, PrayerBonus.MORYTANIA_DIARY_3_SHADES), - WYRM_BONES(ItemID.WYRM_BONES, 1, 50, PrayerBonus.BONE_BONUSES), - RIYL_REMAINS(ItemID.RIYL_REMAINS, 1, 59.5f, PrayerBonus.MORYTANIA_DIARY_3_SHADES), - WYVERN_BONES(ItemID.WYVERN_BONES, 1, 72, PrayerBonus.BONE_BONUSES), - DRAGON_BONES(ItemID.DRAGON_BONES, 1, 72, PrayerBonus.BONE_BONUSES), - DRAKE_BONES(ItemID.DRAKE_BONES, 1, 80, PrayerBonus.BONE_BONUSES), - ASYN_REMAINS(ItemID.ASYN_REMAINS, 1, 82.5f, PrayerBonus.MORYTANIA_DIARY_3_SHADES), - FAYRG_BONES(ItemID.FAYRG_BONES, 1, 84, PrayerBonus.BONE_BONUSES), - FIYR_REMAINS(ItemID.FIYR_REMAINS, 1, 84, PrayerBonus.MORYTANIA_DIARY_3_SHADES), - LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, 1, 85, PrayerBonus.BONE_BONUSES), - RAURG_BONES(ItemID.RAURG_BONES, 1, 96, PrayerBonus.BONE_BONUSES), - HYDRA_BONES(ItemID.HYDRA_BONES, 1, 110, PrayerBonus.BONE_BONUSES), - DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, 1, 125, PrayerBonus.BONE_BONUSES), - OURG_BONES(ItemID.OURG_BONES, 1, 140, PrayerBonus.BONE_BONUSES), - URIUM_REMAINS(ItemID.URIUM_REMAINS, 1, 120, PrayerBonus.MORYTANIA_DIARY_3_SHADES), - GUPPY(ItemID.GUPPY, 1, 4), - CAVEFISH(ItemID.CAVEFISH, 1, 7), - TETRA(ItemID.TETRA, 1, 10), - CATFISH(ItemID.CATFISH, 1, 16), - SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, 70, 150, PrayerBonus.BONE_BONUSES), + ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD, 1, 130, PrayerMethod.ENSOULED_HEAD), + ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD, 1, 182, PrayerMethod.ENSOULED_HEAD), + ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD, 1, 286, PrayerMethod.ENSOULED_HEAD), + ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD, 1, 364, PrayerMethod.ENSOULED_HEAD), + ENSOULED_SCORPION_HEAD(ItemID.ENSOULED_SCORPION_HEAD, 1, 454, PrayerMethod.ENSOULED_HEAD), + ENSOULED_BEAR_HEAD(ItemID.ENSOULED_BEAR_HEAD, 1, 480, PrayerMethod.ENSOULED_HEAD), + ENSOULED_UNICORN_HEAD(ItemID.ENSOULED_UNICORN_HEAD, 1, 494, PrayerMethod.ENSOULED_HEAD), + ENSOULED_DOG_HEAD(ItemID.ENSOULED_DOG_HEAD, 1, 520, PrayerMethod.ENSOULED_HEAD), + ENSOULED_CHAOS_DRUID_HEAD(ItemID.ENSOULED_CHAOS_DRUID_HEAD, 1, 584, PrayerMethod.ENSOULED_HEAD), + ENSOULED_GIANT_HEAD(ItemID.ENSOULED_GIANT_HEAD, 1, 650, PrayerMethod.ENSOULED_HEAD), + ENSOULED_OGRE_HEAD(ItemID.ENSOULED_OGRE_HEAD, 1, 716, PrayerMethod.ENSOULED_HEAD), + ENSOULED_ELF_HEAD(ItemID.ENSOULED_ELF_HEAD, 1, 754, PrayerMethod.ENSOULED_HEAD), + ENSOULED_TROLL_HEAD(ItemID.ENSOULED_TROLL_HEAD, 1, 780, PrayerMethod.ENSOULED_HEAD), + ENSOULED_HORROR_HEAD(ItemID.ENSOULED_HORROR_HEAD, 1, 832, PrayerMethod.ENSOULED_HEAD), + ENSOULED_KALPHITE_HEAD(ItemID.ENSOULED_KALPHITE_HEAD, 1, 884, PrayerMethod.ENSOULED_HEAD), + ENSOULED_DAGANNOTH_HEAD(ItemID.ENSOULED_DAGANNOTH_HEAD, 1, 936, PrayerMethod.ENSOULED_HEAD), + ENSOULED_BLOODVELD_HEAD(ItemID.ENSOULED_BLOODVELD_HEAD, 1, 1040, PrayerMethod.ENSOULED_HEAD), + ENSOULED_TZHAAR_HEAD(ItemID.ENSOULED_TZHAAR_HEAD, 1, 1104, PrayerMethod.ENSOULED_HEAD), + ENSOULED_DEMON_HEAD(ItemID.ENSOULED_DEMON_HEAD, 1, 1170, PrayerMethod.ENSOULED_HEAD), + ENSOULED_HELLHOUND_HEAD(ItemID.ENSOULED_HELLHOUND_HEAD, 1, 1200, PrayerMethod.ENSOULED_HEAD), + ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD, 1, 1234, PrayerMethod.ENSOULED_HEAD), + ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD, 1, 1300, PrayerMethod.ENSOULED_HEAD), + ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD, 1, 1560, PrayerMethod.ENSOULED_HEAD), + FIENDISH_ASHES(ItemID.FIENDISH_ASHES, 1, 10, PrayerMethod.DEMONIC_ASHES), + VILE_ASHES(ItemID.VILE_ASHES, 1, 25, PrayerMethod.DEMONIC_ASHES), + MALICIOUS_ASHES(ItemID.MALICIOUS_ASHES, 1, 65, PrayerMethod.DEMONIC_ASHES), + ABYSSAL_ASHES(ItemID.ABYSSAL_ASHES, 1, 85, PrayerMethod.DEMONIC_ASHES), + INFERNAL_ASHES(ItemID.INFERNAL_ASHES, 1, 110, PrayerMethod.DEMONIC_ASHES), + BONES(ItemID.BONES, 1, 4.5f, PrayerMethod.BONES), + WOLF_BONES(ItemID.WOLF_BONES, 1, 4.5f, PrayerMethod.BONES), + LOAR_REMAINS(ItemID.LOAR_REMAINS, 1, 33, PrayerMethod.SHADE_REMAINS), + BURNT_BONES(ItemID.BURNT_BONES, 1, 4.5f, PrayerMethod.BONES), + MONKEY_BONES(ItemID.MONKEY_BONES, 1, 5, PrayerMethod.BONES), + BAT_BONES(ItemID.BAT_BONES, 1, 5.3f, PrayerMethod.BONES), + JOGRE_BONES(ItemID.JOGRE_BONES, 1, 15, PrayerMethod.BONES), + BIG_BONES(ItemID.BIG_BONES, 1, 15, PrayerMethod.BONES), + ZOGRE_BONES(ItemID.ZOGRE_BONES, 1, 22.5f, PrayerMethod.BONES), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, 1, 25, PrayerMethod.BONES), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, 1, 30, PrayerMethod.BONES), + PHRIN_REMAINS(ItemID.PHRIN_REMAINS, 1, 46.5f, PrayerMethod.SHADE_REMAINS), + WYRM_BONES(ItemID.WYRM_BONES, 1, 50, PrayerMethod.BONES), + RIYL_REMAINS(ItemID.RIYL_REMAINS, 1, 59.5f, PrayerMethod.SHADE_REMAINS), + WYVERN_BONES(ItemID.WYVERN_BONES, 1, 72, PrayerMethod.BONES), + DRAGON_BONES(ItemID.DRAGON_BONES, 1, 72, PrayerMethod.BONES), + DRAKE_BONES(ItemID.DRAKE_BONES, 1, 80, PrayerMethod.BONES), + ASYN_REMAINS(ItemID.ASYN_REMAINS, 1, 82.5f, PrayerMethod.SHADE_REMAINS), + FAYRG_BONES(ItemID.FAYRG_BONES, 1, 84, PrayerMethod.BONES), + FIYR_REMAINS(ItemID.FIYR_REMAINS, 1, 84, PrayerMethod.SHADE_REMAINS), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, 1, 85, PrayerMethod.BONES), + RAURG_BONES(ItemID.RAURG_BONES, 1, 96, PrayerMethod.BONES), + HYDRA_BONES(ItemID.HYDRA_BONES, 1, 110, PrayerMethod.BONES), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, 1, 125, PrayerMethod.BONES), + OURG_BONES(ItemID.OURG_BONES, 1, 140, PrayerMethod.BONES), + URIUM_REMAINS(ItemID.URIUM_REMAINS, 1, 120, PrayerMethod.SHADE_REMAINS), + GUPPY(ItemID.GUPPY, 1, 4, PrayerMethod.PREPARED_FISH), + CAVEFISH(ItemID.CAVEFISH, 1, 7, PrayerMethod.PREPARED_FISH), + TETRA(ItemID.TETRA, 1, 10, PrayerMethod.PREPARED_FISH), + CATFISH(ItemID.CATFISH, 1, 16, PrayerMethod.PREPARED_FISH), + SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, 70, 150, PrayerMethod.BONES), ; + private enum PrayerMethod + { + BONES, + DEMONIC_ASHES, + ENSOULED_HEAD, + PREPARED_FISH, + SHADE_REMAINS, + } + + private static final Set EXCLUDED_BONUSES_FOR_BONES = EnumSet.of( + MORYTANIA_DIARY_3_SHADES, + DEMONIC_OFFERING + ); + private static final Set EXCLUDED_BONUSES_FOR_ASHES = EnumSet.complementOf(EnumSet.of(DEMONIC_OFFERING)); + private static final Set EXCLUDED_BONUSES_FOR_REMAINS = EnumSet.complementOf(EnumSet.of(MORYTANIA_DIARY_3_SHADES)); + private final int itemId; private final int level; private final float xp; - private final EnumSet applicableBonuses; - - PrayerAction(int itemId, int level, float xp, PrayerBonus... applicableBonuses) - { - this.itemId = itemId; - this.level = level; - this.xp = xp; - this.applicableBonuses = EnumSet.noneOf(PrayerBonus.class); - this.applicableBonuses.addAll(Arrays.asList(applicableBonuses)); - } + private final PrayerMethod methodType; @Override - public boolean isBonusApplicable(SkillBonus skillBonus) + public Set getExcludedSkillBonuses() { - return skillBonus instanceof PrayerBonus && applicableBonuses.contains(skillBonus); + switch (getMethodType()) + { + case BONES: + return EXCLUDED_BONUSES_FOR_BONES; + case DEMONIC_ASHES: + return EXCLUDED_BONUSES_FOR_ASHES; + case SHADE_REMAINS: + return EXCLUDED_BONUSES_FOR_REMAINS; + default: + return EnumSet.allOf(PrayerBonus.class); + } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java index fe19f43b8b..1004853371 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java @@ -24,6 +24,8 @@ */ package net.runelite.client.plugins.skillcalculator.skills; +import java.util.EnumSet; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; @@ -41,15 +43,35 @@ public enum PrayerBonus implements SkillBonus SACRED_BONE_BURNER("Sacred Bone Burner (300%)", 3), ; - static final PrayerBonus[] BONE_BONUSES = { - LIT_GILDED_ALTAR, - ECTOFUNTUS, - CHAOS_ALTAR, - BONECRUSHER, - SINISTER_OFFERING, - SACRED_BONE_BURNER, - }; - private final String name; private final float value; + + @Override + public Set getCanBeStackedWith() + { + final Set others = EnumSet.noneOf(PrayerBonus.class); + + switch (this) + { + case ECTOFUNTUS: + case LIT_GILDED_ALTAR: + case CHAOS_ALTAR: + case SACRED_BONE_BURNER: + case BONECRUSHER: + others.add(DEMONIC_OFFERING); + break; + case MORYTANIA_DIARY_3_SHADES: + others.add(DEMONIC_OFFERING); + others.add(SINISTER_OFFERING); + break; + case DEMONIC_OFFERING: + return EnumSet.complementOf(EnumSet.of(DEMONIC_OFFERING)); + case SINISTER_OFFERING: + others.add(MORYTANIA_DIARY_3_SHADES); + others.add(DEMONIC_OFFERING); + break; + } + + return others; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java index 2e1c1b586b..7d28c26e9c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java @@ -24,6 +24,9 @@ */ package net.runelite.client.plugins.skillcalculator.skills; +import java.util.Collections; +import java.util.EnumSet; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.ItemID; @@ -86,6 +89,8 @@ public String getName(final ItemManager itemManager) WRATH_RUNE(ItemID.WRATH_RUNE, 95, 8, false), ; + private static final Set RUNECRAFT_BONUSES = EnumSet.allOf(RunecraftBonus.class); + private final int itemId; private final int level; private final float xp; @@ -98,8 +103,13 @@ public String getName(final ItemManager itemManager) } @Override - public boolean isBonusApplicable(SkillBonus bonus) + public Set getExcludedSkillBonuses() { - return !ignoreBonus; + if (ignoreBonus) + { + return RUNECRAFT_BONUSES; + } + + return Collections.emptySet(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SkillAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SkillAction.java index ec9a7726ab..fe5dd97ca6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SkillAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SkillAction.java @@ -24,6 +24,8 @@ */ package net.runelite.client.plugins.skillcalculator.skills; +import java.util.Collections; +import java.util.Set; import net.runelite.api.ItemComposition; import net.runelite.client.game.ItemManager; @@ -92,8 +94,13 @@ default int getSprite() */ default boolean isBonusApplicable(SkillBonus bonus) { - return true; + return !getExcludedSkillBonuses().contains(bonus); } boolean isMembers(final ItemManager itemManager); + + default Set getExcludedSkillBonuses() + { + return Collections.emptySet(); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SkillBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SkillBonus.java index 2ff350b58a..69afd5e489 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SkillBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SkillBonus.java @@ -24,6 +24,9 @@ */ package net.runelite.client.plugins.skillcalculator.skills; +import java.util.Collections; +import java.util.Set; + /** * An object representing a skill bonus, such as from a skilling outfit or activity granting boosted xp. */ @@ -44,4 +47,15 @@ public interface SkillBonus * @return The skill bonus multiplier. */ float getValue(); + + + /** + * Gets the list of skill bonuses this skill bonus can be stacked with. + * + * @return List of stackable skill bonuses + */ + default Set getCanBeStackedWith() + { + return Collections.emptySet(); + } } From 3d496bbcf1fa59553d96c40b4fbe71a0ea61017c Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Tue, 9 Apr 2024 15:40:58 -0700 Subject: [PATCH 10/67] skill calculator: Add Forester's Campfire firemaking bonus Co-authored-by: Jordan Atwood --- .../skillcalculator/skills/FiremakingBonus.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingBonus.java index 4483b65309..c153a2fea0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingBonus.java @@ -24,6 +24,8 @@ */ package net.runelite.client.plugins.skillcalculator.skills; +import java.util.EnumSet; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; @@ -32,8 +34,17 @@ public enum FiremakingBonus implements SkillBonus { PYROMANCER_OUTFIT("Pyromancer Outfit (+2.5%)", 1.025f), + FORESTERS_CAMPFIRE("Forester's Campfire (33%)", 0.33333333f), ; private final String name; private final float value; + + @Override + public Set getCanBeStackedWith() + { + final EnumSet others = EnumSet.allOf(FiremakingBonus.class); + others.remove(this); + return others; + } } From 3ff578fcf8d01cabbeb5ef56b84e32c48a9af233 Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Tue, 9 Apr 2024 15:41:26 -0700 Subject: [PATCH 11/67] skill calculator: Add Zealot Robes prayer bonus Co-authored-by: Jordan Atwood --- .../plugins/skillcalculator/skills/PrayerAction.java | 5 +++++ .../plugins/skillcalculator/skills/PrayerBonus.java | 10 ++++++++++ 2 files changed, 15 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java index 5a1684ed65..30352c2e1a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java @@ -31,6 +31,7 @@ import net.runelite.api.ItemID; import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.DEMONIC_OFFERING; import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.MORYTANIA_DIARY_3_SHADES; +import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.ZEALOT_ROBES; @AllArgsConstructor @Getter @@ -112,6 +113,7 @@ private enum PrayerMethod ); private static final Set EXCLUDED_BONUSES_FOR_ASHES = EnumSet.complementOf(EnumSet.of(DEMONIC_OFFERING)); private static final Set EXCLUDED_BONUSES_FOR_REMAINS = EnumSet.complementOf(EnumSet.of(MORYTANIA_DIARY_3_SHADES)); + private static final Set EXCLUDE_ALL_EXCEPT_ZEALOT_ROBES = EnumSet.complementOf(EnumSet.of(ZEALOT_ROBES)); private final int itemId; private final int level; @@ -127,6 +129,9 @@ public Set getExcludedSkillBonuses() return EXCLUDED_BONUSES_FOR_BONES; case DEMONIC_ASHES: return EXCLUDED_BONUSES_FOR_ASHES; + case ENSOULED_HEAD: + case PREPARED_FISH: + return EXCLUDE_ALL_EXCEPT_ZEALOT_ROBES; case SHADE_REMAINS: return EXCLUDED_BONUSES_FOR_REMAINS; default: diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java index 1004853371..046f4098f0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java @@ -41,6 +41,7 @@ public enum PrayerBonus implements SkillBonus SINISTER_OFFERING("Sinister Offering (300%)", 3), DEMONIC_OFFERING("Demonic Offering (300%)", 3), SACRED_BONE_BURNER("Sacred Bone Burner (300%)", 3), + ZEALOT_ROBES("Zealot Robes (105%)", 1.05f), ; private final String name; @@ -56,6 +57,9 @@ public Set getCanBeStackedWith() case ECTOFUNTUS: case LIT_GILDED_ALTAR: case CHAOS_ALTAR: + others.add(ZEALOT_ROBES); + others.add(DEMONIC_OFFERING); + break; case SACRED_BONE_BURNER: case BONECRUSHER: others.add(DEMONIC_OFFERING); @@ -64,6 +68,12 @@ public Set getCanBeStackedWith() others.add(DEMONIC_OFFERING); others.add(SINISTER_OFFERING); break; + case ZEALOT_ROBES: + others.add(ECTOFUNTUS); + others.add(LIT_GILDED_ALTAR); + others.add(CHAOS_ALTAR); + others.add(DEMONIC_OFFERING); + break; case DEMONIC_OFFERING: return EnumSet.complementOf(EnumSet.of(DEMONIC_OFFERING)); case SINISTER_OFFERING: From 9ad485cc9bbd7fd8629690e45393042b01de17da Mon Sep 17 00:00:00 2001 From: Joshua Dunbrack Date: Wed, 10 Apr 2024 00:20:38 -0400 Subject: [PATCH 12/67] regen meter: apply updated lightbearer equipping behavior (#17678) This commit fixes the special energy regen meter when equipping or uneqipping the Lightbearer after the April 4th 2024 game update. --- .../runelite/client/plugins/regenmeter/RegenMeterPlugin.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java index c8224571a5..447efb450a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/regenmeter/RegenMeterPlugin.java @@ -130,7 +130,9 @@ public void onItemContainerChanged(ItemContainerChanged event) return; } - ticksSinceSpecRegen = 0; + // Lightbearer switch preserves time until next spec regen if <25 ticks remain + // If unequipping Lightbearer, this will always evaluate to 0 + ticksSinceSpecRegen = Math.max(0, ticksSinceSpecRegen - 25); wearingLightbearer = hasLightbearer; } From 4f0f8a1fa3e9ef07fd2231d2380bfaa1ab9b4745 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Wed, 10 Apr 2024 11:40:46 +0000 Subject: [PATCH 13/67] Update Item IDs to 2024-04-10-rev221 --- runelite-api/src/main/java/net/runelite/api/ItemID.java | 7 ++++++- .../src/main/java/net/runelite/api/NullItemID.java | 9 +++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/runelite-api/src/main/java/net/runelite/api/ItemID.java b/runelite-api/src/main/java/net/runelite/api/ItemID.java index 7caef70dc7..bb5462df6a 100644 --- a/runelite-api/src/main/java/net/runelite/api/ItemID.java +++ b/runelite-api/src/main/java/net/runelite/api/ItemID.java @@ -13920,7 +13920,7 @@ public final class ItemID public static final int BLESSED_BONE_STATUETTE_29340 = 29340; public static final int BLESSED_BONE_STATUETTE_29342 = 29342; public static final int BLESSED_BONES = 29344; - public static final int BLESSD_BAT_BONES = 29346; + public static final int BLESSED_BAT_BONES = 29346; public static final int BLESSED_BIG_BONES = 29348; public static final int BLESSED_ZOGRE_BONES = 29350; public static final int BLESSED_BABYDRAGON_BONES = 29352; @@ -13979,5 +13979,10 @@ public final class ItemID public static final int EGG_PRIEST_MITRE = 29443; public static final int EASTER_EGG_MINED = 29445; public static final int EGGNAPPIN_SACK = 29446; + public static final int IMBUED_MAGE_ARENA_CAPE = 29448; + public static final int ZOMBIE_PIRATE_KEY = 29449; + public static final int TELEPORT_ANCHORING_SCROLL = 29455; + public static final int ADAMANT_SEEDS = 29458; + public static final int WILDERNESS_AGILITY_TICKET = 29460; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/NullItemID.java b/runelite-api/src/main/java/net/runelite/api/NullItemID.java index f5e98629f5..b97374b48a 100644 --- a/runelite-api/src/main/java/net/runelite/api/NullItemID.java +++ b/runelite-api/src/main/java/net/runelite/api/NullItemID.java @@ -15258,5 +15258,14 @@ public final class NullItemID public static final int NULL_29442 = 29442; public static final int NULL_29444 = 29444; public static final int NULL_29447 = 29447; + public static final int NULL_29450 = 29450; + public static final int NULL_29451 = 29451; + public static final int NULL_29452 = 29452; + public static final int NULL_29453 = 29453; + public static final int NULL_29454 = 29454; + public static final int NULL_29456 = 29456; + public static final int NULL_29457 = 29457; + public static final int NULL_29459 = 29459; + public static final int NULL_29461 = 29461; /* This file is automatically generated. Do not edit. */ } From fb6f7dbf396cee578c67c6029f0a261968d9e9f4 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Wed, 10 Apr 2024 11:40:47 +0000 Subject: [PATCH 14/67] Update Object IDs to 2024-04-10-rev221 --- .../main/java/net/runelite/api/NullObjectID.java | 14 +++++++++----- .../src/main/java/net/runelite/api/ObjectID.java | 12 +++++++----- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java index 043d3168c4..ab4a330ee9 100644 --- a/runelite-api/src/main/java/net/runelite/api/NullObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/NullObjectID.java @@ -27564,11 +27564,6 @@ public final class NullObjectID public static final int NULL_53183 = 53183; public static final int NULL_53184 = 53184; public static final int NULL_53185 = 53185; - public static final int NULL_53188 = 53188; - public static final int NULL_53189 = 53189; - public static final int NULL_53190 = 53190; - public static final int NULL_53191 = 53191; - public static final int NULL_53192 = 53192; public static final int NULL_53193 = 53193; public static final int NULL_53194 = 53194; public static final int NULL_53195 = 53195; @@ -27589,6 +27584,15 @@ public final class NullObjectID public static final int NULL_53210 = 53210; public static final int NULL_53211 = 53211; public static final int NULL_53212 = 53212; + public static final int NULL_53213 = 53213; + public static final int NULL_53214 = 53214; + public static final int NULL_53215 = 53215; + public static final int NULL_53216 = 53216; + public static final int NULL_53217 = 53217; public static final int NULL_53218 = 53218; + public static final int NULL_53219 = 53219; + public static final int NULL_53220 = 53220; + public static final int NULL_53221 = 53221; + public static final int NULL_53223 = 53223; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/ObjectID.java b/runelite-api/src/main/java/net/runelite/api/ObjectID.java index c413ef5248..551b195c60 100644 --- a/runelite-api/src/main/java/net/runelite/api/ObjectID.java +++ b/runelite-api/src/main/java/net/runelite/api/ObjectID.java @@ -25607,10 +25607,12 @@ public final class ObjectID public static final int BUFFERS_53170 = 53170; public static final int COLUMN_53186 = 53186; public static final int DOOR_53187 = 53187; - public static final int SKELETON_53213 = 53213; - public static final int SKELETON_53214 = 53214; - public static final int SKELETON_53215 = 53215; - public static final int TREE_53216 = 53216; - public static final int TREE_STUMP_53217 = 53217; + public static final int TREE_53188 = 53188; + public static final int TREE_STUMP_53189 = 53189; + public static final int SKELETON_53190 = 53190; + public static final int SKELETON_53191 = 53191; + public static final int SKELETON_53192 = 53192; + public static final int ZOMBIE_PIRATES_LOCKER = 53222; + public static final int AGILITY_DISPENSER = 53224; /* This file is automatically generated. Do not edit. */ } From dbecfda340f4b8dae0afa1d1c7a5649c9c926c21 Mon Sep 17 00:00:00 2001 From: RuneLite Cache-Code Autoupdater Date: Wed, 10 Apr 2024 11:40:47 +0000 Subject: [PATCH 15/67] Update NPC IDs to 2024-04-10-rev221 --- .../src/main/java/net/runelite/api/NpcID.java | 25 ++++++++++++++++--- .../main/java/net/runelite/api/NullNpcID.java | 1 + 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/NpcID.java b/runelite-api/src/main/java/net/runelite/api/NpcID.java index cb6a8503ff..e53832fcca 100644 --- a/runelite-api/src/main/java/net/runelite/api/NpcID.java +++ b/runelite-api/src/main/java/net/runelite/api/NpcID.java @@ -10946,11 +10946,10 @@ public final class NpcID public static final int ORYX = 13006; public static final int FLIES_13007 = 13007; public static final int FLIES_13008 = 13008; - public static final int BLOOD_MOON = 13010; - public static final int BLOOD_MOON_13011 = 13011; + public static final int GREEN_DRAGON_13010 = 13010; + public static final int BLOOD_MOON = 13011; public static final int ECLIPSE_MOON = 13012; public static final int BLUE_MOON = 13013; - public static final int ECLIPSE_MOON_13019 = 13019; public static final int MOON_SHIELD = 13020; public static final int BLOOD_JAGUAR = 13021; public static final int FROZEN_WEAPONS = 13025; @@ -11330,5 +11329,25 @@ public final class NpcID public static final int RABBIT_13469 = 13469; public static final int RABBIT_13470 = 13470; public static final int CHICKEN_13471 = 13471; + public static final int ENRAGED_BLOOD_MOON = 13485; + public static final int ENRAGED_BLUE_MOON = 13486; + public static final int ENRAGED_ECLIPSE_MOON = 13487; + public static final int GREEN_DRAGON_13488 = 13488; + public static final int ZOMBIE_PIRATE_13489 = 13489; + public static final int ZOMBIE_PIRATE_13490 = 13490; + public static final int ZOMBIE_PIRATE_13491 = 13491; + public static final int ZOMBIE_PIRATE_13492 = 13492; + public static final int ZOMBIE_PIRATE_13493 = 13493; + public static final int ZOMBIE_PIRATE_13494 = 13494; + public static final int SKELETON_13495 = 13495; + public static final int SKELETON_13496 = 13496; + public static final int SKELETON_13497 = 13497; + public static final int SKELETON_13498 = 13498; + public static final int SKELETON_13499 = 13499; + public static final int SKELETON_13500 = 13500; + public static final int SKELETON_13501 = 13501; + public static final int HILL_GIANT_13502 = 13502; + public static final int HILL_GIANT_13503 = 13503; + public static final int HILL_GIANT_13504 = 13504; /* This file is automatically generated. Do not edit. */ } diff --git a/runelite-api/src/main/java/net/runelite/api/NullNpcID.java b/runelite-api/src/main/java/net/runelite/api/NullNpcID.java index 8e75e987b1..2f623a39a0 100644 --- a/runelite-api/src/main/java/net/runelite/api/NullNpcID.java +++ b/runelite-api/src/main/java/net/runelite/api/NullNpcID.java @@ -2050,6 +2050,7 @@ public final class NullNpcID public static final int NULL_13016 = 13016; public static final int NULL_13017 = 13017; public static final int NULL_13018 = 13018; + public static final int NULL_13019 = 13019; public static final int NULL_13022 = 13022; public static final int NULL_13023 = 13023; public static final int NULL_13024 = 13024; From 857b193179385d740806193207a7cf6f2a6c1d42 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 10 Apr 2024 13:25:34 -0400 Subject: [PATCH 16/67] overlay panel: clear children even if render throws Bad plugins will add children components and then throw exceptions, leading to memory exhaustion --- .../runelite/client/ui/overlay/OverlayPanel.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPanel.java b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPanel.java index 41afe1149c..53545fe313 100644 --- a/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/ui/overlay/OverlayPanel.java @@ -96,11 +96,17 @@ else if (getPreferredSize().width <= ComponentConstants.STANDARD_WIDTH * 0.8) panelComponent.setBackgroundColor(getPreferredColor()); } - final Dimension dimension = panelComponent.render(graphics); - - if (clearChildren) + final Dimension dimension; + try + { + dimension = panelComponent.render(graphics); + } + finally { - panelComponent.getChildren().clear(); + if (clearChildren) + { + panelComponent.getChildren().clear(); + } } panelComponent.setPreferredSize(oldSize); From 9a4c44b06e7b42641a36c6a07d69a1793e879db5 Mon Sep 17 00:00:00 2001 From: Felanbird <41973452+Felanbird@users.noreply.github.com> Date: Wed, 10 Apr 2024 08:26:25 -0400 Subject: [PATCH 17/67] clues: update south-eastern chaos temple hard clue --- .../runelite/client/plugins/cluescrolls/clues/EmoteClue.java | 2 +- .../client/plugins/cluescrolls/clues/emote/STASHUnit.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java index f433edc615..41439b4214 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/EmoteClue.java @@ -202,7 +202,7 @@ public class EmoteClue extends ClueScroll implements LocationClueScroll new EmoteClue("Salute outside the gates of Cam Torum. Beware of double agents! Equip a full set of blue moon equipment.", "Cam Torum", CAM_TORUM_ENTRANCE, new WorldPoint(1436, 3115, 0), DOUBLE_AGENT_141, SALUTE, any("Blue moon helm", item(BLUE_MOON_HELM), item(BLUE_MOON_HELM_29041)), any("Blue moon chestplate", item(BLUE_MOON_CHESTPLATE), item(BLUE_MOON_CHESTPLATE_29037)), any("Blue moon tassets", item(BLUE_MOON_TASSETS), item(BLUE_MOON_TASSETS_29039)), item(BLUE_MOON_SPEAR)), new EmoteClue("Shrug in the mine near Rimmington. Equip a gold necklace, a gold ring and a bronze spear.", "Rimmington mine", RIMMINGTON_MINE, new WorldPoint(2976, 3238, 0), SHRUG, item(GOLD_NECKLACE), item(GOLD_RING), item(BRONZE_SPEAR)), new EmoteClue("Shrug in Catherby bank. Yawn before you talk to me. Equip a maple longbow, green d'hide chaps and an iron med helm.", "Catherby", OUTSIDE_CATHERBY_BANK, new WorldPoint(2808, 3440, 0), SHRUG, YAWN, item(MAPLE_LONGBOW), item(GREEN_DHIDE_CHAPS), item(IRON_MED_HELM)), - new EmoteClue("Shrug in the Zamorak temple found in the Eastern Wilderness. Beware of double agents! Equip rune platelegs, an iron platebody and blue dragonhide vambraces.", "Chaos Temple (east of Ferox Enclave)", CHAOS_TEMPLE_IN_THE_SOUTHEASTERN_WILDERNESS, new WorldPoint(3239, 3611, 0), DOUBLE_AGENT_65, SHRUG, item(RUNE_PLATELEGS), item(IRON_PLATEBODY), item(BLUE_DHIDE_VAMBRACES)), + new EmoteClue("Shrug in the woods east of the Level 19 Wilderness Obelisk. Beware of double agents! Equip rune platelegs, an iron platebody and blue dragonhide vambraces.", "East of the Level 19 Wilderness Obelisk", EAST_OF_THE_LEVEL_19_WILDERNESS_OBELISK, new WorldPoint(3241, 3672, 0), DOUBLE_AGENT_65, SHRUG, item(RUNE_PLATELEGS), item(IRON_PLATEBODY), item(BLUE_DHIDE_VAMBRACES)), new EmoteClue("Shrug in the Shayzien war tent. Equip a blue mystic robe bottom, a rune kiteshield and any bob shirt.", "Shayzien war tent", SHAYZIEN_WAR_TENT, new WorldPoint(1487, 3635, 0), SHRUG, item(MYSTIC_ROBE_BOTTOM), item(RUNE_KITESHIELD), range("Any bob shirt", BOBS_RED_SHIRT, BOBS_PURPLE_SHIRT)), new EmoteClue("Slap your head in the centre of the Kourend catacombs. Beware of double agents! Equip the arclight and the amulet of the damned.", "Kourend catacombs", CENTRE_OF_THE_CATACOMBS_OF_KOUREND, new WorldPoint(1663, 10045, 0), DOUBLE_AGENT_141, SLAP_HEAD, item(ARCLIGHT), any("Amulet of the damned", item(AMULET_OF_THE_DAMNED), item(AMULET_OF_THE_DAMNED_FULL))), new EmoteClue("Spin at the crossroads north of Rimmington. Equip a green gnome hat, cream gnome top and leather chaps.", "Rimmington", ROAD_JUNCTION_NORTH_OF_RIMMINGTON, new WorldPoint(2981, 3276, 0), SPIN, item(GREEN_HAT), item(CREAM_ROBE_TOP), item(LEATHER_CHAPS)), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/emote/STASHUnit.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/emote/STASHUnit.java index e3481e2406..7eba0112b3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/emote/STASHUnit.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/emote/STASHUnit.java @@ -82,7 +82,7 @@ public enum STASHUnit WEST_OF_THE_SHAYZIEN_COMBAT_RING(NullObjectID.NULL_29006, new WorldPoint(1541, 3631, 0)), ENTRANCE_OF_THE_ARCEUUS_LIBRARY(NullObjectID.NULL_29007, new WorldPoint(1642, 3809, 0)), OUTSIDE_DRAYNOR_VILLAGE_JAIL(NullObjectID.NULL_29008, new WorldPoint(3130, 3250, 0)), - CHAOS_TEMPLE_IN_THE_SOUTHEASTERN_WILDERNESS(NullObjectID.NULL_29009, new WorldPoint(3245, 3609, 0)), + EAST_OF_THE_LEVEL_19_WILDERNESS_OBELISK(NullObjectID.NULL_29009, new WorldPoint(3243, 3662, 0)), FISHING_GUILD_BANK(NullObjectID.NULL_29010, new WorldPoint(2593, 3409, 0)), TOP_FLOOR_OF_THE_LIGHTHOUSE(NullObjectID.NULL_29011, new WorldPoint(2512, 3640, 2)), OUTSIDE_THE_GREAT_PYRAMID_OF_SOPHANEM(NullObjectID.NULL_29012, new WorldPoint(3291, 2780, 0)), From 8403ff4ddacad562723d447b0712ee5ee42b24eb Mon Sep 17 00:00:00 2001 From: SRLJustin Date: Wed, 10 Apr 2024 17:57:18 +1000 Subject: [PATCH 18/67] menuentryswapper: add fortis colosseum to jewellerybox swap --- .../client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java | 1 + 1 file changed, 1 insertion(+) 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 6e45d2b405..7ea832c367 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 @@ -266,6 +266,7 @@ void setupSwaps() swap("teleport menu", "pvp arena", config::swapJewelleryBox); swap("teleport menu", "castle wars", config::swapJewelleryBox); swap("teleport menu", "ferox enclave", config::swapJewelleryBox); + swap("teleport menu", "fortis colosseum", config::swapJewelleryBox); swap("teleport menu", "burthorpe", config::swapJewelleryBox); swap("teleport menu", "barbarian outpost", config::swapJewelleryBox); swap("teleport menu", "corporeal beast", config::swapJewelleryBox); From f457f1f91314bf78508bb81a82357c99974ea327 Mon Sep 17 00:00:00 2001 From: Macweese <50101641+Macweese@users.noreply.github.com> Date: Thu, 4 Apr 2024 15:24:43 +0200 Subject: [PATCH 19/67] minimap: restore map dots on config reset --- .../java/net/runelite/client/plugins/minimap/MinimapPlugin.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java index d86f9eaac2..5a2d6d9527 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/minimap/MinimapPlugin.java @@ -122,6 +122,7 @@ else if (event.getKey().equals("zoom")) return; } + restoreOriginalDots(); replaceMapDots(); } From 6434676baa44a62faf7604c92734a0e20af2541b Mon Sep 17 00:00:00 2001 From: Chris Brown Date: Wed, 10 Apr 2024 20:30:02 -0500 Subject: [PATCH 20/67] cache: fix label positions in underground areas --- .../net/runelite/cache/MapImageDumper.java | 8 ++--- .../net/runelite/cache/WorldMapManager.java | 8 ++--- .../WorldMapCompositeDefinition.java | 32 +++++++++++++++++++ .../WorldMapElementDefinition.java | 11 +++++++ .../loaders/WorldMapCompositeLoader.java | 11 +++++++ 5 files changed, 62 insertions(+), 8 deletions(-) diff --git a/cache/src/main/java/net/runelite/cache/MapImageDumper.java b/cache/src/main/java/net/runelite/cache/MapImageDumper.java index 302bd1721e..e2d6ad3af3 100644 --- a/cache/src/main/java/net/runelite/cache/MapImageDumper.java +++ b/cache/src/main/java/net/runelite/cache/MapImageDumper.java @@ -944,7 +944,8 @@ private void drawMapLabels(BufferedImage image, int z) for (WorldMapElementDefinition element : elements) { AreaDefinition area = areas.getArea(element.getAreaDefinitionId()); - if (area == null || area.getName() == null || element.getPosition().getZ() != z) + Position worldPosition = element.getWorldPosition(); + if (area == null || area.getName() == null || worldPosition.getZ() != z) { continue; } @@ -965,9 +966,8 @@ private void drawMapLabels(BufferedImage image, int z) SpriteDefinition sprite = sprites.findSpriteByArchiveName(fontSize.getName(), c); if (sprite.getWidth() != 0 && sprite.getHeight() != 0) { - Position position = element.getPosition(); - int drawX = position.getX() - regionLoader.getLowestX().getBaseX(); - int drawY = regionLoader.getHighestY().getBaseY() - position.getY() + Region.Y - 2; + int drawX = worldPosition.getX() - regionLoader.getLowestX().getBaseX(); + int drawY = regionLoader.getHighestY().getBaseY() - worldPosition.getY() + Region.Y - 2; blitGlyph(image, (drawX * MAP_SCALE) + advance - (stringWidth / 2), (drawY * MAP_SCALE) + ascent - (font.getAscent() / 2), diff --git a/cache/src/main/java/net/runelite/cache/WorldMapManager.java b/cache/src/main/java/net/runelite/cache/WorldMapManager.java index 1d5b2b1aba..ce8be514eb 100644 --- a/cache/src/main/java/net/runelite/cache/WorldMapManager.java +++ b/cache/src/main/java/net/runelite/cache/WorldMapManager.java @@ -41,6 +41,7 @@ public class WorldMapManager { private final Store store; private final List worldMapCompositeDefinitions = new ArrayList<>(); + private final List elements = new ArrayList<>(); public WorldMapManager(Store store) { @@ -60,16 +61,15 @@ public void load() throws IOException WorldMapCompositeDefinition composite = worldMapCompositeLoader.load(compositeFile.getContents()); worldMapCompositeDefinitions.add(composite); } - } - public List getElements() - { - List elements = new ArrayList<>(); for (WorldMapCompositeDefinition compositeDefinition : worldMapCompositeDefinitions) { elements.addAll(compositeDefinition.getWorldMapElementDefinitions()); } + } + public List getElements() + { return elements; } } diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java index 70145743c4..9f508db32a 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapCompositeDefinition.java @@ -25,6 +25,7 @@ package net.runelite.cache.definitions; import lombok.Data; +import net.runelite.cache.region.Position; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -36,4 +37,35 @@ public class WorldMapCompositeDefinition public final Set mapSquareDefinitions = new HashSet<>(); public final Set zoneDefinitions = new HashSet<>(); public final List worldMapElementDefinitions = new ArrayList<>(); + + public Position calculateWorldOffset(Position position) + { + int squareX = position.getX() / 64; + int squareZ = position.getY() / 64; + int zoneX = (position.getX() & 63) / 8; + int zoneZ = (position.getY() & 63) / 8; + Position offset = null; + + for (MapSquareDefinition mapSquare : mapSquareDefinitions) + { + if (squareX == mapSquare.sourceSquareX && squareZ == mapSquare.sourceSquareZ) + { + int shiftX = ((mapSquare.displaySquareX - mapSquare.sourceSquareX) * 64); + int shiftZ = ((mapSquare.displaySquareZ - mapSquare.sourceSquareZ) * 64); + offset = new Position(shiftX, shiftZ, mapSquare.getMinLevel()); + } + } + + for (ZoneDefinition zone : zoneDefinitions) + { + if (squareX == zone.sourceSquareX && squareZ == zone.sourceSquareZ && zoneX == zone.sourceZoneX && zoneZ == zone.sourceZoneZ) + { + int shiftX = ((zone.displaySquareX - zone.sourceSquareX) * 64) + ((zone.displayZoneX - zone.sourceZoneX) * 8); + int shiftZ = ((zone.displaySquareZ - zone.sourceSquareZ) * 64) + ((zone.displayZoneY - zone.sourceZoneZ) * 8); + offset = new Position(shiftX, shiftZ, zone.getMinLevel()); + } + } + + return offset; + } } diff --git a/cache/src/main/java/net/runelite/cache/definitions/WorldMapElementDefinition.java b/cache/src/main/java/net/runelite/cache/definitions/WorldMapElementDefinition.java index 7c17b908df..569ad819b4 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/WorldMapElementDefinition.java +++ b/cache/src/main/java/net/runelite/cache/definitions/WorldMapElementDefinition.java @@ -31,6 +31,17 @@ public class WorldMapElementDefinition { public Position position; + public Position offset; public int areaDefinitionId; public boolean membersOnly; + + public Position getWorldPosition() + { + if (offset == null) + { + return new Position(position.getX(), position.getY(), 0); + } + + return new Position(position.getX() + offset.getX(), position.getY() + offset.getY(), offset.getZ()); + } } diff --git a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapCompositeLoader.java b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapCompositeLoader.java index 488d4f868d..eb503322eb 100644 --- a/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapCompositeLoader.java +++ b/cache/src/main/java/net/runelite/cache/definitions/loaders/WorldMapCompositeLoader.java @@ -25,7 +25,9 @@ package net.runelite.cache.definitions.loaders; import net.runelite.cache.definitions.WorldMapCompositeDefinition; +import net.runelite.cache.definitions.WorldMapElementDefinition; import net.runelite.cache.io.InputStream; +import net.runelite.cache.region.Position; public class WorldMapCompositeLoader { @@ -55,6 +57,15 @@ public WorldMapCompositeDefinition load(byte[] buffer) worldMapCompositeDefinition.worldMapElementDefinitions.add(worldMapElementLoader.load(in)); } + // The graphics in the world map are patched together from parts of the map and the position of + // the map elements are based on the result of that. This calculates the offset needed to place the element back + // into world coordinates. + for (WorldMapElementDefinition element : worldMapCompositeDefinition.worldMapElementDefinitions) + { + Position position = element.getPosition(); + element.setOffset(worldMapCompositeDefinition.calculateWorldOffset(position)); + } + return worldMapCompositeDefinition; } } From 4cea37d476ed6b02b43e3453b7d301f4c3fbd08b Mon Sep 17 00:00:00 2001 From: Chris Brown Date: Wed, 10 Apr 2024 20:30:20 -0500 Subject: [PATCH 21/67] cache: fix icons rendering on the wrong plane --- .../main/java/net/runelite/cache/MapImageDumper.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/cache/src/main/java/net/runelite/cache/MapImageDumper.java b/cache/src/main/java/net/runelite/cache/MapImageDumper.java index e2d6ad3af3..934bd451c5 100644 --- a/cache/src/main/java/net/runelite/cache/MapImageDumper.java +++ b/cache/src/main/java/net/runelite/cache/MapImageDumper.java @@ -1096,13 +1096,9 @@ private void drawMapIcons(BufferedImage img, Region region, int z, int drawBaseX { int localX = location.getPosition().getX() - region.getBaseX(); int localY = location.getPosition().getY() - region.getBaseY(); - boolean isBridge = (region.getTileSetting(1, localX, localY) & 2) != 0; - int tileZ = z + (isBridge ? 1 : 0); - int localZ = location.getPosition().getZ(); - if (z != 0 && localZ != tileZ) + if (z != location.getPosition().getZ()) { - // draw all icons on z=0 continue; } @@ -1122,8 +1118,8 @@ private void drawMapIcons(BufferedImage img, Region region, int z, int drawBaseX assert sprite != null; blitIcon(img, - 2 + (drawX * MAP_SCALE) - (sprite.getMaxWidth() / 2), - 2 + (drawY * MAP_SCALE) - (sprite.getMaxHeight() / 2), + (drawX * MAP_SCALE) - (sprite.getMaxWidth() / 2), + (drawY * MAP_SCALE) - (sprite.getMaxHeight() / 2), sprite); } } From d8f430af3a59f9a931677a99a3b9f88f144d1442 Mon Sep 17 00:00:00 2001 From: Chris Brown Date: Wed, 10 Apr 2024 20:33:57 -0500 Subject: [PATCH 22/67] cache: add map links --- .../net/runelite/cache/MapImageDumper.java | 25 ++++++++++ .../net/runelite/cache/WorldMapManager.java | 49 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/cache/src/main/java/net/runelite/cache/MapImageDumper.java b/cache/src/main/java/net/runelite/cache/MapImageDumper.java index 934bd451c5..eea52a5495 100644 --- a/cache/src/main/java/net/runelite/cache/MapImageDumper.java +++ b/cache/src/main/java/net/runelite/cache/MapImageDumper.java @@ -1123,6 +1123,31 @@ private void drawMapIcons(BufferedImage img, Region region, int z, int drawBaseX sprite); } } + + // Draw the intermap link icons which are not stored with the map locations + List elements = worldMapManager.getElements(); + for (WorldMapElementDefinition element : elements) + { + AreaDefinition area = areas.getArea(element.getAreaDefinitionId()); + Position worldPosition = element.getWorldPosition(); + int regionX = worldPosition.getX() / Region.X; + int regionY = worldPosition.getY() / Region.Y; + + if (area == null || area.getName() != null || worldPosition.getZ() != z || regionX != region.getRegionX() || regionY != region.getRegionY()) + { + continue; + } + + int localX = worldPosition.getX() - region.getBaseX(); + int localY = worldPosition.getY() - region.getBaseY(); + int drawX = drawBaseX + localX; + int drawY = drawBaseY + (Region.Y - 1 - localY); + SpriteDefinition sprite = sprites.findSprite(area.spriteId, 0); + blitIcon(img, + (drawX * MAP_SCALE) - (sprite.getMaxWidth() / 2), + (drawY * MAP_SCALE) - (sprite.getMaxHeight() / 2), + sprite); + } } private void loadRegions() throws IOException diff --git a/cache/src/main/java/net/runelite/cache/WorldMapManager.java b/cache/src/main/java/net/runelite/cache/WorldMapManager.java index ce8be514eb..d40d0cfd0d 100644 --- a/cache/src/main/java/net/runelite/cache/WorldMapManager.java +++ b/cache/src/main/java/net/runelite/cache/WorldMapManager.java @@ -24,8 +24,10 @@ */ package net.runelite.cache; +import net.runelite.cache.definitions.ScriptDefinition; import net.runelite.cache.definitions.WorldMapCompositeDefinition; import net.runelite.cache.definitions.WorldMapElementDefinition; +import net.runelite.cache.definitions.loaders.ScriptLoader; import net.runelite.cache.definitions.loaders.WorldMapCompositeLoader; import net.runelite.cache.fs.Archive; import net.runelite.cache.fs.ArchiveFiles; @@ -33,15 +35,21 @@ import net.runelite.cache.fs.Storage; import net.runelite.cache.fs.Store; import net.runelite.cache.fs.Index; +import net.runelite.cache.region.Position; import java.io.IOException; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; public class WorldMapManager { private final Store store; private final List worldMapCompositeDefinitions = new ArrayList<>(); private final List elements = new ArrayList<>(); + private final Map intermapLinks = new HashMap<>(); public WorldMapManager(Store store) { @@ -66,10 +74,51 @@ public void load() throws IOException { elements.addAll(compositeDefinition.getWorldMapElementDefinitions()); } + + // Intermap links. These are stored in a client script, so we have to parse it from there. + final int intermapLinkScriptId = 1705; + Index scriptIndex = store.getIndex(IndexType.CLIENTSCRIPT); + Archive intermapLinkScript = scriptIndex.getArchive(intermapLinkScriptId); + byte[] data = storage.loadArchive(intermapLinkScript); + FSFile file = intermapLinkScript.getFiles(data).findFile(0); + + ScriptLoader scriptLoader = new ScriptLoader(); + ScriptDefinition scriptDefinition = scriptLoader.load(intermapLinkScriptId, file.getContents()); + + List linkEnds = new ArrayList<>(); + for (int i = 1; i < scriptDefinition.getIntOperands().length; ++i) + { + // 1706 is the script that gets called to jump to a location and the position is the int just before it + if (scriptDefinition.getIntOperands()[i] == 1706) + { + linkEnds.add(Position.fromPacked(scriptDefinition.getIntOperands()[i - 1])); + } + } + + // The starting positions are in switch cases. We need to sort the cases by their jump positions + // so that they correspond to the end position in the int operands. + Map linkSwitch = scriptDefinition.getSwitches()[0]; + int[] linkStarts = Arrays.stream(linkSwitch.keySet().toArray()) + .sorted(Comparator.comparingInt(linkSwitch::get)) + .mapToInt(i -> (int)i) + .toArray(); + assert (linkStarts.length - 1) == linkEnds.size(); // The last case in the switch statement is the default case + + for (int i = 0; i < linkStarts.length; ++i) + { + Position linkStartPosition = Position.fromPacked(linkStarts[i]); + Position linkEndPosition = linkEnds.get(i); + intermapLinks.put(linkStartPosition, linkEndPosition); + } } public List getElements() { return elements; } + + public Map getIntermapLinks() + { + return intermapLinks; + } } From e184e85074e328b5a5dcbee5994007b71323826c Mon Sep 17 00:00:00 2001 From: capslock13 Date: Thu, 11 Apr 2024 11:04:18 -0400 Subject: [PATCH 23/67] spec counter: add Tonalztics of Ralos --- .../specialcounter/SpecialCounterPlugin.java | 25 ++++++++++++++++--- .../plugins/specialcounter/SpecialWeapon.java | 7 +++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java index f0acc918c9..47cf88a1d5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java @@ -70,6 +70,7 @@ import net.runelite.client.party.WSClient; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; +import static net.runelite.client.plugins.specialcounter.SpecialWeapon.TONALZTICS_OF_RALOS; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.ImageUtil; @@ -99,7 +100,9 @@ public class SpecialCounterPlugin extends Plugin private int hitsplatTick; // most recent hitsplat and the target it was on private Hitsplat lastSpecHitsplat; + private Hitsplat secondToLastSpecHitsplat; private NPC lastSpecTarget; + private int specialAttackHits = 0; private final Set interactedNpcIndexes = new HashSet<>(); private final SpecialCounter[] specialCounter = new SpecialCounter[SpecialWeapon.values().length]; @@ -163,6 +166,7 @@ protected void shutDown() specialWeapon = null; lastSpecTarget = null; lastSpecHitsplat = null; + secondToLastSpecHitsplat = null; removeCounters(); overlayManager.remove(playerInfoDropOverlay); wsClient.unregisterMessage(SpecialCounterUpdate.class); @@ -185,12 +189,25 @@ public void onGameTick(GameTick event) { if (lastSpecHitsplat.getAmount() > 0) { - specialAttackHit(specialWeapon, lastSpecHitsplat, lastSpecTarget); + specialAttackHits++; + } + if (specialWeapon == TONALZTICS_OF_RALOS && secondToLastSpecHitsplat != null + && secondToLastSpecHitsplat.getAmount() > 0) + { + specialAttackHits++; + } + + if (specialAttackHits > 0) + { + int hit = specialWeapon == TONALZTICS_OF_RALOS ? specialAttackHits : getHit(specialWeapon, lastSpecHitsplat); + specialAttackHit(specialWeapon, hit, lastSpecTarget); } specialWeapon = null; lastSpecHitsplat = null; + secondToLastSpecHitsplat = null; lastSpecTarget = null; + specialAttackHits = 0; } } @@ -286,16 +303,16 @@ public void onHitsplatApplied(HitsplatApplied hitsplatApplied) // venge or thralls. if (hitsplatTick == client.getTickCount()) { + secondToLastSpecHitsplat = lastSpecHitsplat; lastSpecHitsplat = hitsplat; } } - private void specialAttackHit(SpecialWeapon specialWeapon, Hitsplat hitsplat, NPC target) + private void specialAttackHit(SpecialWeapon specialWeapon, int hit, NPC target) { - int hit = getHit(specialWeapon, hitsplat); int localPlayerId = client.getLocalPlayer().getId(); - log.debug("Special attack hit {} hitsplat {}", specialWeapon, hitsplat.getAmount()); + log.debug("Special attack hit {} hitsplat {}", specialWeapon, hit); if (config.infobox()) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java index 5806b909db..2628358bdb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialWeapon.java @@ -54,7 +54,12 @@ public enum SpecialWeapon (distance) -> 46 + distance * 10, (c) -> 0 ), - ; + TONALZTICS_OF_RALOS( + "Tonalztics of Ralos", + new int[]{ItemID.TONALZTICS_OF_RALOS}, + false, + (distance) -> 50, //The hitsplat is always applied 2t after spec regardless of distance + (c) -> 0); private final String name; private final int[] itemID; From 3628c48942d1bdcb4062965d07cf03267af7365e Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Wed, 10 Apr 2024 21:14:34 -0700 Subject: [PATCH 24/67] timers: Remove tzhaar timer defeat message trigger It is not clear what purpose this trigger serves given that there is also a GameStateChanged trigger which accomplishes the same thing. Because the chat message and game state fire when dying in the fight caves or inferno, the resulting `config.startTime(null)` call after the timer is removed leads to an ElapsedTimer being created with a null start time, which causes an NPE during infobox rendering. Because this trigger is redundant, it can safely be removed, which also prevents the aforementioned null start time ElapsedTimer from being created. --- .../runelite/client/plugins/timers/TimersPlugin.java | 10 ---------- .../client/plugins/timers/TimersPluginTest.java | 6 ++++++ 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java index 8c42216dd8..09cbc7cde0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/TimersPlugin.java @@ -130,7 +130,6 @@ public class TimersPlugin extends Plugin static final int FIGHT_CAVES_REGION_ID = 9551; static final int INFERNO_REGION_ID = 9043; private static final Pattern TZHAAR_WAVE_MESSAGE = Pattern.compile("Wave: (\\d+)"); - private static final String TZHAAR_DEFEATED_MESSAGE = "You have been defeated!"; private static final Pattern TZHAAR_PAUSED_MESSAGE = Pattern.compile("The (?:Inferno|Fight Cave) has been paused. You may now log out."); private TimerTimer freezeTimer; @@ -895,15 +894,6 @@ else if (client.getVarbitValue(Varbits.COMBAT_ACHIEVEMENT_TIER_MASTER) == 2) } } - if (message.equals(TZHAAR_DEFEATED_MESSAGE)) - { - log.debug("Stopping tzhaar timer"); - removeTzhaarTimer(); - config.tzhaarStartTime(null); - config.tzhaarLastTime(null); - return; - } - if (TZHAAR_PAUSED_MESSAGE.matcher(message).find()) { log.debug("Pausing tzhaar timer"); diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java index 0ae89c83c1..5499078c2a 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/timers/TimersPluginTest.java @@ -34,9 +34,11 @@ import java.util.function.Predicate; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; +import net.runelite.api.GameState; import net.runelite.api.Skill; import net.runelite.api.Varbits; import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.VarbitChanged; import net.runelite.client.events.ConfigChanged; import net.runelite.client.game.ItemManager; @@ -301,7 +303,11 @@ class InstantRef // test timer remove: verify the infobox was removed (and no more were added) chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "You have been defeated!", "", 0); + final GameStateChanged gameStateChanged = new GameStateChanged(); + gameStateChanged.setGameState(GameState.LOADING); + when(client.getMapRegions()).thenReturn(new int[0]); timersPlugin.onChatMessage(chatMessage); + timersPlugin.onGameStateChanged(gameStateChanged); verify(infoBoxManager, times(3)).removeInfoBox(captor.capture()); verify(infoBoxManager, times(3)).addInfoBox(captor.capture()); } From 4e3f4fc2d85979df6a0a9506e3c4fafbc8936d38 Mon Sep 17 00:00:00 2001 From: capslock13 Date: Tue, 9 Apr 2024 19:14:22 -0400 Subject: [PATCH 25/67] timers: Fix ElapsedTimer NPE with null start time --- .../net/runelite/client/plugins/timers/ElapsedTimer.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java index 55a06025d4..b9571ae11e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timers/ElapsedTimer.java @@ -68,6 +68,11 @@ public Color getTextColor() @Override public String getTooltip() { + if (startTime == null) + { + return null; + } + Duration time = Duration.between(startTime, lastTime == null ? Instant.now() : lastTime); return "Elapsed time: " + DurationFormatUtils.formatDuration(time.toMillis(), "HH:mm:ss", true); } From 3d2fb794429335606006ac0b5eb67593122c08c3 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Thu, 11 Apr 2024 17:34:03 -0600 Subject: [PATCH 26/67] rl-client: show reveal button on swing password fields --- .../java/net/runelite/client/plugins/config/ConfigPanel.java | 1 - .../net/runelite/client/ui/laf/RuneLiteLAF.properties | 3 +++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java index 88e683a089..ee70c45385 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/ConfigPanel.java @@ -483,7 +483,6 @@ private JTextComponent createTextField(ConfigDescriptor cd, ConfigItemDescriptor if (cid.getItem().secret()) { textField = new JPasswordField(); - textField.setFont(FontManager.getDefaultFont()); } else { diff --git a/runelite-client/src/main/resources/net/runelite/client/ui/laf/RuneLiteLAF.properties b/runelite-client/src/main/resources/net/runelite/client/ui/laf/RuneLiteLAF.properties index 1b1f9afc98..3924f9c117 100644 --- a/runelite-client/src/main/resources/net/runelite/client/ui/laf/RuneLiteLAF.properties +++ b/runelite-client/src/main/resources/net/runelite/client/ui/laf/RuneLiteLAF.properties @@ -83,6 +83,9 @@ MenuBar.border=0,0,0,0 MenuItem.iconTextGap=0 MenuItem.minimumIconSize=0,0 +PasswordField.showRevealButton=true +PasswordField.font=dialog 12 + RadioButtonUI=net.runelite.client.ui.laf.RuneLiteRadioButtonUI RootPaneUI=net.runelite.client.ui.laf.RuneLiteRootPaneUI From 7b768e6acf3fe8fb4985eaf1b5024f97c728d2e8 Mon Sep 17 00:00:00 2001 From: Antony Linden Date: Thu, 11 Apr 2024 21:52:07 -0700 Subject: [PATCH 27/67] skill calculator: Order actions by level, xp, then alphabetically Co-authored-by: DapperMickie --- .../skillcalculator/skills/AgilityAction.java | 4 +- .../skills/ConstructionAction.java | 100 +++++++++--------- .../skillcalculator/skills/CookingAction.java | 56 +++++----- .../skills/CraftingAction.java | 66 ++++++------ .../skillcalculator/skills/FarmingAction.java | 24 ++--- .../skills/FiremakingAction.java | 2 +- .../skillcalculator/skills/FishingAction.java | 10 +- .../skills/FletchingAction.java | 42 ++++---- .../skills/HerbloreAction.java | 4 +- .../skillcalculator/skills/HunterAction.java | 6 +- .../skillcalculator/skills/MagicAction.java | 94 ++++++++-------- .../skillcalculator/skills/MiningAction.java | 14 +-- .../skillcalculator/skills/PrayerAction.java | 70 ++++++------ .../skills/RunecraftAction.java | 10 +- .../skills/SmithingAction.java | 82 +++++++------- .../skills/ThievingAction.java | 6 +- .../skills/WoodcuttingAction.java | 2 +- .../skillcalculator/CalculatorTypeTest.java | 31 +++++- 18 files changed, 322 insertions(+), 301 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java index f4b32eaf6d..8beb9ec679 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java @@ -38,14 +38,14 @@ public enum AgilityAction implements NamedSkillAction DRAYNOR_VILLAGE_ROOFTOP("Draynor Village Rooftop", 10, 120, ItemID.MARK_OF_GRACE), LEAPING_TROUT("Leaping trout", 15, 5, ItemID.LEAPING_TROUT), AL_KHARID_ROOFTOP("Al Kharid Rooftop", 20, 180, ItemID.MARK_OF_GRACE), + LEAPING_SALMON("Leaping salmon", 30, 6, ItemID.LEAPING_SALMON), VARROCK_ROOFTOP("Varrock Rooftop", 30, 238, ItemID.MARK_OF_GRACE), PENGUIN_AGILITY_COURSE("Penguin Agility Course", 30, 540, ItemID.CLOCKWORK_SUIT), - LEAPING_SALMON("Leaping salmon", 30, 6, ItemID.LEAPING_SALMON), BARBARIAN_OUTPOST("Barbarian Outpost", 35, 152.5f, ItemID.STEEL_BATTLEAXE), CANIFIS_ROOFTOP("Canifis Rooftop", 40, 240, ItemID.MARK_OF_GRACE), LEAPING_STURGEON("Leaping sturgeon", 45, 7, ItemID.LEAPING_STURGEON), - APE_ATOLL_COURSE("Ape Atoll", 48, 580, ItemID.GORILLA_GREEGREE), SHAYZIEN_ADVANCED_COURSE("Shayzien Advanced Course", 48, 474.3f, ItemID.SHAYZIEN_HELM_5), + APE_ATOLL_COURSE("Ape Atoll", 48, 580, ItemID.GORILLA_GREEGREE), FALADOR_ROOFTOP("Falador Rooftop", 50, 440, ItemID.MARK_OF_GRACE), WILDERNESS_AGILITY_COURSE("Wilderness Agility Course", 52, 571, ItemID.SKULL), HALLOWED_SEPULCHRE_FLOOR_1("Hallowed Sepulchre Floor 1", 52, 575, ItemID.RING_OF_ENDURANCE), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java index 9e0665e2b8..88d0d63b62 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java @@ -33,71 +33,71 @@ @Getter public enum ConstructionAction implements NamedSkillAction { - EXIT_PORTAL("Exit Portal", 1, 100, ItemID.EXIT_PORTAL), PLANK("Plank", 1, 29, ItemID.PLANK), - OAK_PLANK("Oak Plank", 1, 60, ItemID.OAK_PLANK), - TEAK_PLANK("Teak Plank", 1, 90, ItemID.TEAK_PLANK), - MAHOGANY_PLANK("Mahogany Plank", 1, 140, ItemID.MAHOGANY_PLANK), - PLANT("Plant", 1, 31, ItemID.PLANT), + DOCK_LEAF("Dock Leaf", 1, 31, ItemID.DOCK_LEAF), FERN_BIG_PLANT("Fern (big plant)", 1, 31, ItemID.FERN_8186), + PLANT("Plant", 1, 31, ItemID.PLANT), SHORT_PLANT("Short Plant", 1, 31, ItemID.SHORT_PLANT), - DOCK_LEAF("Dock Leaf", 1, 31, ItemID.DOCK_LEAF), CRUDE_WOODEN_CHAIR("Crude Wooden Chair", 1, 58, ItemID.CRUDE_WOODEN_CHAIR), + OAK_PLANK("Oak Plank", 1, 60, ItemID.OAK_PLANK), + TEAK_PLANK("Teak Plank", 1, 90, ItemID.TEAK_PLANK), + EXIT_PORTAL("Exit Portal", 1, 100, ItemID.EXIT_PORTAL), + MAHOGANY_PLANK("Mahogany Plank", 1, 140, ItemID.MAHOGANY_PLANK), BROWN_RUG("Brown Rug", 2, 30, ItemID.BROWN_RUG), TORN_CURTAINS("Torn Curtains", 2, 132, ItemID.TORN_CURTAINS), CLAY_FIREPLACE("Clay Fireplace", 3, 30, ItemID.CLAY_FIREPLACE), WOODEN_BOOKCASE("Wooden Bookcase", 4, 115, ItemID.WOODEN_BOOKCASE), - FIREPIT("Firepit", 5, 40, ItemID.FIREPIT), CAT_BLANKET("Cat Blanket", 5, 15, ItemID.CAT_BLANKET), - DECORATIVE_ROCK("Decorative Rock", 5, 100, ItemID.DECORATIVE_ROCK), TREE("Tree", 5, 31, ItemID.TREE), - SMALL_FERN("Small Fern", 6, 70, ItemID.SMALL_FERN), - THISTLE("Thistle", 6, 70, ItemID.THISTLE), + FIREPIT("Firepit", 5, 40, ItemID.FIREPIT), + DECORATIVE_ROCK("Decorative Rock", 5, 100, ItemID.DECORATIVE_ROCK), BUSH("Bush", 6, 70, ItemID.BUSH), LARGE_LEAF_BUSH("Large Leaf Bush", 6, 70, ItemID.LARGELEAF_PLANT), + SMALL_FERN("Small Fern", 6, 70, ItemID.SMALL_FERN), + THISTLE("Thistle", 6, 70, ItemID.THISTLE), WOODEN_SHELVES_1("Wooden Shelves 1", 6, 87, ItemID.WOODEN_SHELVES_1), - PUMP_AND_DRAIN("Pump and Drain", 7, 100, ItemID.PUMP_AND_DRAIN), BEER_BARREL("Beer Barrel", 7, 87, ItemID.BEER_BARREL), + PUMP_AND_DRAIN("Pump and Drain", 7, 100, ItemID.PUMP_AND_DRAIN), WOODEN_CHAIR("Wooden Chair", 8, 87, ItemID.WOODEN_CHAIR), WOODEN_LARDER("Wooden Larder", 9, 228, ItemID.WOODEN_LARDER), - WOOD_DINING_TABLE("Wood Dining Table", 10, 115, ItemID.WOOD_DINING_TABLE), - POND("Pond", 10, 100, ItemID.POND), NICE_TREE("Nice Tree", 10, 44, ItemID.NICE_TREE), + POND("Pond", 10, 100, ItemID.POND), WOODEN_BENCH("Wooden Bench", 10, 115, ItemID.WOODEN_BENCH), + WOOD_DINING_TABLE("Wood Dining Table", 10, 115, ItemID.WOOD_DINING_TABLE), FIREPIT_WITH_HOOK("Firepit with Hook", 11, 60, ItemID.FIREPIT_WITH_HOOK), - REEDS("Reeds", 12, 100, ItemID.REEDS), - FERN_SMALL_PLANT("Fern (small plant)", 12, 100, ItemID.FERN), - CIDER_BARREL("Cider Barrel", 12, 91, ItemID.CIDER_BARREL), - WOODEN_SHELVES_2("Wooden Shelves 2", 12, 147, ItemID.WOODEN_SHELVES_2), WOOD_TABLE("Wood Table", 12, 87, ItemID.WOOD_DINING_TABLE), + CIDER_BARREL("Cider Barrel", 12, 91, ItemID.CIDER_BARREL), + FERN_SMALL_PLANT("Fern (small plant)", 12, 100, ItemID.FERN), HUGE_PLANT("Huge Plant", 12, 100, ItemID.HUGE_PLANT), + REEDS("Reeds", 12, 100, ItemID.REEDS), TALL_PLANT("Tall Plant", 12, 100, ItemID.TALL_PLANT), + WOODEN_SHELVES_2("Wooden Shelves 2", 12, 147, ItemID.WOODEN_SHELVES_2), RUG("Rug", 13, 60, ItemID.RUG), ROCKING_CHAIR("Rocking Chair", 14, 87, ItemID.ROCKING_CHAIR), - IMP_STATUE("Imp Statue", 15, 150, ItemID.IMP_STATUE), OAK_TREE("Oak Tree", 15, 70, ItemID.OAK_TREE), + IMP_STATUE("Imp Statue", 15, 150, ItemID.IMP_STATUE), OAK_DECORATION("Oak Decoration", 16, 120, ItemID.OAK_DECORATION), FIREPIT_WITH_POT("Firepit with Pot", 17, 80, ItemID.FIREPIT_WITH_POT), - CURTAINS("Curtains", 18, 225, ItemID.CURTAINS), ASGARNIAN_ALE("Asgarnian Ale", 18, 184, ItemID.ASGARNIAN_ALE), + CURTAINS("Curtains", 18, 225, ItemID.CURTAINS), CAT_BASKET("Cat Basket", 19, 58, ItemID.CAT_BASKET), OAK_CHAIR("Oak Chair", 19, 120, ItemID.OAK_CHAIR), - WOODEN_BED("Wooden Bed", 20, 117, ItemID.WOODEN_BED), SHOE_BOX("Shoe Box", 20, 58, ItemID.SHOE_BOX), + WOODEN_BED("Wooden Bed", 20, 117, ItemID.WOODEN_BED), SHAVING_STAND("Shaving Stand", 21, 30, ItemID.SHAVING_STAND), - OAK_DINING_TABLE("Oak Dining Table", 22, 240, ItemID.OAK_DINING_TABLE), OAK_BENCH("Oak Bench", 22, 240, ItemID.OAK_BENCH), + OAK_DINING_TABLE("Oak Dining Table", 22, 240, ItemID.OAK_DINING_TABLE), WOODEN_SHELVES_3("Wooden Shelves 3", 23, 147, ItemID.WOODEN_SHELVES_3), SMALL_OVEN("Small Oven", 24, 80, ItemID.SMALL_OVEN), OAK_CLOCK("Oak Clock", 25, 142, ItemID.OAK_CLOCK), - GREENMANS_ALE("Greenman's Ale", 26, 184, ItemID.GREENMANS_ALE), - OAK_ARMCHAIR("Oak Armchair", 26, 180, ItemID.OAK_ARMCHAIR), ROPE_BELL_PULL("Rope Bell-Pull", 26, 64, ItemID.ROPE_BELLPULL), - PUMP_AND_TUB("Pump and Tub", 27, 200, ItemID.PUMP_AND_TUB), + OAK_ARMCHAIR("Oak Armchair", 26, 180, ItemID.OAK_ARMCHAIR), + GREENMANS_ALE("Greenman's Ale", 26, 184, ItemID.GREENMANS_ALE), OAK_DRAWERS("Oak Drawers", 27, 120, ItemID.OAK_DRAWERS), - OAK_BOOKCASE("Oak Bookcase", 29, 180, ItemID.OAK_BOOKCASE), - LARGE_OVEN("Large Oven", 29, 100, ItemID.LARGE_OVEN), + PUMP_AND_TUB("Pump and Tub", 27, 200, ItemID.PUMP_AND_TUB), OAK_SHAVING_STAND("Oak Shaving Stand", 29, 61, ItemID.OAK_SHAVING_STAND), + LARGE_OVEN("Large Oven", 29, 100, ItemID.LARGE_OVEN), + OAK_BOOKCASE("Oak Bookcase", 29, 180, ItemID.OAK_BOOKCASE), WILLOW_TREE("Willow Tree", 30, 100, ItemID.WILLOW_TREE), OAK_BED("Oak Bed", 30, 210, ItemID.OAK_BED), LONG_BONE("Long Bone", 30, 4500, ItemID.LONG_BONE), @@ -106,27 +106,27 @@ public enum ConstructionAction implements NamedSkillAction CARVED_OAK_TABLE("Carved Oak Table", 31, 360, ItemID.CARVED_OAK_TABLE), OAK_KITCHEN_TABLE("Oak Kitchen Table", 32, 180, ItemID.TEAK_TABLE), BOXING_RING("Boxing Ring", 32, 420, ItemID.BOXING_RING), - OAK_LARDER("Oak Larder", 33, 480, ItemID.OAK_LARDER), - CUSHIONED_BASKET("Cushioned Basket", 33, 58, ItemID.CUSHIONED_BASKET), STONE_FIREPLACE("Stone Fireplace", 33, 40, ItemID.STONE_FIREPLACE), + CUSHIONED_BASKET("Cushioned Basket", 33, 58, ItemID.CUSHIONED_BASKET), + OAK_LARDER("Oak Larder", 33, 480, ItemID.OAK_LARDER), + GLOVE_RACK("Glove Rack", 34, 120, ItemID.GLOVE_RACK), STEEL_RANGE("Steel Range", 34, 120, ItemID.STEEL_RANGE), OAK_SHELVES_1("Oak Shelves 1", 34, 240, ItemID.OAK_SHELVES_1), - GLOVE_RACK("Glove Rack", 34, 120, ItemID.GLOVE_RACK), LARGE_OAK_BED("Large Oak Bed", 34, 330, ItemID.LARGE_OAK_BED), TEAK_ARMCHAIR("Teak Armchair", 35, 180, ItemID.TEAK_ARMCHAIR), - DRAGON_BITTER("Dragon Bitter", 36, 224, ItemID.DRAGON_BITTER), TEAK_DECORATION("Teak Decoration", 36, 180, ItemID.TEAK_DECORATION), + DRAGON_BITTER("Dragon Bitter", 36, 224, ItemID.DRAGON_BITTER), BELL_PULL("Bell-Pull", 37, 120, ItemID.BELLPULL), OAK_DRESSER("Oak Dresser", 37, 121, ItemID.OAK_DRESSER), TEAK_BENCH("Teak Bench", 38, 360, ItemID.CARVED_TEAK_BENCH), TEAK_TABLE("Teak Table", 38, 360, ItemID.TEAK_TABLE), OAK_WARDROBE("Oak Wardrobe", 39, 180, ItemID.OAK_WARDROBE), - TEAK_BED("Teak Bed", 40, 300, ItemID.TEAK_BED), - MAHOGANY_BOOKCASE("Mahogany Bookcase", 40, 420, ItemID.MAHOGANY_BOOKCASE), OAK_LECTERN("Oak Lectern", 40, 60, ItemID.OAK_LECTERN), + TEAK_BED("Teak Bed", 40, 300, ItemID.TEAK_BED), OPULENT_CURTAINS("Opulent Curtains", 40, 315, ItemID.OPULENT_CURTAINS), - FENCING_RING("Fencing Ring", 41, 570, ItemID.FENCING_RING), + MAHOGANY_BOOKCASE("Mahogany Bookcase", 40, 420, ItemID.MAHOGANY_BOOKCASE), GLOBE("Globe", 41, 180, ItemID.GLOBE), + FENCING_RING("Fencing Ring", 41, 570, ItemID.FENCING_RING), FANCY_RANGE("Fancy Range", 42, 160, ItemID.FANCY_RANGE), CRYSTAL_BALL("Crystal Ball", 42, 280, ItemID.CRYSTAL_BALL), ALCHEMICAL_CHART("Alchemical Chart", 43, 30, ItemID.ALCHEMICAL_CHART), @@ -134,20 +134,20 @@ public enum ConstructionAction implements NamedSkillAction WOODEN_TELESCOPE("Wooden Telescope", 44, 121, ItemID.OAK_TELESCOPE), WEAPONS_RACK("Weapons Rack", 44, 180, ItemID.WEAPONS_RACK), CARVED_TEAK_BENCH("Carved Teak Bench", 44, 360, ItemID.CARVED_TEAK_BENCH), + MAPLE_TREE("Maple Tree", 45, 122, ItemID.MAPLE_TREE), OAK_SHELVES_2("Oak Shelves 2", 45, 240, ItemID.OAK_SHELVES_2), - CARVED_TEAK_TABLE("Carved Teak Table", 45, 600, ItemID.CARVED_TEAK_TABLE), LARGE_TEAK_BED("Large Teak Bed", 45, 480, ItemID.LARGE_TEAK_BED), - MAPLE_TREE("Maple Tree", 45, 122, ItemID.MAPLE_TREE), + CARVED_TEAK_TABLE("Carved Teak Table", 45, 600, ItemID.CARVED_TEAK_TABLE), TEAK_DRESSER("Teak Dresser", 46, 181, ItemID.TEAK_DRESSER), - SINK("Sink", 47, 300, ItemID.SINK), - EAGLE_LECTERN("Eagle Lectern", 47, 120, ItemID.EAGLE_LECTERN), DEMON_LECTERN("Demon Lectern", 47, 120, ItemID.DEMON_LECTERN), + EAGLE_LECTERN("Eagle Lectern", 47, 120, ItemID.EAGLE_LECTERN), + SINK("Sink", 47, 300, ItemID.SINK), MOUNTED_MYTHICAL_CAPE("Mounted Mythical Cape", 47, 370, ItemID.MYTHICAL_CAPE), CHEFS_DELIGHT("Chef's Delight", 48, 224, ItemID.CHEFS_DELIGHT), + TELEPORT_FOCUS("Teleport Focus", 50, 40, ItemID.TELEPORT_FOCUS), + ORNAMENTAL_GLOBE("Ornamental Globe", 50, 270, ItemID.ORNAMENTAL_GLOBE), TEAK_PORTAL("Teak Portal", 50, 270, ItemID.TEAK_PORTAL), MAHOGANY_ARMCHAIR("Mahogany Armchair", 50, 280, ItemID.MAHOGANY_ARMCHAIR), - ORNAMENTAL_GLOBE("Ornamental Globe", 50, 270, ItemID.ORNAMENTAL_GLOBE), - TELEPORT_FOCUS("Teleport Focus", 50, 40, ItemID.TELEPORT_FOCUS), TEAK_DRAWERS("Teak Drawers", 51, 180, ItemID.TEAK_DRAWERS), COMBAT_RING("Combat Ring", 51, 630, ItemID.COMBAT_RING), TEAK_KITCHEN_TABLE("Teak Kitchen Table", 52, 270, ItemID.TEAK_TABLE), @@ -157,41 +157,41 @@ public enum ConstructionAction implements NamedSkillAction EXTRA_WEAPONS_RACK("Extra Weapons Rack", 54, 440, ItemID.EXTRA_WEAPONS_RACK), ELEMENTAL_SPHERE("Elemental Sphere", 54, 580, ItemID.ELEMENTAL_SPHERE), TEAK_CLOCK("Teak Clock", 55, 202, ItemID.TEAK_CLOCK), - GILDED_DECORATION("Gilded Decoration", 56, 1020, ItemID.GILDED_DECORATION), FANCY_TEAK_DRESSER("Fancy Teak Dresser", 56, 182, ItemID.FANCY_TEAK_DRESSER), TEAK_SHELVES_1("Teak Shelves 1", 56, 330, ItemID.TEAK_SHELVES_1), - TEAK_EAGLE_LECTERN("Teak Eagle Lectern", 57, 180, ItemID.TEAK_EAGLE_LECTERN), + GILDED_DECORATION("Gilded Decoration", 56, 1020, ItemID.GILDED_DECORATION), TEAK_DEMON_LECTERN("Teak Demon Lectern", 57, 180, ItemID.TEAK_DEMON_LECTERN), + TEAK_EAGLE_LECTERN("Teak Eagle Lectern", 57, 180, ItemID.TEAK_EAGLE_LECTERN), LIMESTONE_ATTACK_STONE("Limestone attack stone", 59, 200, ItemID.ATTACK_STONE), LUNAR_GLOBE("Lunar Globe", 59, 570, ItemID.LUNAR_GLOBE), - GILDED_FOUR_POSTER_BED("Gilded 4-Poster Bed", 60, 1330, ItemID.GILDED_4POSTER), - POSH_BELL_PULL("Posh Bell-Pull", 60, 420, ItemID.POSH_BELLPULL), - SPICE_RACK("Spice Rack", 60, 374, ItemID.SPICE_RACK), YEW_TREE("Yew Tree", 60, 141, ItemID.YEW_TREE), + SPICE_RACK("Spice Rack", 60, 374, ItemID.SPICE_RACK), + POSH_BELL_PULL("Posh Bell-Pull", 60, 420, ItemID.POSH_BELLPULL), + GILDED_FOUR_POSTER_BED("Gilded 4-Poster Bed", 60, 1330, ItemID.GILDED_4POSTER), GILDED_BENCH("Gilded Bench", 61, 1760, ItemID.GILDED_BENCH), + ASTRONOMICAL_CHART("Astronomical Chart", 63, 45, ItemID.ASTRONOMICAL_CHART), TEAK_WARDROBE("Teak Wardrobe", 63, 270, ItemID.TEAK_WARDROBE), MARBLE_FIREPLACE("Marble Fireplace", 63, 500, ItemID.MARBLE_FIREPLACE), - ASTRONOMICAL_CHART("Astronomical Chart", 63, 45, ItemID.ASTRONOMICAL_CHART), TEAK_TELESCOPE("Teak Telescope", 64, 181, ItemID.TEAK_TELESCOPE), MAHOGANY_DRESSER("Mahogany Dresser", 64, 281, ItemID.MAHOGANY_DRESSER), + OPULENT_RUG("Opulent Rug", 65, 360, ItemID.OPULENT_RUG), MAHOGANY_PORTAL("Mahogany Portal", 65, 420, ItemID.MAHOGANY_PORTAL), GREATER_FOCUS("Greater Focus", 65, 500, ItemID.GREATER_FOCUS), - OPULENT_RUG("Opulent Rug", 65, 360, ItemID.OPULENT_RUG), TEAK_GARDEN_BENCH("Teak Garden Bench", 66, 540, ItemID.TEAK_GARDEN_BENCH), CRYSTAL_OF_POWER("Crystal of Power", 66, 890, ItemID.CRYSTAL_OF_POWER), - TEAK_SHELVES_2("Teak Shelves 2", 67, 930, ItemID.TEAK_SHELVES_2), MAHOGANY_DEMON_LECTERN("Mahogany Demon Lectern", 67, 580, ItemID.TEAK_DEMON_LECTERN), MAHOGANY_EAGLE_LECTERN("Mahogany Eagle Lectern", 67, 580, ItemID.TEAK_DEMON_LECTERN), + TEAK_SHELVES_2("Teak Shelves 2", 67, 930, ItemID.TEAK_SHELVES_2), CELESTIAL_GLOBE("Celestial Globe", 68, 570, ItemID.CELESTIAL_GLOBE), DUNGEON_ENTRANCE("Dungeon Entrance", 70, 500, ItemID.DUNGEON_ENTRANCE), RANGING_PEDESTALS("Ranging Pedestals", 71, 720, ItemID.RANGING_PEDESTALS), OPULENT_TABLE("Opulent Table", 72, 3100, ItemID.OPULENT_TABLE), - OAK_DOOR("Oak Door", 74, 600, ItemID.OAK_DOOR), GILDED_DRESSER("Gilded Dresser", 74, 582, ItemID.GILDED_DRESSER), - MAHOGANY_WARDROBE("Mahogany Wardrobe", 75, 420, ItemID.MAHOGANY_WARDROBE), + OAK_DOOR("Oak Door", 74, 600, ItemID.OAK_DOOR), MAGIC_TREE("Magic Tree", 75, 223, ItemID.MAGIC_TREE), - ARMILLARY_GLOBE("Armillary Globe", 77, 960, ItemID.GLOBE), + MAHOGANY_WARDROBE("Mahogany Wardrobe", 75, 420, ItemID.MAHOGANY_WARDROBE), GNOME_BENCH("Gnome Bench", 77, 840, ItemID.GNOME_BENCH), + ARMILLARY_GLOBE("Armillary Globe", 77, 960, ItemID.GLOBE), MARBLE_PORTAL("Marble Portal", 80, 1500, ItemID.MARBLE_PORTAL), SCRYING_POOL("Scrying Pool", 80, 2000, ItemID.SCRYING_POOL), BALANCE_BEAM("Balance Beam", 81, 1000, ItemID.BALANCE_BEAM), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CookingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CookingAction.java index 1613e7b46d..669a0871c2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CookingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CookingAction.java @@ -34,19 +34,19 @@ public enum CookingAction implements ItemSkillAction { SINEW(ItemID.SINEW, 1, 3), - SHRIMPS(ItemID.SHRIMPS, 1, 30), + ANCHOVIES(ItemID.ANCHOVIES, 1, 30), COOKED_CHICKEN(ItemID.COOKED_CHICKEN, 1, 30), COOKED_MEAT(ItemID.COOKED_MEAT, 1, 30), COOKED_RABBIT(ItemID.COOKED_RABBIT, 1, 30), - ANCHOVIES(ItemID.ANCHOVIES, 1, 30), + SHRIMPS(ItemID.SHRIMPS, 1, 30), + BREAD(ItemID.BREAD, 1, 40), SARDINE(ItemID.SARDINE, 1, 40), - POISON_KARAMBWAN(ItemID.POISON_KARAMBWAN, 1, 80), UGTHANKI_MEAT(ItemID.UGTHANKI_MEAT, 1, 40), - BREAD(ItemID.BREAD, 1, 40), + POISON_KARAMBWAN(ItemID.POISON_KARAMBWAN, 1, 80), HERRING(ItemID.HERRING, 5, 50), FRUIT_BLAST(ItemID.FRUIT_BLAST, 6, 50), - BAKED_POTATO(ItemID.BAKED_POTATO, 7, 15), GUPPY(ItemID.GUPPY, 7, 12), + BAKED_POTATO(ItemID.BAKED_POTATO, 7, 15), PINEAPPLE_PUNCH(ItemID.PINEAPPLE_PUNCH, 8, 70), SPICY_SAUCE(ItemID.SPICY_SAUCE, 9, 25), MACKEREL(ItemID.MACKEREL, 10, 60), @@ -57,25 +57,25 @@ public enum CookingAction implements ItemSkillAction THIN_SNAIL_MEAT(ItemID.THIN_SNAIL_MEAT, 12, 70), SPICY_CRUNCHIES(ItemID.SPICY_CRUNCHIES, 12, 100), SCRAMBLED_EGG(ItemID.SCRAMBLED_EGG, 13, 50), - CIDER(ItemID.CIDER, 14, 182), WORM_CRUNCHIES(ItemID.WORM_CRUNCHIES, 14, 104), + CIDER(ItemID.CIDER, 14, 182), TROUT(ItemID.TROUT, 15, 70), - SPIDER_ON_STICK(ItemID.SPIDER_ON_STICK, 16, 80), - SPIDER_ON_SHAFT(ItemID.SPIDER_ON_SHAFT, 16, 80), ROAST_RABBIT(ItemID.ROAST_RABBIT, 16, 72.5f), + SPIDER_ON_SHAFT(ItemID.SPIDER_ON_SHAFT, 16, 80), + SPIDER_ON_STICK(ItemID.SPIDER_ON_STICK, 16, 80), CHOCCHIP_CRUNCHIES(ItemID.CHOCCHIP_CRUNCHIES, 16, 100), LEAN_SNAIL_MEAT(ItemID.LEAN_SNAIL_MEAT, 17, 80), COD(ItemID.COD, 18, 75), WIZARD_BLIZZARD(ItemID.WIZARD_BLIZZARD, 18, 110), DWARVEN_STOUT(ItemID.DWARVEN_STOUT, 19, 215, true), - SHORT_GREEN_GUY(ItemID.SHORT_GREEN_GUY, 20, 120), - MEAT_PIE(ItemID.MEAT_PIE, 20, 110), - PIKE(ItemID.PIKE, 20, 80), - CUP_OF_TEA(ItemID.CUP_OF_TEA, 20, 52), CAVEFISH(ItemID.CAVEFISH, 20, 23), + CUP_OF_TEA(ItemID.CUP_OF_TEA, 20, 52), + PIKE(ItemID.PIKE, 20, 80), + MEAT_PIE(ItemID.MEAT_PIE, 20, 110), + SHORT_GREEN_GUY(ItemID.SHORT_GREEN_GUY, 20, 120), + POT_OF_CREAM(ItemID.POT_OF_CREAM, 21, 18), ROAST_BEAST_MEAT(ItemID.ROAST_BEAST_MEAT, 21, 82.5f), COOKED_CRAB_MEAT(ItemID.COOKED_CRAB_MEAT, 21, 100), - POT_OF_CREAM(ItemID.POT_OF_CREAM, 21, 18), FAT_SNAIL_MEAT(ItemID.FAT_SNAIL_MEAT, 22, 95), EGG_AND_TOMATO(ItemID.EGG_AND_TOMATO, 23, 50), ASGARNIAN_ALE(ItemID.ASGARNIAN_ALE, 24, 248, true), @@ -84,47 +84,47 @@ public enum CookingAction implements ItemSkillAction FRUIT_BATTA(ItemID.FRUIT_BATTA, 25, 150), TOAD_BATTA(ItemID.TOAD_BATTA, 26, 152), WORM_BATTA(ItemID.WORM_BATTA, 27, 154), - VEGETABLE_BATTA(ItemID.VEGETABLE_BATTA, 28, 156), - SWEETCORN(ItemID.COOKED_SWEETCORN, 28, 104), COOKED_SLIMY_EEL(ItemID.COOKED_SLIMY_EEL, 28, 95), + SWEETCORN(ItemID.COOKED_SWEETCORN, 28, 104), + VEGETABLE_BATTA(ItemID.VEGETABLE_BATTA, 28, 156), MUD_PIE(ItemID.MUD_PIE, 29, 128), - GREENMANS_ALE(ItemID.GREENMANS_ALE, 29, 281), CHEESE_AND_TOMATO_BATTA(ItemID.CHEESETOM_BATTA, 29, 158), + GREENMANS_ALE(ItemID.GREENMANS_ALE, 29, 281), + ROASTED_CHOMPY(ItemID.COOKED_CHOMPY, 30, 100), TUNA(ItemID.TUNA, 30, 100), APPLE_PIE(ItemID.APPLE_PIE, 30, 130), WORM_HOLE(ItemID.WORM_HOLE, 30, 170), COOKED_KARAMBWAN(ItemID.COOKED_KARAMBWAN, 30, 190), - ROASTED_CHOMPY(ItemID.COOKED_CHOMPY, 30, 100), FISHCAKE(ItemID.COOKED_FISHCAKE, 31, 100), DRUNK_DRAGON(ItemID.DRUNK_DRAGON, 32, 160), - CHOC_SATURDAY(ItemID.CHOC_SATURDAY, 33, 170), TETRA(ItemID.TETRA, 33, 31), + CHOC_SATURDAY(ItemID.CHOC_SATURDAY, 33, 170), GARDEN_PIE(ItemID.GARDEN_PIE, 34, 138), WIZARDS_MIND_BOMB(ItemID.WIZARDS_MIND_BOMB, 34, 314, true), - JUG_OF_WINE(ItemID.JUG_OF_WINE, 35, 200), - PLAIN_PIZZA(ItemID.PLAIN_PIZZA, 35, 143), RAINBOW_FISH(ItemID.RAINBOW_FISH, 35, 110), + PLAIN_PIZZA(ItemID.PLAIN_PIZZA, 35, 143), VEG_BALL(ItemID.VEG_BALL, 35, 175), + JUG_OF_WINE(ItemID.JUG_OF_WINE, 35, 200), BLURBERRY_SPECIAL(ItemID.BLURBERRY_SPECIAL, 37, 180), - CAVE_EEL(ItemID.CAVE_EEL, 38, 115), PAT_OF_BUTTER(ItemID.PAT_OF_BUTTER, 38, 40.5f), - DRAGON_BITTER(ItemID.DRAGON_BITTER, 39, 347), + CAVE_EEL(ItemID.CAVE_EEL, 38, 115), POTATO_WITH_BUTTER(ItemID.POTATO_WITH_BUTTER, 39, 40), + DRAGON_BITTER(ItemID.DRAGON_BITTER, 39, 347), LOBSTER(ItemID.LOBSTER, 40, 120), CAKE(ItemID.CAKE, 40, 180), TANGLED_TOADS_LEGS(ItemID.TANGLED_TOADS_LEGS, 40, 185), - CHILLI_POTATO(ItemID.CHILLI_POTATO, 41, 165.5f), COOKED_JUBBLY(ItemID.COOKED_JUBBLY, 41, 160), - CHOCOLATE_BOMB(ItemID.CHOCOLATE_BOMB, 42, 190), + CHILLI_POTATO(ItemID.CHILLI_POTATO, 41, 165.5f), FRIED_ONIONS(ItemID.FRIED_ONIONS, 42, 60), + CHOCOLATE_BOMB(ItemID.CHOCOLATE_BOMB, 42, 190), BASS(ItemID.BASS, 43, 130), MOONLIGHT_MEAD(ItemID.MOONLIGHT_MEAD, 44, 380), SWORDFISH(ItemID.SWORDFISH, 45, 140), MEAT_PIZZA(ItemID.MEAT_PIZZA, 45, 169), - FRIED_MUSHROOMS(ItemID.FRIED_MUSHROOMS, 46, 60), CATFISH(ItemID.CATFISH, 46, 43), - FISH_PIE(ItemID.FISH_PIE, 47, 164), + FRIED_MUSHROOMS(ItemID.FRIED_MUSHROOMS, 46, 60), POTATO_WITH_CHEESE(ItemID.POTATO_WITH_CHEESE, 47, 40), + FISH_PIE(ItemID.FISH_PIE, 47, 164), CHEESE(ItemID.CHEESE, 48, 64, true), AXEMANS_FOLLY(ItemID.AXEMANS_FOLLY, 49, 413), COOKED_OOMLIE_WRAP(ItemID.COOKED_OOMLIE_WRAP, 50, 30), @@ -135,11 +135,11 @@ public enum CookingAction implements ItemSkillAction CHEFS_DELIGHT(ItemID.CHEFS_DELIGHT, 54, 446), ANCHOVY_PIZZA(ItemID.ANCHOVY_PIZZA, 55, 182), MUSHROOM_AND_ONION(ItemID.MUSHROOM__ONION, 57, 120), - UGTHANKI_KEBAB_FRESH(ItemID.UGTHANKI_KEBAB, 58, 80), PITTA_BREAD(ItemID.PITTA_BREAD, 58, 40), + UGTHANKI_KEBAB_FRESH(ItemID.UGTHANKI_KEBAB, 58, 80), SLAYERS_RESPITE(ItemID.SLAYERS_RESPITE, 59, 479), - CURRY(ItemID.CURRY, 60, 280), MUSHROOM_PIE(ItemID.MUSHROOM_PIE, 60, 200), + CURRY(ItemID.CURRY, 60, 280), MONKFISH(ItemID.MONKFISH, 62, 150), MUSHROOM_POTATO(ItemID.MUSHROOM_POTATO, 64, 270.5f), PINEAPPLE_PIZZA(ItemID.PINEAPPLE_PIZZA, 65, 188), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CraftingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CraftingAction.java index 5f3618468e..9806fa02e9 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CraftingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CraftingAction.java @@ -34,27 +34,27 @@ public enum CraftingAction implements ItemSkillAction { BALL_OF_WOOL(ItemID.BALL_OF_WOOL, 1, 2.5f), - UNFIRED_POT(ItemID.UNFIRED_POT, 1, 6.3f), POT(ItemID.POT, 1, 6.3f), + UNFIRED_POT(ItemID.UNFIRED_POT, 1, 6.3f), + OPAL_RING(ItemID.OPAL_RING, 1, 10), LEATHER_GLOVES(ItemID.LEATHER_GLOVES, 1, 13.8f), OPAL(ItemID.OPAL, 1, 15), - OPAL_RING(ItemID.OPAL_RING, 1, 10), - MOLTEN_GLASS(ItemID.MOLTEN_GLASS, 1, 20), BEER_GLASS(ItemID.BEER_GLASS, 1, 17.5f, true), + MOLTEN_GLASS(ItemID.MOLTEN_GLASS, 1, 20), EMPTY_CANDLE_LANTERN(ItemID.EMPTY_CANDLE_LANTERN, 4, 19), - GOLD_RING(ItemID.GOLD_RING, 5, 15), BIRD_HOUSE(ItemID.BIRD_HOUSE, 5, 15), + GOLD_RING(ItemID.GOLD_RING, 5, 15), GOLD_NECKLACE(ItemID.GOLD_NECKLACE, 6, 20), - LEATHER_BOOTS(ItemID.LEATHER_BOOTS, 7, 16.3f), - UNFIRED_PIE_DISH(ItemID.UNFIRED_PIE_DISH, 7, 15), PIE_DISH(ItemID.PIE_DISH, 7, 10), + UNFIRED_PIE_DISH(ItemID.UNFIRED_PIE_DISH, 7, 15), + LEATHER_BOOTS(ItemID.LEATHER_BOOTS, 7, 16.3f), GOLD_BRACELET(ItemID.GOLD_BRACELET_11069, 7, 25), - UNFIRED_BOWL(ItemID.UNFIRED_BOWL, 8, 18), BOWL(ItemID.BOWL, 8, 15), + UNFIRED_BOWL(ItemID.UNFIRED_BOWL, 8, 18), GOLD_AMULET_U(ItemID.GOLD_AMULET_U, 8, 30), COWL(ItemID.LEATHER_COWL, 9, 18.5f), - CROSSBOW_STRING(ItemID.CROSSBOW_STRING, 10, 15), BOW_STRING(ItemID.BOW_STRING, 10, 15), + CROSSBOW_STRING(ItemID.CROSSBOW_STRING, 10, 15), LEATHER_VAMBRACES(ItemID.LEATHER_VAMBRACES, 11, 22), EMPTY_OIL_LAMP(ItemID.EMPTY_OIL_LAMP, 12, 25), JADE(ItemID.JADE, 13, 20), @@ -62,79 +62,79 @@ public enum CraftingAction implements ItemSkillAction LEATHER_BODY(ItemID.LEATHER_BODY, 14, 25), OAK_BIRD_HOUSE(ItemID.OAK_BIRD_HOUSE, 15, 20), RED_TOPAZ(ItemID.RED_TOPAZ, 16, 25), + OPAL_NECKLACE(ItemID.OPAL_NECKLACE, 16, 35), TOPAZ_RING(ItemID.TOPAZ_RING, 16, 35), HOLY_SYMBOL(ItemID.HOLY_SYMBOL, 16, 50), - OPAL_NECKLACE(ItemID.OPAL_NECKLACE, 16, 35), UNHOLY_SYMBOL(ItemID.UNHOLY_SYMBOL, 17, 50), LEATHER_CHAPS(ItemID.LEATHER_CHAPS, 18, 27), - UNFIRED_PLANT_POT(ItemID.UNFIRED_PLANT_POT, 19, 20), EMPTY_PLANT_POT(ItemID.EMPTY_PLANT_POT, 19, 17.5f), + UNFIRED_PLANT_POT(ItemID.UNFIRED_PLANT_POT, 19, 20), MAGIC_STRING(ItemID.MAGIC_STRING, 19, 30), - SAPPHIRE(ItemID.SAPPHIRE, 20, 50), SAPPHIRE_RING(ItemID.SAPPHIRE_RING, 20, 40), + SAPPHIRE(ItemID.SAPPHIRE, 20, 50), EMPTY_SACK(ItemID.EMPTY_SACK, 21, 38), - SAPPHIRE_NECKLACE(ItemID.SAPPHIRE_NECKLACE, 22, 55), OPAL_BRACELET(ItemID.OPAL_BRACELET, 22, 45), - SAPPHIRE_BRACELET(ItemID.SAPPHIRE_BRACELET_11072, 23, 60), + SAPPHIRE_NECKLACE(ItemID.SAPPHIRE_NECKLACE, 22, 55), TIARA(ItemID.TIARA, 23, 52.5f), + SAPPHIRE_BRACELET(ItemID.SAPPHIRE_BRACELET_11072, 23, 60), SAPPHIRE_AMULET_U(ItemID.SAPPHIRE_AMULET_U, 24, 65), - UNFIRED_POT_LID(ItemID.UNFIRED_POT_LID, 25, 20), POT_LID(ItemID.POT_LID, 25, 20), - JADE_NECKLACE(ItemID.JADE_NECKLACE, 25, 54), + UNFIRED_POT_LID(ItemID.UNFIRED_POT_LID, 25, 20), WILLOW_BIRD_HOUSE(ItemID.WILLOW_BIRD_HOUSE, 25, 25), + JADE_NECKLACE(ItemID.JADE_NECKLACE, 25, 54), DRIFT_NET(ItemID.DRIFT_NET, 26, 55), - EMERALD(ItemID.EMERALD, 27, 67.5f), EMERALD_RING(ItemID.EMERALD_RING, 27, 55), OPAL_AMULET_U(ItemID.OPAL_AMULET_U, 27, 55), + EMERALD(ItemID.EMERALD, 27, 67.5f), HARDLEATHER_BODY(ItemID.HARDLEATHER_BODY, 28, 35), EMERALD_NECKLACE(ItemID.EMERALD_NECKLACE, 29, 60), JADE_BRACELET(ItemID.JADE_BRACELET, 29, 60), - EMERALD_BRACELET(ItemID.EMERALD_BRACELET, 30, 65), ROPE(ItemID.ROPE, 30, 25, true), + EMERALD_BRACELET(ItemID.EMERALD_BRACELET, 30, 65), EMERALD_AMULET_U(ItemID.EMERALD_AMULET_U, 31, 70), SPIKY_VAMBRACES(ItemID.SPIKY_VAMBRACES, 32, 6), TOPAZ_NECKLACE(ItemID.TOPAZ_NECKLACE, 32, 70), VIAL(ItemID.VIAL, 33, 35, true), - RUBY(ItemID.RUBY, 34, 85), - RUBY_RING(ItemID.RUBY_RING, 34, 70), JADE_AMULET_U(ItemID.JADE_AMULET_U, 34, 70), - BROODOO_SHIELD(ItemID.BROODOO_SHIELD, 35, 100), + RUBY_RING(ItemID.RUBY_RING, 34, 70), + RUBY(ItemID.RUBY, 34, 85), TEAK_BIRD_HOUSE(ItemID.TEAK_BIRD_HOUSE, 35, 30), + BROODOO_SHIELD(ItemID.BROODOO_SHIELD, 35, 100), BASKET(ItemID.BASKET, 36, 56), COIF(ItemID.COIF, 38, 37, true), TOPAZ_BRACELET(ItemID.TOPAZ_BRACELET, 38, 75), RUBY_NECKLACE(ItemID.RUBY_NECKLACE, 40, 75), HARD_LEATHER_SHIELD(ItemID.HARD_LEATHER_SHIELD, 41, 70), - RUBY_BRACELET(ItemID.RUBY_BRACELET, 42, 80), FISHBOWL(ItemID.FISHBOWL, 42, 42.5f), - DIAMOND(ItemID.DIAMOND, 43, 107.5f), + RUBY_BRACELET(ItemID.RUBY_BRACELET, 42, 80), DIAMOND_RING(ItemID.DIAMOND_RING, 43, 85), - TOPAZ_AMULET_U(ItemID.TOPAZ_AMULET_U, 45, 80), + DIAMOND(ItemID.DIAMOND, 43, 107.5f), SNAKESKIN_BOOTS(ItemID.SNAKESKIN_BOOTS, 45, 30), MAPLE_BIRD_HOUSE(ItemID.MAPLE_BIRD_HOUSE, 45, 35), + TOPAZ_AMULET_U(ItemID.TOPAZ_AMULET_U, 45, 80), UNPOWERED_ORB(ItemID.UNPOWERED_ORB, 46, 52.5f), SNAKESKIN_VAMBRACES(ItemID.SNAKESKIN_VAMBRACES, 47, 35), SNAKESKIN_BANDANA(ItemID.SNAKESKIN_BANDANA, 48, 45), LANTERN_LENS(ItemID.LANTERN_LENS, 49, 55), - RUBY_AMULET_U(ItemID.RUBY_AMULET_U, 50, 85), MAHOGANY_BIRD_HOUSE(ItemID.MAHOGANY_BIRD_HOUSE, 50, 40), + RUBY_AMULET_U(ItemID.RUBY_AMULET_U, 50, 85), SNAKESKIN_CHAPS(ItemID.SNAKESKIN_CHAPS, 51, 50), SNAKESKIN_BODY(ItemID.SNAKESKIN_BODY, 53, 55), WATER_BATTLESTAFF(ItemID.WATER_BATTLESTAFF, 54, 100), - DRAGONSTONE(ItemID.DRAGONSTONE, 55, 137.5f), DRAGONSTONE_RING(ItemID.DRAGONSTONE_RING, 55, 100), + DRAGONSTONE(ItemID.DRAGONSTONE, 55, 137.5f), DIAMOND_NECKLACE(ItemID.DIAMOND_NECKLACE, 56, 90), SNAKESKIN_SHIELD(ItemID.SNAKESKIN_SHIELD, 56, 100), GREEN_DHIDE_VAMB(ItemID.GREEN_DHIDE_VAMBRACES, 57, 62, true), DIAMOND_BRACELET(ItemID.DIAMOND_BRACELET, 58, 95), EARTH_BATTLESTAFF(ItemID.EARTH_BATTLESTAFF, 58, 112.5f), - GREEN_DHIDE_CHAPS(ItemID.GREEN_DHIDE_CHAPS, 60, 124, true), YEW_BIRD_HOUSE(ItemID.YEW_BIRD_HOUSE, 60, 45), - FIRE_BATTLESTAFF(ItemID.FIRE_BATTLESTAFF, 62, 125), + GREEN_DHIDE_CHAPS(ItemID.GREEN_DHIDE_CHAPS, 60, 124, true), GREEN_DHIDE_SHIELD(ItemID.GREEN_DHIDE_SHIELD, 62, 124), + FIRE_BATTLESTAFF(ItemID.FIRE_BATTLESTAFF, 62, 125), GREEN_DHIDE_BODY(ItemID.GREEN_DHIDE_BODY, 63, 186, true), - AIR_BATTLESTAFF(ItemID.AIR_BATTLESTAFF, 66, 137.5f), BLUE_DHIDE_VAMB(ItemID.BLUE_DHIDE_VAMBRACES, 66, 70), + AIR_BATTLESTAFF(ItemID.AIR_BATTLESTAFF, 66, 137.5f), ONYX_RING(ItemID.ONYX_RING, 67, 115), ONYX(ItemID.ONYX, 67, 167.5f), BLUE_DHIDE_CHAPS(ItemID.BLUE_DHIDE_CHAPS, 68, 140), @@ -144,26 +144,26 @@ public enum CraftingAction implements ItemSkillAction DRAGONSTONE_NECKLACE(ItemID.DRAGON_NECKLACE, 72, 105), RED_DHIDE_VAMB(ItemID.RED_DHIDE_VAMBRACES, 73, 78), DRAGONSTONE_BRACELET(ItemID.DRAGONSTONE_BRACELET, 74, 110), - RED_DHIDE_CHAPS(ItemID.RED_DHIDE_CHAPS, 75, 156), MAGIC_BIRD_HOUSE(ItemID.MAGIC_BIRD_HOUSE, 75, 50), + RED_DHIDE_CHAPS(ItemID.RED_DHIDE_CHAPS, 75, 156), RED_DHIDE_SHIELD(ItemID.RED_DHIDE_SHIELD, 76, 156), RED_DHIDE_BODY(ItemID.RED_DHIDE_BODY, 77, 234), BLACK_DHIDE_VAMB(ItemID.BLACK_DHIDE_VAMBRACES, 79, 86), DRAGONSTONE_AMULET_U(ItemID.DRAGONSTONE_AMULET_U, 80, 150), - BLACK_DHIDE_CHAPS(ItemID.BLACK_DHIDE_CHAPS, 82, 172), ONYX_NECKLACE(ItemID.ONYX_NECKLACE, 82, 120), + BLACK_DHIDE_CHAPS(ItemID.BLACK_DHIDE_CHAPS, 82, 172), AMETHYST_BOLT_TIPS(ItemID.AMETHYST_BOLT_TIPS, 83, 4), BLACK_DHIDE_SHIELD(ItemID.BLACK_DHIDE_SHIELD, 83, 172), - BLACK_DHIDE_BODY(ItemID.BLACK_DHIDE_BODY, 84, 258), ONYX_BRACELET(ItemID.ONYX_BRACELET, 84, 125), + BLACK_DHIDE_BODY(ItemID.BLACK_DHIDE_BODY, 84, 258), AMETHYST_ARROWTIPS(ItemID.AMETHYST_ARROWTIPS, 85, 4), AMETHYST_JAVELIN_HEADS(ItemID.AMETHYST_JAVELIN_HEADS, 87, 12), LIGHT_ORB(ItemID.LIGHT_ORB, 87, 70), AMETHYST_DART_TIP(ItemID.AMETHYST_DART_TIP, 89, 7.5f), - ZENYTE(ItemID.ZENYTE, 89, 200), ZENYTE_RING(ItemID.ZENYTE_RING, 89, 150), - ONYX_AMULET_U(ItemID.ONYX_AMULET_U, 90, 165), + ZENYTE(ItemID.ZENYTE, 89, 200), REDWOOD_BIRD_HOUSE(ItemID.REDWOOD_BIRD_HOUSE, 90, 55), + ONYX_AMULET_U(ItemID.ONYX_AMULET_U, 90, 165), ZENYTE_NECKLACE(ItemID.ZENYTE_NECKLACE, 92, 165), ZENYTE_BRACELET(ItemID.ZENYTE_BRACELET_19532, 95, 180), ZENYTE_AMULET_U(ItemID.ZENYTE_AMULET_U, 98, 200), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FarmingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FarmingAction.java index 2ce70b1cce..24ff6014a8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FarmingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FarmingAction.java @@ -34,23 +34,23 @@ @Getter public enum FarmingAction implements NamedSkillAction { - PLANT("Plant", 1, 31, ItemID.PLANT), + POTATOES("Potatoes", 1, 8, ItemID.POTATO), + WINTER_SQIRK("Winter Sq'irk", 1, 30, ItemID.WINTER_SQIRK), + DOCK_LEAF("Dock Leaf", 1, 31, ItemID.DOCK_LEAF), FERN_BIG_PLANT("Fern (big plant)", 1, 31, ItemID.FERN_8186), + PLANT("Plant", 1, 31, ItemID.PLANT), SHORT_PLANT("Short Plant", 1, 31, ItemID.SHORT_PLANT), - DOCK_LEAF("Dock Leaf", 1, 31, ItemID.DOCK_LEAF), - SMALL_FERN("Small Fern", 1, 70, ItemID.SMALL_FERN), - THISTLE("Thistle", 1, 70, ItemID.THISTLE), + SPRING_SQIRK("Spring Sq'irk", 1, 40, ItemID.SPRING_SQIRK), + AUTUMN_SQIRK("Autumn Sq'irk", 1, 50, ItemID.AUTUMN_SQIRK), + SUMMER_SQIRK("Summer Sq'irk", 1, 60, ItemID.SUMMER_SQIRK), BUSH("Bush", 1, 70, ItemID.BUSH), LARGE_LEAF_BUSH("Large Leaf Bush", 1, 70, ItemID.LARGELEAF_PLANT), + SMALL_FERN("Small Fern", 1, 70, ItemID.SMALL_FERN), + THISTLE("Thistle", 1, 70, ItemID.THISTLE), + FERN_SMALL_PLANT("Fern (small plant)", 1, 100, ItemID.FERN), HUGE_PLANT("Huge Plant", 1, 100, ItemID.HUGE_PLANT), - TALL_PLANT("Tall Plant", 1, 100, ItemID.TALL_PLANT), REEDS("Reeds", 1, 100, ItemID.REEDS), - FERN_SMALL_PLANT("Fern (small plant)", 1, 100, ItemID.FERN), - WINTER_SQIRK("Winter Sq'irk", 1, 30, ItemID.WINTER_SQIRK), - SPRING_SQIRK("Spring Sq'irk", 1, 40, ItemID.SPRING_SQIRK), - AUTUMN_SQIRK("Autumn Sq'irk", 1, 50, ItemID.AUTUMN_SQIRK), - SUMMER_SQIRK("Summer Sq'irk", 1, 60, ItemID.SUMMER_SQIRK), - POTATOES("Potatoes", 1, 8, ItemID.POTATO), + TALL_PLANT("Tall Plant", 1, 100, ItemID.TALL_PLANT), ONIONS("Onions", 5, 10, ItemID.ONION), CABBAGES("Cabbages", 7, 10, ItemID.CABBAGE), GUAM_LEAF("Guam Leaf", 9, 11, ItemID.GUAM_LEAF), @@ -94,8 +94,8 @@ public enum FarmingAction implements NamedSkillAction DWARF_WEED("Dwarf Weed", 79, 170.5f, ItemID.DWARF_WEED), DRAGONFRUIT_TREE("Dragonfruit Tree", 81, 17895, ItemID.DRAGONFRUIT), SPIRIT_TREE("Spirit Tree", 83, 19501.3f, ItemID.SPIRIT_TREE), - CELASTRUS_TREE("Celastrus Tree", 85, 14334, ItemID.CELASTRUS_BARK), TORSTOL("Torstol", 85, 199.5f, ItemID.TORSTOL), + CELASTRUS_TREE("Celastrus Tree", 85, 14334, ItemID.CELASTRUS_BARK), REDWOOD_TREE("Redwood Tree", 90, 22680, ItemID.REDWOOD_LOGS), ; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingAction.java index 424fb851a8..f75baa737f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingAction.java @@ -32,8 +32,8 @@ @Getter public enum FiremakingAction implements ItemSkillAction { - LOGS(ItemID.LOGS, 1, 40), ACHEY_TREE_LOGS(ItemID.ACHEY_TREE_LOGS, 1, 40), + LOGS(ItemID.LOGS, 1, 40), OAK_LOGS(ItemID.OAK_LOGS, 15, 60), WILLOW_LOGS(ItemID.WILLOW_LOGS, 30, 90), TEAK_LOGS(ItemID.TEAK_LOGS, 35, 105), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingAction.java index 386ec964c8..ac5d904ca0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingAction.java @@ -33,25 +33,25 @@ public enum FishingAction implements ItemSkillAction { RAW_SHRIMPS(ItemID.RAW_SHRIMPS, 1, 10), - RAW_SARDINE(ItemID.RAW_SARDINE, 5, 20), RAW_KARAMBWANJI(ItemID.RAW_KARAMBWANJI, 5, 5), + RAW_SARDINE(ItemID.RAW_SARDINE, 5, 20), RAW_GUPPY(ItemID.RAW_GUPPY, 7, 8), RAW_HERRING(ItemID.RAW_HERRING, 10, 30), RAW_ANCHOVIES(ItemID.RAW_ANCHOVIES, 15, 40), RAW_MACKEREL(ItemID.RAW_MACKEREL, 16, 20), - RAW_TROUT(ItemID.RAW_TROUT, 20, 50), RAW_CAVEFISH(ItemID.RAW_CAVEFISH, 20, 16), + RAW_TROUT(ItemID.RAW_TROUT, 20, 50), RAW_COD(ItemID.RAW_COD, 23, 45), RAW_PIKE(ItemID.RAW_PIKE, 25, 60), RAW_SLIMY_EEL(ItemID.RAW_SLIMY_EEL, 28, 80), RAW_SALMON(ItemID.RAW_SALMON, 30, 70), RAW_TETRA(ItemID.RAW_TETRA, 33, 24), RAW_TUNA(ItemID.RAW_TUNA, 35, 80), - RAW_RAINBOW_FISH(ItemID.RAW_RAINBOW_FISH, 38, 80), RAW_CAVE_EEL(ItemID.RAW_CAVE_EEL, 38, 80), + RAW_RAINBOW_FISH(ItemID.RAW_RAINBOW_FISH, 38, 80), RAW_LOBSTER(ItemID.RAW_LOBSTER, 40, 90), - RAW_BASS(ItemID.RAW_BASS, 46, 100), RAW_CATFISH(ItemID.RAW_CATFISH, 46, 33), + RAW_BASS(ItemID.RAW_BASS, 46, 100), LEAPING_TROUT(ItemID.LEAPING_TROUT, 48, 50), RAW_SWORDFISH(ItemID.RAW_SWORDFISH, 50, 100), LEAPING_SALMON(ItemID.LEAPING_SALMON, 58, 70), @@ -62,8 +62,8 @@ public enum FishingAction implements ItemSkillAction RAW_SEA_TURTLE(ItemID.RAW_SEA_TURTLE, 79, 38), INFERNAL_EEL(ItemID.INFERNAL_EEL, 80, 95), RAW_MANTA_RAY(ItemID.RAW_MANTA_RAY, 81, 46), - RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, 82, 120), MINNOW(ItemID.MINNOW, 82, 26.5f), + RAW_ANGLERFISH(ItemID.RAW_ANGLERFISH, 82, 120), RAW_DARK_CRAB(ItemID.RAW_DARK_CRAB, 85, 130), SACRED_EEL(ItemID.SACRED_EEL, 87, 105), ; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java index 2e8726ee55..a189711492 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java @@ -38,81 +38,81 @@ public enum FletchingAction implements ItemSkillAction BRONZE_ARROW(ItemID.BRONZE_ARROW, 1, 1.3f), BRONZE_JAVELIN(ItemID.BRONZE_JAVELIN, 3, 1), OGRE_ARROW(ItemID.OGRE_ARROW, 5, 1), - SHORTBOW_U(ItemID.SHORTBOW_U, 5, 5), SHORTBOW(ItemID.SHORTBOW, 5, 5), + SHORTBOW_U(ItemID.SHORTBOW_U, 5, 5), BRONZE_BOLTS(ItemID.BRONZE_BOLTS, 9, 0.5f), + BRONZE_CROSSBOW(ItemID.BRONZE_CROSSBOW, 9, 6), WOODEN_STOCK(ItemID.WOODEN_STOCK, 9, 6), BRONZE_CROSSBOW_U(ItemID.BRONZE_CROSSBOW_U, 9, 12), - BRONZE_CROSSBOW(ItemID.BRONZE_CROSSBOW, 9, 6), BRONZE_DART(ItemID.BRONZE_DART, 10, 1.8f), LONGBOW(ItemID.LONGBOW, 10, 10), LONGBOW_U(ItemID.LONGBOW_U, 10, 10), OPAL_BOLTS(ItemID.OPAL_BOLTS, 11, 1.6f), IRON_ARROW(ItemID.IRON_ARROW, 15, 2.5f), IRON_JAVELIN(ItemID.IRON_JAVELIN, 17, 2), - OAK_SHORTBOW_U(ItemID.OAK_SHORTBOW_U, 20, 16.5f), OAK_SHORTBOW(ItemID.OAK_SHORTBOW, 20, 16.5f), + OAK_SHORTBOW_U(ItemID.OAK_SHORTBOW_U, 20, 16.5f), IRON_DART(ItemID.IRON_DART, 22, 3.8f), + BLURITE_CROSSBOW(ItemID.BLURITE_CROSSBOW, 24, 16), OAK_STOCK(ItemID.OAK_STOCK, 24, 16), BLURITE_CROSSBOW_U(ItemID.BLURITE_CROSSBOW_U, 24, 32), - BLURITE_CROSSBOW(ItemID.BLURITE_CROSSBOW, 24, 16), - OAK_LONGBOW_U(ItemID.OAK_LONGBOW_U, 25, 25), OAK_LONGBOW(ItemID.OAK_LONGBOW, 25, 25), + OAK_LONGBOW_U(ItemID.OAK_LONGBOW_U, 25, 25), OAK_SHIELD(ItemID.OAK_SHIELD, 27, 50), STEEL_ARROW(ItemID.STEEL_ARROW, 30, 5), - STEEL_JAVELIN(ItemID.STEEL_JAVELIN, 32, 5), KEBBIT_BOLTS(ItemID.KEBBIT_BOLTS, 32, 1), - WILLOW_SHORTBOW_U(ItemID.WILLOW_SHORTBOW_U, 35, 33.3f), + STEEL_JAVELIN(ItemID.STEEL_JAVELIN, 32, 5), WILLOW_SHORTBOW(ItemID.WILLOW_SHORTBOW, 35, 33.3f), + WILLOW_SHORTBOW_U(ItemID.WILLOW_SHORTBOW_U, 35, 33.3f), STEEL_DART(ItemID.STEEL_DART, 37, 7.5f), IRON_BOLTS(ItemID.IRON_BOLTS, 39, 1.5f), + IRON_CROSSBOW(ItemID.IRON_CROSSBOW, 39, 22), WILLOW_STOCK(ItemID.WILLOW_STOCK, 39, 22), IRON_CROSSBOW_U(ItemID.IRON_CROSSBOW_U, 39, 44), - IRON_CROSSBOW(ItemID.IRON_CROSSBOW, 39, 22), - WILLOW_LONGBOW_U(ItemID.WILLOW_LONGBOW_U, 40, 41.5f), WILLOW_LONGBOW(ItemID.WILLOW_LONGBOW, 40, 41.5f), + WILLOW_LONGBOW_U(ItemID.WILLOW_LONGBOW_U, 40, 41.5f), BATTLESTAFF(ItemID.BATTLESTAFF, 40, 80), PEARL_BOLTS(ItemID.PEARL_BOLTS, 41, 3.2f), - WILLOW_SHIELD(ItemID.WILLOW_SHIELD, 42, 83), LONG_KEBBIT_BOLTS(ItemID.LONG_KEBBIT_BOLTS, 42, 1.3f), + WILLOW_SHIELD(ItemID.WILLOW_SHIELD, 42, 83), SILVER_BOLTS(ItemID.SILVER_BOLTS, 43, 2.5f), MITHRIL_ARROW(ItemID.MITHRIL_ARROW, 45, 7.5f), STEEL_BOLTS(ItemID.STEEL_BOLTS, 46, 3.5f), + STEEL_CROSSBOW(ItemID.STEEL_CROSSBOW, 46, 27), TEAK_STOCK(ItemID.TEAK_STOCK, 46, 27), STEEL_CROSSBOW_U(ItemID.STEEL_CROSSBOW_U, 46, 54), - STEEL_CROSSBOW(ItemID.STEEL_CROSSBOW, 46, 27), MITHRIL_JAVELIN(ItemID.MITHRIL_JAVELIN, 47, 8), - MAPLE_SHORTBOW_U(ItemID.MAPLE_SHORTBOW_U, 50, 50), MAPLE_SHORTBOW(ItemID.MAPLE_SHORTBOW, 50, 50), + MAPLE_SHORTBOW_U(ItemID.MAPLE_SHORTBOW_U, 50, 50), BARBED_BOLTS(ItemID.BARBED_BOLTS, 51, 9.5f), - MITHRIL_DART(ItemID.MITHRIL_DART, 52, 11.2f), BROAD_ARROWS(ItemID.BROAD_ARROWS, 52, 10), + MITHRIL_DART(ItemID.MITHRIL_DART, 52, 11.2f), TOXIC_BLOWPIPE(ItemID.TOXIC_BLOWPIPE, 53, 120), - MITH_CROSSBOW(ItemID.MITHRIL_CROSSBOW, 54, 32), - MAPLE_STOCK(ItemID.MAPLE_STOCK, 54, 32), MITHRIL_BOLTS(ItemID.MITHRIL_BOLTS, 54, 5), + MAPLE_STOCK(ItemID.MAPLE_STOCK, 54, 32), + MITH_CROSSBOW(ItemID.MITHRIL_CROSSBOW, 54, 32), MITHRIL_CROSSBOW_U(ItemID.MITHRIL_CROSSBOW_U, 54, 64), - MAPLE_LONGBOW_U(ItemID.MAPLE_LONGBOW_U, 55, 58.3f), BROAD_BOLTS(ItemID.BROAD_BOLTS, 55, 3), MAPLE_LONGBOW(ItemID.MAPLE_LONGBOW, 55, 58), + MAPLE_LONGBOW_U(ItemID.MAPLE_LONGBOW_U, 55, 58.3f), SAPPHIRE_BOLTS(ItemID.SAPPHIRE_BOLTS, 56, 4.7f), MAPLE_SHIELD(ItemID.MAPLE_SHIELD, 57, 116.5f), EMERALD_BOLTS(ItemID.EMERALD_BOLTS, 58, 5.5f), ADAMANT_ARROW(ItemID.ADAMANT_ARROW, 60, 10), ADAMANT_BOLTS(ItemID.ADAMANT_BOLTS, 61, 7), + ADAMANT_CROSSBOW(ItemID.ADAMANT_CROSSBOW, 61, 41), MAHOGANY_STOCK(ItemID.MAHOGANY_STOCK, 61, 41), ADAMANT_CROSSBOW_U(ItemID.ADAMANT_CROSSBOW_U, 61, 82), - ADAMANT_CROSSBOW(ItemID.ADAMANT_CROSSBOW, 61, 41), ADAMANT_JAVELIN(ItemID.ADAMANT_JAVELIN, 62, 10), RUBY_BOLTS(ItemID.RUBY_BOLTS, 63, 6.3f), DIAMOND_BOLTS(ItemID.DIAMOND_BOLTS, 65, 7), YEW_SHORTBOW(ItemID.YEW_SHORTBOW, 65, 67.5f), YEW_SHORTBOW_U(ItemID.YEW_SHORTBOW_U, 65, 67.5f), ADAMANT_DART(ItemID.ADAMANT_DART, 67, 15), - RUNITE_CROSSBOW_U(ItemID.RUNITE_CROSSBOW_U, 69, 100), + RUNITE_BOLTS(ItemID.RUNITE_BOLTS, 69, 10), RUNE_CROSSBOW(ItemID.RUNE_CROSSBOW, 69, 50), YEW_STOCK(ItemID.YEW_STOCK, 69, 50), - RUNITE_BOLTS(ItemID.RUNITE_BOLTS, 69, 10), + RUNITE_CROSSBOW_U(ItemID.RUNITE_CROSSBOW_U, 69, 100), YEW_LONGBOW(ItemID.YEW_LONGBOW, 70, 75), YEW_LONGBOW_U(ItemID.YEW_LONGBOW_U, 70, 75), DRAGONSTONE_BOLTS(ItemID.DRAGONSTONE_BOLTS, 71, 8.2f), @@ -121,9 +121,9 @@ public enum FletchingAction implements ItemSkillAction RUNE_ARROW(ItemID.RUNE_ARROW, 75, 12.5f), AMETHYST_BROAD_BOLTS(ItemID.AMETHYST_BROAD_BOLTS, 76, 10.6f), RUNE_JAVELIN(ItemID.RUNE_JAVELIN, 77, 12.4f), + DRAGON_CROSSBOW(ItemID.DRAGON_CROSSBOW, 78, 70), MAGIC_STOCK(ItemID.MAGIC_STOCK, 78, 70), DRAGON_CROSSBOW_U(ItemID.DRAGON_CROSSBOW_U, 78, 135), - DRAGON_CROSSBOW(ItemID.DRAGON_CROSSBOW, 78, 70), MAGIC_SHORTBOW(ItemID.MAGIC_SHORTBOW, 80, 83.3f), MAGIC_SHORTBOW_U(ItemID.MAGIC_SHORTBOW_U, 80, 83.3f), RUNE_DART(ItemID.RUNE_DART, 81, 18.8f), @@ -133,8 +133,8 @@ public enum FletchingAction implements ItemSkillAction MAGIC_LONGBOW(ItemID.MAGIC_LONGBOW, 85, 91.5f), MAGIC_LONGBOW_U(ItemID.MAGIC_LONGBOW_U, 85, 91.5f), MAGIC_SHIELD(ItemID.MAGIC_SHIELD, 87, 183), - AMETHYST_DART(ItemID.AMETHYST_DART, 90, 21), DRAGON_ARROW(ItemID.DRAGON_ARROW, 90, 15), + AMETHYST_DART(ItemID.AMETHYST_DART, 90, 21), DRAGON_JAVELIN(ItemID.DRAGON_JAVELIN, 92, 15), REDWOOD_SHIELD(ItemID.REDWOOD_SHIELD, 92, 216), DRAGON_DART(ItemID.DRAGON_DART, 95, 25), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HerbloreAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HerbloreAction.java index 025022ffd5..2e1ac43c4f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HerbloreAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HerbloreAction.java @@ -33,8 +33,8 @@ @Getter public enum HerbloreAction implements ItemSkillAction { - ATTACK_POTION_3(ItemID.ATTACK_POTION3, 3, 25), GUAM_LEAF(ItemID.GUAM_LEAF, 3, 2.5f), + ATTACK_POTION_3(ItemID.ATTACK_POTION3, 3, 25), MARRENTILL(ItemID.MARRENTILL, 5, 3.8f), ANTIPOISON_3(ItemID.ANTIPOISON3, 5, 37.5f), RELICYMS_BALM_3(ItemID.RELICYMS_BALM3, 8, 40), @@ -58,8 +58,8 @@ public enum HerbloreAction implements ItemSkillAction IRIT_LEAF(ItemID.IRIT_LEAF, 40, 8.8f), HARRALANDER_TAR(ItemID.HARRALANDER_TAR, 44, 72.5f), SUPER_ATTACK_3(ItemID.SUPER_ATTACK3, 45, 100), - SUPERANTIPOISON_3(ItemID.SUPERANTIPOISON3, 48, 106.3f), AVANTOE(ItemID.AVANTOE, 48, 10), + SUPERANTIPOISON_3(ItemID.SUPERANTIPOISON3, 48, 106.3f), FISHING_POTION_3(ItemID.FISHING_POTION3, 50, 112.5f), SUPER_ENERGY_3(ItemID.SUPER_ENERGY3, 52, 117.5f), HUNTER_POTION_3(ItemID.HUNTER_POTION3, 53, 120), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HunterAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HunterAction.java index 4fac817a16..415e467b5d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HunterAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HunterAction.java @@ -33,8 +33,8 @@ @Getter public enum HunterAction implements NamedSkillAction { - CRIMSON_SWIFT("Crimson Swift", 1, 34, ItemID.CRIMSON_SWIFT), POLAR_KEBBIT("Polar Kebbit", 1, 30, ItemID.KEBBIT), + CRIMSON_SWIFT("Crimson Swift", 1, 34, ItemID.CRIMSON_SWIFT), COMMON_KEBBIT("Common Kebbit", 3, 36, ItemID.KEBBIT_9954), GOLDEN_WARBLER("Golden Warbler", 5, 47, ItemID.GOLDEN_WARBLER), REGULAR_BIRD_HOUSE("Regular Bird House", 5, 280, ItemID.BIRD_HOUSE), @@ -83,14 +83,14 @@ public enum HunterAction implements NamedSkillAction BLACK_SALAMANDER("Black Salamander", 67, 319.5f, ItemID.BLACK_SALAMANDER), DASHING_KEBBIT("Dashing Kebbit", 69, 156, ItemID.KEBBIT_9964), BLACK_CHINCHOMPA("Black Chinchompa", 73, 315, ItemID.BLACK_CHINCHOMPA), - MAGIC_BIRD_HOUSE("Magic Bird House", 74, 1140, ItemID.MAGIC_BIRD_HOUSE), NINJA_IMPLING("Ninja Impling", 74, 52, ItemID.NINJA_IMPLING_JAR), NINJA_IMPLING_GIELINOR("Ninja Impling (Gielinor)", 74, 240, ItemID.NINJA_IMPLING_JAR), + MAGIC_BIRD_HOUSE("Magic Bird House", 74, 1140, ItemID.MAGIC_BIRD_HOUSE), CRYSTAL_IMPLING("Crystal Impling", 80, 280, ItemID.CRYSTAL_IMPLING_JAR), DRAGON_IMPLING("Dragon Impling", 83, 65, ItemID.DRAGON_IMPLING_JAR), DRAGON_IMPLING_GIELINOR("Dragon Impling (Gielinor)", 83, 300, ItemID.DRAGON_IMPLING_JAR), - REDWOOD_BIRD_HOUSE("Redwood Bird House", 89, 1200, ItemID.REDWOOD_BIRD_HOUSE), LUCKY_IMPLING("Lucky Impling", 89, 380, ItemID.LUCKY_IMPLING_JAR), + REDWOOD_BIRD_HOUSE("Redwood Bird House", 89, 1200, ItemID.REDWOOD_BIRD_HOUSE), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java index bb3547fe71..36688d08de 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java @@ -38,27 +38,27 @@ public enum MagicAction implements SkillAction ENCHANT_OPAL_BOLT("Enchant Opal Bolt", 4, 9, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), WATER_STRIKE("Water Strike", 5, 7.5f, SpriteID.SPELL_WATER_STRIKE, false), ARCEUUS_LIBRARY_TELEPORT("Arceuus Library Teleport", 6, 10, SpriteID.SPELL_ARCEUUS_LIBRARY_TELEPORT, true), - ENCHANT_SAPPHIRE_JEWELLERY("Enchant Sapphire Jewellery", 7, 17.5f, SpriteID.SPELL_LVL_1_ENCHANT, false), ENCHANT_SAPPHIRE_BOLT("Enchant Sapphire Bolt", 7, 17.5f, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), + ENCHANT_SAPPHIRE_JEWELLERY("Enchant Sapphire Jewellery", 7, 17.5f, SpriteID.SPELL_LVL_1_ENCHANT, false), EARTH_STRIKE("Earth Strike", 9, 9.5f, SpriteID.SPELL_EARTH_STRIKE, false), WEAKEN("Weaken", 11, 21, SpriteID.SPELL_WEAKEN, false), FIRE_STRIKE("Fire Strike", 13, 11.5f, SpriteID.SPELL_FIRE_STRIKE, false), ENCHANT_JADE_BOLT("Enchant Jade Bolt", 14, 19, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), BONES_TO_BANANAS("Bones To Bananas", 15, 25, SpriteID.SPELL_BONES_TO_BANANAS, false), BASIC_REANIMATION("Basic Reanimation", 16, 32, SpriteID.SPELL_BASIC_REANIMATION, true), - DRAYNOR_MANOR_TELEPORT("Draynor Manor Teleport", 17, 16, SpriteID.SPELL_DRAYNOR_MANOR_TELEPORT, true), WIND_BOLT("Wind Bolt", 17, 13.5f, SpriteID.SPELL_WIND_BOLT, false), + DRAYNOR_MANOR_TELEPORT("Draynor Manor Teleport", 17, 16, SpriteID.SPELL_DRAYNOR_MANOR_TELEPORT, true), CURSE("Curse", 19, 29, SpriteID.SPELL_CURSE, false), BIND("Bind", 20, 30, SpriteID.SPELL_BIND, false), LOW_LEVEL_ALCHEMY("Low Level Alchemy", 21, 31, SpriteID.SPELL_LOW_LEVEL_ALCHEMY, false), WATER_BOLT("Water Bolt", 23, 16.5f, SpriteID.SPELL_WATER_BOLT, false), ENCHANT_PEARL_BOLT("Enchant Pearl Bolt", 24, 29, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), VARROCK_TELEPORT("Varrock Teleport", 25, 35, SpriteID.SPELL_VARROCK_TELEPORT, false), - ENCHANT_EMERALD_JEWELLERY("Enchant Emerald Jewellery", 27, 37, SpriteID.SPELL_LVL_2_ENCHANT, false), ENCHANT_EMERALD_BOLT("Enchant Emerald Bolt", 27, 37, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), + ENCHANT_EMERALD_JEWELLERY("Enchant Emerald Jewellery", 27, 37, SpriteID.SPELL_LVL_2_ENCHANT, false), MIND_ALTAR_TELEPORT("Mind Altar Teleport", 28, 22, SpriteID.SPELL_MIND_ALTAR_TELEPORT, true), - ENCHANT_TOPAZ_BOLT("Enchant Topaz Bolt", 29, 33, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), EARTH_BOLT("Earth Bolt", 29, 19.5f, SpriteID.SPELL_EARTH_BOLT, false), + ENCHANT_TOPAZ_BOLT("Enchant Topaz Bolt", 29, 33, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), LUMBRIDGE_TELEPORT("Lumbridge Teleport", 31, 41, SpriteID.SPELL_LUMBRIDGE_TELEPORT, false), TELEKINETIC_GRAB("Telekinetic Grab", 33, 43, SpriteID.SPELL_TELEKINETIC_GRAB, false), RESPAWN_TELEPORT("Respawn Teleport", 34, 27, SpriteID.SPELL_RESPAWN_TELEPORT, true), @@ -69,138 +69,138 @@ public enum MagicAction implements SkillAction CRUMBLE_UNDEAD("Crumble Undead", 39, 24.5f, SpriteID.SPELL_CRUMBLE_UNDEAD, false), SALVE_GRAVEYARD_TELEPORT("Salve Graveyard Teleport", 40, 30, SpriteID.SPELL_SALVE_GRAVEYARD_TELEPORT, true), TELEPORT_TO_HOUSE("Teleport To House", 40, 30, SpriteID.SPELL_TELEPORT_TO_HOUSE, true), - ADEPT_REANIMATION("Adept Reanimation", 41, 80, SpriteID.SPELL_ADEPT_REANIMATION, true), WIND_BLAST("Wind Blast", 41, 25.5f, SpriteID.SPELL_WIND_BLAST, false), + ADEPT_REANIMATION("Adept Reanimation", 41, 80, SpriteID.SPELL_ADEPT_REANIMATION, true), SUPERHEAT_ITEM("Superheat Item", 43, 53, SpriteID.SPELL_SUPERHEAT_ITEM, false), INFERIOR_DEMONBANE("Inferior Demonbane", 44, 27, SpriteID.SPELL_INFERIOR_DEMONBANE, true), CAMELOT_TELEPORT("Camelot Teleport", 45, 55.5f, SpriteID.SPELL_CAMELOT_TELEPORT, true), WATER_BLAST("Water Blast", 47, 28.5f, SpriteID.SPELL_WATER_BLAST, false), SHADOW_VEIL("Shadow Veil", 47, 58, SpriteID.SPELL_SHADOW_VEIL, true), FENKENSTRAINS_CASTLE_TELEPORT("Fenkenstrain's Castle Teleport", 48, 50, SpriteID.SPELL_FENKENSTRAINS_CASTLE_TELEPORT, true), - ENCHANT_RUBY_JEWELLERY("Enchant Ruby Jewellery", 49, 59, SpriteID.SPELL_LVL_3_ENCHANT, false), ENCHANT_RUBY_BOLT("Enchant Ruby Bolt", 49, 59, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), + ENCHANT_RUBY_JEWELLERY("Enchant Ruby Jewellery", 49, 59, SpriteID.SPELL_LVL_3_ENCHANT, false), IBAN_BLAST("Iban Blast", 50, 30, SpriteID.SPELL_IBAN_BLAST, true), - SMOKE_RUSH("Smoke Rush", 50, 30, SpriteID.SPELL_SMOKE_RUSH, true), MAGIC_DART("Magic Dart", 50, 30, SpriteID.SPELL_MAGIC_DART, true), - SNARE("Snare", 50, 60, SpriteID.SPELL_SNARE, false), + SMOKE_RUSH("Smoke Rush", 50, 30, SpriteID.SPELL_SMOKE_RUSH, true), DARK_LURE("Dark Lure", 50, 60, SpriteID.SPELL_DARK_LURE, true), + SNARE("Snare", 50, 60, SpriteID.SPELL_SNARE, false), ARDOUGNE_TELEPORT("Ardougne Teleport", 51, 61, SpriteID.SPELL_ARDOUGNE_TELEPORT, true), SHADOW_RUSH("Shadow Rush", 52, 31, SpriteID.SPELL_SHADOW_RUSH, true), EARTH_BLAST("Earth Blast", 53, 31.5f, SpriteID.SPELL_EARTH_BLAST, false), PADDEWWA_TELEPORT("Paddewwa Teleport", 54, 64, SpriteID.SPELL_PADDEWWA_TELEPORT, true), HIGH_LEVEL_ALCHEMY("High Level Alchemy", 55, 65, SpriteID.SPELL_HIGH_LEVEL_ALCHEMY, false), - CHARGE_WATER_ORB("Charge Water Orb", 56, 66, SpriteID.SPELL_CHARGE_WATER_ORB, true), BLOOD_RUSH("Blood Rush", 56, 33, SpriteID.SPELL_BLOOD_RUSH, true), SKELETAL_GRASP("Skeletal Grasp", 56, 33, SpriteID.SPELL_SKELETAL_GRASP, true), - ENCHANT_DIAMOND_JEWELLERY("Enchant Diamond Jewellery", 57, 67, SpriteID.SPELL_LVL_4_ENCHANT, false), + CHARGE_WATER_ORB("Charge Water Orb", 56, 66, SpriteID.SPELL_CHARGE_WATER_ORB, true), ENCHANT_DIAMOND_BOLT("Enchant Diamond Bolt", 57, 67, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), + ENCHANT_DIAMOND_JEWELLERY("Enchant Diamond Jewellery", 57, 67, SpriteID.SPELL_LVL_4_ENCHANT, false), RESURRECT_SUPERIOR_THRALL("Resurrect Superior Thrall", 57, 70, SpriteID.SPELL_RESURRECT_SUPERIOR_SKELETON, true), - WATCHTOWER_TELEPORT("Watchtower Teleport", 58, 68, SpriteID.SPELL_WATCHTOWER_TELEPORT, true), ICE_RUSH("Ice Rush", 58, 34, SpriteID.SPELL_ICE_RUSH, true), + WATCHTOWER_TELEPORT("Watchtower Teleport", 58, 68, SpriteID.SPELL_WATCHTOWER_TELEPORT, true), FIRE_BLAST("Fire Blast", 59, 34.5f, SpriteID.SPELL_FIRE_BLAST, false), MARK_OF_DARKNESS("Mark of Darkness", 59, 70, SpriteID.SPELL_MARK_OF_DARKNESS, true), - SENNTISTEN_TELEPORT("Senntisten Teleport", 60, 70, SpriteID.SPELL_SENNTISTEN_TELEPORT, true), CLAWS_OF_GUTHIX("Claws Of Guthix", 60, 35, SpriteID.SPELL_CLAWS_OF_GUTHIX, true), FLAMES_OF_ZAMORAK("Flames Of Zamorak", 60, 35, SpriteID.SPELL_FLAMES_OF_ZAMORAK, true), SARADOMIN_STRIKE("Saradomin Strike", 60, 35, SpriteID.SPELL_SARADOMIN_STRIKE, true), - CHARGE_EARTH_ORB("Charge Earth Orb", 60, 70, SpriteID.SPELL_CHARGE_EARTH_ORB, true), BONES_TO_PEACHES("Bones To Peaches", 60, 35.5f, SpriteID.SPELL_BONES_TO_PEACHES, true), - WEST_ARDOUGNE_TELEPORT("West Ardougne Teleport", 61, 68, SpriteID.SPELL_WEST_ARDOUGNE_TELEPORT, true), + CHARGE_EARTH_ORB("Charge Earth Orb", 60, 70, SpriteID.SPELL_CHARGE_EARTH_ORB, true), + SENNTISTEN_TELEPORT("Senntisten Teleport", 60, 70, SpriteID.SPELL_SENNTISTEN_TELEPORT, true), TROLLHEIM_TELEPORT("Trollheim Teleport", 61, 68, SpriteID.SPELL_TROLLHEIM_TELEPORT, true), + WEST_ARDOUGNE_TELEPORT("West Ardougne Teleport", 61, 68, SpriteID.SPELL_WEST_ARDOUGNE_TELEPORT, true), SMOKE_BURST("Smoke Burst", 62, 36, SpriteID.SPELL_SMOKE_BURST, true), - WIND_WAVE("Wind Wave", 62, 36, SpriteID.SPELL_WIND_WAVE, true), SUPERIOR_DEMONBANE("Superior Demonbane", 62, 36, SpriteID.SPELL_SUPERIOR_DEMONBANE, true), + WIND_WAVE("Wind Wave", 62, 36, SpriteID.SPELL_WIND_WAVE, true), CHARGE_FIRE_ORB("Charge Fire Orb", 63, 73, SpriteID.SPELL_CHARGE_FIRE_ORB, true), SHADOW_BURST("Shadow Burst", 64, 37, SpriteID.SPELL_SHADOW_BURST, true), TELEPORT_APE_ATOLL("Teleport Ape Atoll", 64, 74, SpriteID.SPELL_TELEPORT_TO_APE_ATOLL, true), LESSER_CORRUPTION("Lesser Corruption", 64, 75, SpriteID.SPELL_LESSER_CORRUPTION, true), + WATER_WAVE("Water Wave", 65, 37.5f, SpriteID.SPELL_WATER_WAVE, true), BAKE_PIE("Bake Pie", 65, 60, SpriteID.SPELL_BAKE_PIE, true), - HARMONY_ISLAND_TELEPORT("Harmony Island Teleport", 65, 74, SpriteID.SPELL_HARMONY_ISLAND_TELEPORT, true), GEOMANCY("Geomancy", 65, 60, SpriteID.SPELL_GEOMANCY, true), - WATER_WAVE("Water Wave", 65, 37.5f, SpriteID.SPELL_WATER_WAVE, true), - CHARGE_AIR_ORB("Charge Air Orb", 66, 76, SpriteID.SPELL_CHARGE_AIR_ORB, true), + HARMONY_ISLAND_TELEPORT("Harmony Island Teleport", 65, 74, SpriteID.SPELL_HARMONY_ISLAND_TELEPORT, true), CURE_PLANT("Cure Plant", 66, 60, SpriteID.SPELL_CURE_PLANT, true), - KHARYRLL_TELEPORT("Kharyrll Teleport", 66, 76, SpriteID.SPELL_KHARYRLL_TELEPORT, true), - VULNERABILITY("Vulnerability", 66, 76, SpriteID.SPELL_VULNERABILITY, true), MONSTER_EXAMINE("Monster Examine", 66, 61, SpriteID.SPELL_MONSTER_EXAMINE, true), + CHARGE_AIR_ORB("Charge Air Orb", 66, 76, SpriteID.SPELL_CHARGE_AIR_ORB, true), + KHARYRLL_TELEPORT("Kharyrll Teleport", 66, 76, SpriteID.SPELL_KHARYRLL_TELEPORT, true), VILE_VIGOUR("Vile Vigour", 66, 76, SpriteID.SPELL_VILE_VIGOUR, true), + VULNERABILITY("Vulnerability", 66, 76, SpriteID.SPELL_VULNERABILITY, true), NPC_CONTACT("Npc Contact", 67, 63, SpriteID.SPELL_NPC_CONTACT, true), BLOOD_BURST("Blood Burst", 68, 39, SpriteID.SPELL_BLOOD_BURST, true), CURE_OTHER("Cure Other", 68, 65, SpriteID.SPELL_CURE_OTHER, true), - ENCHANT_DRAGONSTONE_JEWELLERY("Enchant Dragonstone Jewellery", 68, 78, SpriteID.SPELL_LVL_5_ENCHANT, true), - ENCHANT_DRAGONSTONE_BOLT("Enchant Dragonstone Bolt", 68, 78, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), HUMIDIFY("Humidify", 68, 65, SpriteID.SPELL_HUMIDIFY, true), + ENCHANT_DRAGONSTONE_BOLT("Enchant Dragonstone Bolt", 68, 78, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), + ENCHANT_DRAGONSTONE_JEWELLERY("Enchant Dragonstone Jewellery", 68, 78, SpriteID.SPELL_LVL_5_ENCHANT, true), MOONCLAN_TELEPORT("Moonclan Teleport", 69, 66, SpriteID.SPELL_MOONCLAN_TELEPORT, true), EARTH_WAVE("Earth Wave", 70, 40, SpriteID.SPELL_EARTH_WAVE, true), ICE_BURST("Ice Burst", 70, 40, SpriteID.SPELL_ICE_BURST, true), TELE_GROUP_MOONCLAN("Tele Group Moonclan", 70, 67, SpriteID.SPELL_TELE_GROUP_MOONCLAN, true), DEGRIME("Degrime", 70, 83, SpriteID.SPELL_DEGRIME, true), - OURANIA_TELEPORT("Ourania Teleport", 71, 69, SpriteID.SPELL_OURANIA_TELEPORT, true), - CEMETERY_TELEPORT("Cemetery Teleport", 71, 82, SpriteID.SPELL_CEMETERY_TELEPORT, true), CURE_ME("Cure Me", 71, 69, SpriteID.SPELL_CURE_ME, true), + OURANIA_TELEPORT("Ourania Teleport", 71, 69, SpriteID.SPELL_OURANIA_TELEPORT, true), HUNTER_KIT("Hunter Kit", 71, 70, SpriteID.SPELL_HUNTER_KIT, true), - EXPERT_REANIMATION("Expert Reanimation", 72, 138, SpriteID.SPELL_EXPERT_REANIMATION, true), - LASSAR_TELEPORT("Lassar Teleport", 72, 82, SpriteID.SPELL_LASSAR_TELEPORT, true), + CEMETERY_TELEPORT("Cemetery Teleport", 71, 82, SpriteID.SPELL_CEMETERY_TELEPORT, true), WATERBIRTH_TELEPORT("Waterbirth Teleport", 72, 71, SpriteID.SPELL_WATERBIRTH_TELEPORT, true), + LASSAR_TELEPORT("Lassar Teleport", 72, 82, SpriteID.SPELL_LASSAR_TELEPORT, true), + EXPERT_REANIMATION("Expert Reanimation", 72, 138, SpriteID.SPELL_EXPERT_REANIMATION, true), TELE_GROUP_WATERBIRTH("Tele Group Waterbirth", 73, 72, SpriteID.SPELL_TELE_GROUP_WATERBIRTH, true), ENFEEBLE("Enfeeble", 73, 83, SpriteID.SPELL_ENFEEBLE, true), WARD_OF_ARCEUUS("Ward of Arceuus", 73, 83, SpriteID.SPELL_WARD_OF_ARCEUUS, true), - TELEOTHER_LUMBRIDGE("Teleother Lumbridge", 74, 84, SpriteID.SPELL_TELEOTHER_LUMBRIDGE, true), SMOKE_BLITZ("Smoke Blitz", 74, 42, SpriteID.SPELL_SMOKE_BLITZ, true), CURE_GROUP("Cure Group", 74, 74, SpriteID.SPELL_CURE_GROUP, true), - STAT_SPY("Stat Spy", 75, 76, SpriteID.SPELL_STAT_SPY, true), - BARBARIAN_TELEPORT("Barbarian Teleport", 75, 76, SpriteID.SPELL_BARBARIAN_TELEPORT, true), + TELEOTHER_LUMBRIDGE("Teleother Lumbridge", 74, 84, SpriteID.SPELL_TELEOTHER_LUMBRIDGE, true), FIRE_WAVE("Fire Wave", 75, 42.5f, SpriteID.SPELL_FIRE_WAVE, true), - TELE_GROUP_BARBARIAN("Tele Group Barbarian", 76, 77, SpriteID.SPELL_TELE_GROUP_ICE_PLATEAU, true), + BARBARIAN_TELEPORT("Barbarian Teleport", 75, 76, SpriteID.SPELL_BARBARIAN_TELEPORT, true), + STAT_SPY("Stat Spy", 75, 76, SpriteID.SPELL_STAT_SPY, true), SHADOW_BLITZ("Shadow Blitz", 76, 43, SpriteID.SPELL_SHADOW_BLITZ, true), SPIN_FLAX("Spin Flax", 76, 75, SpriteID.SPELL_SPIN_FLAX, true), + TELE_GROUP_BARBARIAN("Tele Group Barbarian", 76, 77, SpriteID.SPELL_TELE_GROUP_ICE_PLATEAU, true), RESURRECT_GREATER_THRALL("Resurrect Greater Thrall", 76, 88, SpriteID.SPELL_RESURRECT_GREATER_ZOMBIE, true), SUPERGLASS_MAKE("Superglass Make", 77, 78, SpriteID.SPELL_SUPERGLASS_MAKE, true), - TAN_LEATHER("Tan Leather", 78, 81, SpriteID.SPELL_TAN_LEATHER, true), KHAZARD_TELEPORT("Khazard Teleport", 78, 80, SpriteID.SPELL_KHAZARD_TELEPORT, true), + TAN_LEATHER("Tan Leather", 78, 81, SpriteID.SPELL_TAN_LEATHER, true), DAREEYAK_TELEPORT("Dareeyak Teleport", 78, 88, SpriteID.SPELL_DAREEYAK_TELEPORT, true), RESURRECT_CROPS("Resurrect Crops", 78, 90, SpriteID.SPELL_RESURRECT_CROPS, true), - ENTANGLE("Entangle", 79, 89, SpriteID.SPELL_ENTANGLE, true), + UNDEAD_GRASP("Undead Grasp", 79, 46.5f, SpriteID.SPELL_UNDEAD_GRASP, true), TELE_GROUP_KHAZARD("Tele Group Khazard", 79, 81, SpriteID.SPELL_TELE_GROUP_KHAZARD, true), DREAM("Dream", 79, 82, SpriteID.SPELL_DREAM, true), - UNDEAD_GRASP("Undead Grasp", 79, 46.5f, SpriteID.SPELL_UNDEAD_GRASP, true), - CHARGE("Charge", 80, 180, SpriteID.SPELL_CHARGE, true), + ENTANGLE("Entangle", 79, 89, SpriteID.SPELL_ENTANGLE, true), BLOOD_BLITZ("Blood Blitz", 80, 45, SpriteID.SPELL_BLOOD_BLITZ, true), - STUN("Stun", 80, 90, SpriteID.SPELL_STUN, true), STRING_JEWELLERY("String Jewellery", 80, 83, SpriteID.SPELL_STRING_JEWELLERY, true), DEATH_CHARGE("Death Charge", 80, 90, SpriteID.SPELL_DEATH_CHARGE, true), - STAT_RESTORE_POT_SHARE("Stat Restore Pot Share", 81, 84, SpriteID.SPELL_STAT_RESTORE_POT_SHARE, true), + STUN("Stun", 80, 90, SpriteID.SPELL_STUN, true), + CHARGE("Charge", 80, 180, SpriteID.SPELL_CHARGE, true), WIND_SURGE("Wind Surge", 81, 44, SpriteID.SPELL_WIND_SURGE, true), - TELEOTHER_FALADOR("Teleother Falador", 82, 92, SpriteID.SPELL_TELEOTHER_FALADOR, true), - MAGIC_IMBUE("Magic Imbue", 82, 86, SpriteID.SPELL_MAGIC_IMBUE, true), - ICE_BLITZ("Ice Blitz", 82, 46, SpriteID.SPELL_ICE_BLITZ, true), + STAT_RESTORE_POT_SHARE("Stat Restore Pot Share", 81, 84, SpriteID.SPELL_STAT_RESTORE_POT_SHARE, true), DARK_DEMONBANE("Dark Demonbane", 82, 43.5f, SpriteID.SPELL_DARK_DEMONBANE, true), + ICE_BLITZ("Ice Blitz", 82, 46, SpriteID.SPELL_ICE_BLITZ, true), + MAGIC_IMBUE("Magic Imbue", 82, 86, SpriteID.SPELL_MAGIC_IMBUE, true), + TELEOTHER_FALADOR("Teleother Falador", 82, 92, SpriteID.SPELL_TELEOTHER_FALADOR, true), FERTILE_SOIL("Fertile Soil", 83, 87, SpriteID.SPELL_FERTILE_SOIL, true), BARROWS_TELEPORT("Barrows Teleport", 83, 90, SpriteID.SPELL_BARROWS_TELEPORT, true), CARRALLANGER_TELEPORT("Carrallanger Teleport", 84, 82, SpriteID.SPELL_CARRALLANGAR_TELEPORT, true), BOOST_POTION_SHARE("Boost Potion Share", 84, 88, SpriteID.SPELL_BOOST_POTION_SHARE, true), DEMONIC_OFFERING("Demonic Offering", 84, 175, SpriteID.SPELL_DEMONIC_OFFERING, true), + TELEPORT_TO_TARGET("Teleport To Target", 85, 45, SpriteID.SPELL_TELEPORT_TO_BOUNTY_TARGET, true), WATER_SURGE("Water Surge", 85, 46, SpriteID.SPELL_WATER_SURGE, true), - FISHING_GUILD_TELEPORT("Fishing Guild Teleport", 85, 89, SpriteID.SPELL_FISHING_GUILD_TELEPORT, true), TELE_BLOCK("Tele Block", 85, 80, SpriteID.SPELL_TELE_BLOCK, false), - TELEPORT_TO_TARGET("Teleport To Target", 85, 45, SpriteID.SPELL_TELEPORT_TO_BOUNTY_TARGET, true), + FISHING_GUILD_TELEPORT("Fishing Guild Teleport", 85, 89, SpriteID.SPELL_FISHING_GUILD_TELEPORT, true), GREATER_CORRUPTION("Greater Corruption", 85, 95, SpriteID.SPELL_GREATER_CORRUPTION, true), SMOKE_BARRAGE("Smoke Barrage", 86, 48, SpriteID.SPELL_SMOKE_BARRAGE, true), - TELE_GROUP_FISHING_GUILD("Tele Group Fishing Guild", 86, 90, SpriteID.SPELL_TELE_GROUP_FISHING_GUILD, true), PLANK_MAKE("Plank Make", 86, 90, SpriteID.SPELL_PLANK_MAKE, true), + TELE_GROUP_FISHING_GUILD("Tele Group Fishing Guild", 86, 90, SpriteID.SPELL_TELE_GROUP_FISHING_GUILD, true), CATHERBY_TELEPORT("Catherby Teleport", 87, 92, SpriteID.SPELL_CATHERBY_TELEPORT, true), - ENCHANT_ONYX_JEWELLERY("Enchant Onyx Jewellery", 87, 97, SpriteID.SPELL_LVL_6_ENCHANT, true), ENCHANT_ONYX_BOLT("Enchant Onyx Bolt", 87, 97, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), + ENCHANT_ONYX_JEWELLERY("Enchant Onyx Jewellery", 87, 97, SpriteID.SPELL_LVL_6_ENCHANT, true), SHADOW_BARRAGE("Shadow Barrage", 88, 48, SpriteID.SPELL_SHADOW_BARRAGE, true), TELE_GROUP_CATHERBY("Tele Group Catherby", 88, 93, SpriteID.SPELL_TELE_GROUP_CATHERBY, true), ICE_PLATEAU_TELEPORT("Ice Plateau Teleport", 89, 96, SpriteID.SPELL_ICE_PLATEAU_TELEPORT, true), RECHARGE_DRAGONSTONE("Recharge Dragonstone", 89, 97.5f, SpriteID.SPELL_RECHARGE_DRAGONSTONE, true), - ANNAKARL_TELEPORT("Annakarl Teleport", 90, 100, SpriteID.SPELL_ANNAKARL_TELEPORT, true), EARTH_SURGE("Earth Surge", 90, 48, SpriteID.SPELL_EARTH_SURGE, true), - MASTER_REANIMATION("Master Reanimation", 90, 170, SpriteID.SPELL_MASTER_REANIMATION, true), TELE_GROUP_ICE_PLATEAU("Tele Group Ice Plateau", 90, 99, SpriteID.SPELL_TELE_GROUP_ICE_PLATEAU, true), - TELEOTHER_CAMELOT("Teleother Camelot", 90, 100, SpriteID.SPELL_TELEOTHER_CAMELOT, true), + ANNAKARL_TELEPORT("Annakarl Teleport", 90, 100, SpriteID.SPELL_ANNAKARL_TELEPORT, true), APE_ATOLL_TELEPORT("Ape Atoll Teleport", 90, 100, SpriteID.SPELL_APE_ATOLL_TELEPORT, true), + TELEOTHER_CAMELOT("Teleother Camelot", 90, 100, SpriteID.SPELL_TELEOTHER_CAMELOT, true), + MASTER_REANIMATION("Master Reanimation", 90, 170, SpriteID.SPELL_MASTER_REANIMATION, true), ENERGY_TRANSFER("Energy Transfer", 91, 100, SpriteID.SPELL_ENERGY_TRANSFER, true), BLOOD_BARRAGE("Blood Barrage", 92, 51, SpriteID.SPELL_BLOOD_BARRAGE, true), HEAL_OTHER("Heal Other", 92, 101, SpriteID.SPELL_HEAL_OTHER, true), @@ -209,8 +209,8 @@ public enum MagicAction implements SkillAction ENCHANT_ZENYTE_JEWELLERY("Enchant Zenyte Jewellery", 93, 110, SpriteID.SPELL_LVL_7_ENCHANT, true), ICE_BARRAGE("Ice Barrage", 94, 52, SpriteID.SPELL_ICE_BARRAGE, true), VENGEANCE("Vengeance", 94, 112, SpriteID.SPELL_VENGEANCE, true), - HEAL_GROUP("Heal Group", 95, 124, SpriteID.SPELL_HEAL_GROUP, true), FIRE_SURGE("Fire Surge", 95, 51, SpriteID.SPELL_FIRE_SURGE, true), + HEAL_GROUP("Heal Group", 95, 124, SpriteID.SPELL_HEAL_GROUP, true), GHORROCK_TELEPORT("Ghorrock Teleport", 96, 106, SpriteID.SPELL_GHORROCK_TELEPORT, true), SPELLBOOK_SWAP("Spellbook Swap", 96, 130, SpriteID.SPELL_SPELLBOOK_SWAP, true), ; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningAction.java index bd6a71f689..6d3f824ac5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningAction.java @@ -64,15 +64,15 @@ public boolean isMembers(final ItemManager itemManager) SANDSTONE_5KG(ItemID.SANDSTONE_5KG, 35, 50), SANDSTONE_10KG(ItemID.SANDSTONE_10KG, 35, 60), DENSE_ESSENCE_BLOCK(ItemID.DENSE_ESSENCE_BLOCK, 38, 12), - GOLD_ORE(ItemID.GOLD_ORE, 40, 65), GEM_ROCKS(ItemID.UNCUT_RED_TOPAZ, 40, 65) - { - @Override - public String getName(final ItemManager itemManager) { - return "Gem rocks"; - } - }, + @Override + public String getName(final ItemManager itemManager) + { + return "Gem rocks"; + } + }, + GOLD_ORE(ItemID.GOLD_ORE, 40, 65), GRANITE_500G(ItemID.GRANITE_500G, 45, 50), GRANITE_2KG(ItemID.GRANITE_2KG, 45, 60), GRANITE_5KG(ItemID.GRANITE_5KG, 45, 75), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java index 30352c2e1a..62c7236fe4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java @@ -37,7 +37,42 @@ @Getter public enum PrayerAction implements ItemSkillAction { + GUPPY(ItemID.GUPPY, 1, 4, PrayerMethod.PREPARED_FISH), + BONES(ItemID.BONES, 1, 4.5f, PrayerMethod.BONES), + BURNT_BONES(ItemID.BURNT_BONES, 1, 4.5f, PrayerMethod.BONES), + WOLF_BONES(ItemID.WOLF_BONES, 1, 4.5f, PrayerMethod.BONES), + MONKEY_BONES(ItemID.MONKEY_BONES, 1, 5, PrayerMethod.BONES), + BAT_BONES(ItemID.BAT_BONES, 1, 5.3f, PrayerMethod.BONES), + CAVEFISH(ItemID.CAVEFISH, 1, 7, PrayerMethod.PREPARED_FISH), + FIENDISH_ASHES(ItemID.FIENDISH_ASHES, 1, 10, PrayerMethod.DEMONIC_ASHES), + TETRA(ItemID.TETRA, 1, 10, PrayerMethod.PREPARED_FISH), + BIG_BONES(ItemID.BIG_BONES, 1, 15, PrayerMethod.BONES), + JOGRE_BONES(ItemID.JOGRE_BONES, 1, 15, PrayerMethod.BONES), + CATFISH(ItemID.CATFISH, 1, 16, PrayerMethod.PREPARED_FISH), + ZOGRE_BONES(ItemID.ZOGRE_BONES, 1, 22.5f, PrayerMethod.BONES), + SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, 1, 25, PrayerMethod.BONES), + VILE_ASHES(ItemID.VILE_ASHES, 1, 25, PrayerMethod.DEMONIC_ASHES), + BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, 1, 30, PrayerMethod.BONES), + LOAR_REMAINS(ItemID.LOAR_REMAINS, 1, 33, PrayerMethod.SHADE_REMAINS), + PHRIN_REMAINS(ItemID.PHRIN_REMAINS, 1, 46.5f, PrayerMethod.SHADE_REMAINS), + WYRM_BONES(ItemID.WYRM_BONES, 1, 50, PrayerMethod.BONES), + RIYL_REMAINS(ItemID.RIYL_REMAINS, 1, 59.5f, PrayerMethod.SHADE_REMAINS), + MALICIOUS_ASHES(ItemID.MALICIOUS_ASHES, 1, 65, PrayerMethod.DEMONIC_ASHES), + DRAGON_BONES(ItemID.DRAGON_BONES, 1, 72, PrayerMethod.BONES), + WYVERN_BONES(ItemID.WYVERN_BONES, 1, 72, PrayerMethod.BONES), + DRAKE_BONES(ItemID.DRAKE_BONES, 1, 80, PrayerMethod.BONES), + ASYN_REMAINS(ItemID.ASYN_REMAINS, 1, 82.5f, PrayerMethod.SHADE_REMAINS), + FAYRG_BONES(ItemID.FAYRG_BONES, 1, 84, PrayerMethod.BONES), + FIYR_REMAINS(ItemID.FIYR_REMAINS, 1, 84, PrayerMethod.SHADE_REMAINS), + ABYSSAL_ASHES(ItemID.ABYSSAL_ASHES, 1, 85, PrayerMethod.DEMONIC_ASHES), + LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, 1, 85, PrayerMethod.BONES), + RAURG_BONES(ItemID.RAURG_BONES, 1, 96, PrayerMethod.BONES), + HYDRA_BONES(ItemID.HYDRA_BONES, 1, 110, PrayerMethod.BONES), + INFERNAL_ASHES(ItemID.INFERNAL_ASHES, 1, 110, PrayerMethod.DEMONIC_ASHES), + URIUM_REMAINS(ItemID.URIUM_REMAINS, 1, 120, PrayerMethod.SHADE_REMAINS), + DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, 1, 125, PrayerMethod.BONES), ENSOULED_GOBLIN_HEAD(ItemID.ENSOULED_GOBLIN_HEAD, 1, 130, PrayerMethod.ENSOULED_HEAD), + OURG_BONES(ItemID.OURG_BONES, 1, 140, PrayerMethod.BONES), ENSOULED_MONKEY_HEAD(ItemID.ENSOULED_MONKEY_HEAD, 1, 182, PrayerMethod.ENSOULED_HEAD), ENSOULED_IMP_HEAD(ItemID.ENSOULED_IMP_HEAD, 1, 286, PrayerMethod.ENSOULED_HEAD), ENSOULED_MINOTAUR_HEAD(ItemID.ENSOULED_MINOTAUR_HEAD, 1, 364, PrayerMethod.ENSOULED_HEAD), @@ -60,41 +95,6 @@ public enum PrayerAction implements ItemSkillAction ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD, 1, 1234, PrayerMethod.ENSOULED_HEAD), ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD, 1, 1300, PrayerMethod.ENSOULED_HEAD), ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD, 1, 1560, PrayerMethod.ENSOULED_HEAD), - FIENDISH_ASHES(ItemID.FIENDISH_ASHES, 1, 10, PrayerMethod.DEMONIC_ASHES), - VILE_ASHES(ItemID.VILE_ASHES, 1, 25, PrayerMethod.DEMONIC_ASHES), - MALICIOUS_ASHES(ItemID.MALICIOUS_ASHES, 1, 65, PrayerMethod.DEMONIC_ASHES), - ABYSSAL_ASHES(ItemID.ABYSSAL_ASHES, 1, 85, PrayerMethod.DEMONIC_ASHES), - INFERNAL_ASHES(ItemID.INFERNAL_ASHES, 1, 110, PrayerMethod.DEMONIC_ASHES), - BONES(ItemID.BONES, 1, 4.5f, PrayerMethod.BONES), - WOLF_BONES(ItemID.WOLF_BONES, 1, 4.5f, PrayerMethod.BONES), - LOAR_REMAINS(ItemID.LOAR_REMAINS, 1, 33, PrayerMethod.SHADE_REMAINS), - BURNT_BONES(ItemID.BURNT_BONES, 1, 4.5f, PrayerMethod.BONES), - MONKEY_BONES(ItemID.MONKEY_BONES, 1, 5, PrayerMethod.BONES), - BAT_BONES(ItemID.BAT_BONES, 1, 5.3f, PrayerMethod.BONES), - JOGRE_BONES(ItemID.JOGRE_BONES, 1, 15, PrayerMethod.BONES), - BIG_BONES(ItemID.BIG_BONES, 1, 15, PrayerMethod.BONES), - ZOGRE_BONES(ItemID.ZOGRE_BONES, 1, 22.5f, PrayerMethod.BONES), - SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, 1, 25, PrayerMethod.BONES), - BABYDRAGON_BONES(ItemID.BABYDRAGON_BONES, 1, 30, PrayerMethod.BONES), - PHRIN_REMAINS(ItemID.PHRIN_REMAINS, 1, 46.5f, PrayerMethod.SHADE_REMAINS), - WYRM_BONES(ItemID.WYRM_BONES, 1, 50, PrayerMethod.BONES), - RIYL_REMAINS(ItemID.RIYL_REMAINS, 1, 59.5f, PrayerMethod.SHADE_REMAINS), - WYVERN_BONES(ItemID.WYVERN_BONES, 1, 72, PrayerMethod.BONES), - DRAGON_BONES(ItemID.DRAGON_BONES, 1, 72, PrayerMethod.BONES), - DRAKE_BONES(ItemID.DRAKE_BONES, 1, 80, PrayerMethod.BONES), - ASYN_REMAINS(ItemID.ASYN_REMAINS, 1, 82.5f, PrayerMethod.SHADE_REMAINS), - FAYRG_BONES(ItemID.FAYRG_BONES, 1, 84, PrayerMethod.BONES), - FIYR_REMAINS(ItemID.FIYR_REMAINS, 1, 84, PrayerMethod.SHADE_REMAINS), - LAVA_DRAGON_BONES(ItemID.LAVA_DRAGON_BONES, 1, 85, PrayerMethod.BONES), - RAURG_BONES(ItemID.RAURG_BONES, 1, 96, PrayerMethod.BONES), - HYDRA_BONES(ItemID.HYDRA_BONES, 1, 110, PrayerMethod.BONES), - DAGANNOTH_BONES(ItemID.DAGANNOTH_BONES, 1, 125, PrayerMethod.BONES), - OURG_BONES(ItemID.OURG_BONES, 1, 140, PrayerMethod.BONES), - URIUM_REMAINS(ItemID.URIUM_REMAINS, 1, 120, PrayerMethod.SHADE_REMAINS), - GUPPY(ItemID.GUPPY, 1, 4, PrayerMethod.PREPARED_FISH), - CAVEFISH(ItemID.CAVEFISH, 1, 7, PrayerMethod.PREPARED_FISH), - TETRA(ItemID.TETRA, 1, 10, PrayerMethod.PREPARED_FISH), - CATFISH(ItemID.CATFISH, 1, 16, PrayerMethod.PREPARED_FISH), SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, 70, 150, PrayerMethod.BONES), ; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java index 7d28c26e9c..c46e7cb87d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java @@ -36,6 +36,7 @@ @Getter public enum RunecraftAction implements ItemSkillAction { + AIR_RUNE(ItemID.AIR_RUNE, 1, 5, false), AIR_TIARA(ItemID.AIR_TIARA, 1, 25, true), MIND_TIARA(ItemID.MIND_TIARA, 1, 27.5f, true), WATER_TIARA(ItemID.WATER_TIARA, 1, 30, true), @@ -48,7 +49,6 @@ public enum RunecraftAction implements ItemSkillAction LAW_TIARA(ItemID.LAW_TIARA, 1, 47.5f, true), DEATH_TIARA(ItemID.DEATH_TIARA, 1, 50, true), WRATH_TIARA(ItemID.WRATH_TIARA, 1, 52.5f, true), - AIR_RUNE(ItemID.AIR_RUNE, 1, 5, false), MIND_RUNE(ItemID.MIND_RUNE, 2, 5.5f, false), MIND_CORE(ItemID.MIND_CORE, 2, 55, true), WATER_RUNE(ItemID.WATER_RUNE, 5, 6, false), @@ -69,20 +69,20 @@ public enum RunecraftAction implements ItemSkillAction NATURE_RUNE(ItemID.NATURE_RUNE, 44, 9, false, true), LAW_RUNE(ItemID.LAW_RUNE, 54, 9.5f, false, true), DEATH_RUNE(ItemID.DEATH_RUNE, 65, 10, false, true), - ZEAH_BLOOD_RUNE(ItemID.BLOOD_RUNE, 77, 24.425f, true) + TRUE_BLOOD_RUNE(ItemID.BLOOD_RUNE, 77, 10.5f, false) { @Override public String getName(final ItemManager itemManager) { - return "Blood rune (Zeah)"; + return "Blood rune (True Altar)"; } }, - TRUE_BLOOD_RUNE(ItemID.BLOOD_RUNE, 77, 10.5f, false) + ZEAH_BLOOD_RUNE(ItemID.BLOOD_RUNE, 77, 24.425f, true) { @Override public String getName(final ItemManager itemManager) { - return "Blood rune (True Altar)"; + return "Blood rune (Zeah)"; } }, SOUL_RUNE(ItemID.SOUL_RUNE, 90, 30.325f, true), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingAction.java index 46275cc703..e9c403c4a8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingAction.java @@ -37,19 +37,19 @@ public enum SmithingAction implements ItemSkillAction BRONZE_AXE(ItemID.BRONZE_AXE, 1, 12.5f), BRONZE_DAGGER(ItemID.BRONZE_DAGGER, 1, 12.5f), BRONZE_MACE(ItemID.BRONZE_MACE, 2, 12.5f), - BRONZE_MED_HELM(ItemID.BRONZE_MED_HELM, 3, 12.5f), BRONZE_BOLTS_UNF(ItemID.BRONZE_BOLTS_UNF, 3, 12.5f), + BRONZE_MED_HELM(ItemID.BRONZE_MED_HELM, 3, 12.5f), + BRONZE_DART_TIP(ItemID.BRONZE_DART_TIP, 4, 12.5f), BRONZE_NAILS(ItemID.BRONZE_NAILS, 4, 12.5f), BRONZE_SWORD(ItemID.BRONZE_SWORD, 4, 12.5f), BRONZE_WIRE(ItemID.BRONZE_WIRE, 4, 12.5f), - BRONZE_DART_TIP(ItemID.BRONZE_DART_TIP, 4, 12.5f), BRONZE_ARROWTIPS(ItemID.BRONZE_ARROWTIPS, 5, 12.5f), - BRONZE_SCIMITAR(ItemID.BRONZE_SCIMITAR, 5, 25), BRONZE_HASTA(ItemID.BRONZE_HASTA, 5, 25), + BRONZE_SCIMITAR(ItemID.BRONZE_SCIMITAR, 5, 25), BRONZE_SPEAR(ItemID.BRONZE_SPEAR, 5, 25), BRONZE_JAVELIN_HEADS(ItemID.BRONZE_JAVELIN_HEADS, 6, 12.5f), - BRONZE_LONGSWORD(ItemID.BRONZE_LONGSWORD, 6, 25), BRONZE_LIMBS(ItemID.BRONZE_LIMBS, 6, 12.5f), + BRONZE_LONGSWORD(ItemID.BRONZE_LONGSWORD, 6, 25), BRONZE_KNIFE(ItemID.BRONZE_KNIFE, 7, 12.5f), BRONZE_FULL_HELM(ItemID.BRONZE_FULL_HELM, 7, 25), BRONZE_SQ_SHIELD(ItemID.BRONZE_SQ_SHIELD, 8, 25), @@ -58,30 +58,30 @@ public enum SmithingAction implements ItemSkillAction BRONZE_CHAINBODY(ItemID.BRONZE_CHAINBODY, 11, 37.5f), BRONZE_KITESHIELD(ItemID.BRONZE_KITESHIELD, 12, 37.5f), BRONZE_CLAWS(ItemID.BRONZE_CLAWS, 13, 25), - BRONZE_2H_SWORD(ItemID.BRONZE_2H_SWORD, 14, 37.5f), BARRONITE_DEPOSITS(ItemID.BARRONITE_DEPOSIT, 14, 30), + BRONZE_2H_SWORD(ItemID.BRONZE_2H_SWORD, 14, 37.5f), IRON_BAR(ItemID.IRON_BAR, 15, 12.5f), IRON_DAGGER(ItemID.IRON_DAGGER, 15, 25), IRON_AXE(ItemID.IRON_AXE, 16, 25), BRONZE_PLATELEGS(ItemID.BRONZE_PLATELEGS, 16, 37.5f), BRONZE_PLATESKIRT(ItemID.BRONZE_PLATESKIRT, 16, 37.5f), - IRON_SPIT(ItemID.IRON_SPIT, 17, 25), IRON_MACE(ItemID.IRON_MACE, 17, 25), + IRON_SPIT(ItemID.IRON_SPIT, 17, 25), IRON_BOLTS_UNF(ItemID.IRON_BOLTS_UNF, 18, 25), - BRONZE_PLATEBODY(ItemID.BRONZE_PLATEBODY, 18, 62.5f), IRON_MED_HELM(ItemID.IRON_MED_HELM, 18, 25), - IRON_NAILS(ItemID.IRON_NAILS, 19, 25), + BRONZE_PLATEBODY(ItemID.BRONZE_PLATEBODY, 18, 62.5f), IRON_DART_TIP(ItemID.IRON_DART_TIP, 19, 25), + IRON_NAILS(ItemID.IRON_NAILS, 19, 25), IRON_SWORD(ItemID.IRON_SWORD, 19, 25), SILVER_BAR(ItemID.SILVER_BAR, 20, 13.7f), IRON_ARROWTIPS(ItemID.IRON_ARROWTIPS, 20, 25), - IRON_SCIMITAR(ItemID.IRON_SCIMITAR, 20, 50), IRON_HASTA(ItemID.IRON_HASTA, 20, 50), + IRON_SCIMITAR(ItemID.IRON_SCIMITAR, 20, 50), IRON_SPEAR(ItemID.IRON_SPEAR, 20, 50), - IRON_LONGSWORD(ItemID.IRON_LONGSWORD, 21, 50), IRON_JAVELIN_HEADS(ItemID.IRON_JAVELIN_HEADS, 21, 25), - IRON_FULL_HELM(ItemID.IRON_FULL_HELM, 22, 50), + IRON_LONGSWORD(ItemID.IRON_LONGSWORD, 21, 50), IRON_KNIFE(ItemID.IRON_KNIFE, 22, 25), + IRON_FULL_HELM(ItemID.IRON_FULL_HELM, 22, 50), IRON_LIMBS(ItemID.IRON_LIMBS, 23, 25), IRON_SQ_SHIELD(ItemID.IRON_SQ_SHIELD, 23, 50), IRON_WARHAMMER(ItemID.IRON_WARHAMMER, 24, 75), @@ -91,32 +91,32 @@ public enum SmithingAction implements ItemSkillAction IRON_KITESHIELD(ItemID.IRON_KITESHIELD, 27, 75), IRON_CLAWS(ItemID.IRON_CLAWS, 28, 50), IRON_2H_SWORD(ItemID.IRON_2H_SWORD, 29, 75), - STEEL_DAGGER(ItemID.STEEL_DAGGER, 30, 37.5f), STEEL_BAR(ItemID.STEEL_BAR, 30, 17.5f), - IRON_PLATESKIRT(ItemID.IRON_PLATESKIRT, 31, 75), - IRON_PLATELEGS(ItemID.IRON_PLATELEGS, 31, 75), + STEEL_DAGGER(ItemID.STEEL_DAGGER, 30, 37.5f), STEEL_AXE(ItemID.STEEL_AXE, 31, 37.5f), + IRON_PLATELEGS(ItemID.IRON_PLATELEGS, 31, 75), + IRON_PLATESKIRT(ItemID.IRON_PLATESKIRT, 31, 75), STEEL_MACE(ItemID.STEEL_MACE, 32, 37.5f), - IRON_PLATEBODY(ItemID.IRON_PLATEBODY, 33, 125), - STEEL_MED_HELM(ItemID.STEEL_MED_HELM, 33, 37.5f), STEEL_BOLTS_UNF(ItemID.STEEL_BOLTS_UNF, 33, 37.5f), + STEEL_MED_HELM(ItemID.STEEL_MED_HELM, 33, 37.5f), + IRON_PLATEBODY(ItemID.IRON_PLATEBODY, 33, 125), STEEL_DART_TIP(ItemID.STEEL_DART_TIP, 34, 37.5f), STEEL_NAILS(ItemID.STEEL_NAILS, 34, 37.5f), STEEL_SWORD(ItemID.STEEL_SWORD, 34, 37.5f), CANNONBALL(ItemID.CANNONBALL, 35, 25.6f), - STEEL_SCIMITAR(ItemID.STEEL_SCIMITAR, 35, 75), STEEL_ARROWTIPS(ItemID.STEEL_ARROWTIPS, 35, 37.5f), STEEL_HASTA(ItemID.STEEL_HASTA, 35, 75), + STEEL_SCIMITAR(ItemID.STEEL_SCIMITAR, 35, 75), STEEL_SPEAR(ItemID.STEEL_SPEAR, 35, 75), + STEEL_JAVELIN_HEADS(ItemID.STEEL_JAVELIN_HEADS, 36, 37.5f), STEEL_LIMBS(ItemID.STEEL_LIMBS, 36, 37.5f), STEEL_STUDS(ItemID.STEEL_STUDS, 36, 37.5f), STEEL_LONGSWORD(ItemID.STEEL_LONGSWORD, 36, 75), - STEEL_JAVELIN_HEADS(ItemID.STEEL_JAVELIN_HEADS, 36, 37.5f), STEEL_KNIFE(ItemID.STEEL_KNIFE, 37, 37.5f), STEEL_FULL_HELM(ItemID.STEEL_FULL_HELM, 37, 75), STEEL_SQ_SHIELD(ItemID.STEEL_SQ_SHIELD, 38, 75), STEEL_WARHAMMER(ItemID.STEEL_WARHAMMER, 39, 112.5f), - STEEL_BATTLEAXE(ItemID.STEEL_BATTLEAXE, 40, 112.5f), + GOLD_BAR(ItemID.GOLD_BAR, 40, 22.5f), GOLD_BAR_GOLDSMITH_GAUNTLETS(ItemID.GOLD_BAR, 40, 56.2f) { @Override @@ -131,7 +131,7 @@ public boolean isMembers(final ItemManager itemManager) return true; } }, - GOLD_BAR(ItemID.GOLD_BAR, 40, 22.5f), + STEEL_BATTLEAXE(ItemID.STEEL_BATTLEAXE, 40, 112.5f), STEEL_CHAINBODY(ItemID.STEEL_CHAINBODY, 41, 112.5f), STEEL_KITESHIELD(ItemID.STEEL_KITESHIELD, 42, 112.5f), STEEL_CLAWS(ItemID.STEEL_CLAWS, 43, 75), @@ -140,24 +140,24 @@ public boolean isMembers(final ItemManager itemManager) STEEL_PLATESKIRT(ItemID.STEEL_PLATESKIRT, 46, 112.5f), STEEL_PLATEBODY(ItemID.STEEL_PLATEBODY, 48, 187.5f), BULLSEYE_LANTERN_UNF(ItemID.BULLSEYE_LANTERN_UNF, 49, 37), - MITHRIL_DAGGER(ItemID.MITHRIL_DAGGER, 50, 50), MITHRIL_BAR(ItemID.MITHRIL_BAR, 50, 30), + MITHRIL_DAGGER(ItemID.MITHRIL_DAGGER, 50, 50), MITHRIL_AXE(ItemID.MITHRIL_AXE, 51, 50), MITHRIL_MACE(ItemID.MITHRIL_MACE, 52, 50), - MITHRIL_MED_HELM(ItemID.MITHRIL_MED_HELM, 53, 50), MITHRIL_BOLTS_UNF(ItemID.MITHRIL_BOLTS_UNF, 53, 50), - MITHRIL_SWORD(ItemID.MITHRIL_SWORD, 54, 50), + MITHRIL_MED_HELM(ItemID.MITHRIL_MED_HELM, 53, 50), MITHRIL_DART_TIP(ItemID.MITHRIL_DART_TIP, 54, 50), MITHRIL_NAILS(ItemID.MITHRIL_NAILS, 54, 50), + MITHRIL_SWORD(ItemID.MITHRIL_SWORD, 54, 50), MITHRIL_ARROWTIPS(ItemID.MITHRIL_ARROWTIPS, 55, 50), - MITHRIL_SCIMITAR(ItemID.MITHRIL_SCIMITAR, 55, 100), MITHRIL_HASTA(ItemID.MITHRIL_HASTA, 55, 100), + MITHRIL_SCIMITAR(ItemID.MITHRIL_SCIMITAR, 55, 100), MITHRIL_SPEAR(ItemID.MITHRIL_SPEAR, 55, 100), - MITHRIL_LONGSWORD(ItemID.MITHRIL_LONGSWORD, 56, 100), MITHRIL_JAVELIN_HEADS(ItemID.MITHRIL_JAVELIN_HEADS, 56, 50), MITHRIL_LIMBS(ItemID.MITHRIL_LIMBS, 56, 50), - MITHRIL_FULL_HELM(ItemID.MITHRIL_FULL_HELM, 57, 100), + MITHRIL_LONGSWORD(ItemID.MITHRIL_LONGSWORD, 56, 100), MITHRIL_KNIFE(ItemID.MITHRIL_KNIFE, 57, 50), + MITHRIL_FULL_HELM(ItemID.MITHRIL_FULL_HELM, 57, 100), MITHRIL_SQ_SHIELD(ItemID.MITHRIL_SQ_SHIELD, 58, 100), MITH_GRAPPLE_TIP(ItemID.MITH_GRAPPLE_TIP, 59, 50), MITHRIL_WARHAMMER(ItemID.MITHRIL_WARHAMMER, 59, 150), @@ -167,27 +167,27 @@ public boolean isMembers(final ItemManager itemManager) MITHRIL_KITESHIELD(ItemID.MITHRIL_KITESHIELD, 62, 150), MITHRIL_CLAWS(ItemID.MITHRIL_CLAWS, 63, 100), MITHRIL_2H_SWORD(ItemID.MITHRIL_2H_SWORD, 64, 150), - MITHRIL_PLATESKIRT(ItemID.MITHRIL_PLATESKIRT, 66, 150), MITHRIL_PLATELEGS(ItemID.MITHRIL_PLATELEGS, 66, 150), + MITHRIL_PLATESKIRT(ItemID.MITHRIL_PLATESKIRT, 66, 150), MITHRIL_PLATEBODY(ItemID.MITHRIL_PLATEBODY, 68, 250), - ADAMANT_DAGGER(ItemID.ADAMANT_DAGGER, 70, 62.5f), ADAMANTITE_BAR(ItemID.ADAMANTITE_BAR, 70, 37.5f), + ADAMANT_DAGGER(ItemID.ADAMANT_DAGGER, 70, 62.5f), ADAMANT_AXE(ItemID.ADAMANT_AXE, 71, 62.5f), ADAMANT_MACE(ItemID.ADAMANT_MACE, 72, 62.5f), ADAMANT_BOLTS_UNF(ItemID.ADAMANT_BOLTSUNF, 73, 62.5f), ADAMANT_MED_HELM(ItemID.ADAMANT_MED_HELM, 73, 62.5f), + ADAMANTITE_NAILS(ItemID.ADAMANTITE_NAILS, 74, 62.5f), ADAMANT_DART_TIP(ItemID.ADAMANT_DART_TIP, 74, 62.5f), ADAMANT_SWORD(ItemID.ADAMANT_SWORD, 74, 62.5f), - ADAMANTITE_NAILS(ItemID.ADAMANTITE_NAILS, 74, 62.5f), ADAMANT_ARROWTIPS(ItemID.ADAMANT_ARROWTIPS, 75, 62.5f), - ADAMANT_SCIMITAR(ItemID.ADAMANT_SCIMITAR, 75, 125), ADAMANT_HASTA(ItemID.ADAMANT_HASTA, 75, 125), + ADAMANT_SCIMITAR(ItemID.ADAMANT_SCIMITAR, 75, 125), ADAMANT_SPEAR(ItemID.ADAMANT_SPEAR, 75, 125), ADAMANTITE_LIMBS(ItemID.ADAMANTITE_LIMBS, 76, 62.5f), - ADAMANT_LONGSWORD(ItemID.ADAMANT_LONGSWORD, 76, 125), ADAMANT_JAVELIN_HEADS(ItemID.ADAMANT_JAVELIN_HEADS, 76, 62.5f), - ADAMANT_FULL_HELM(ItemID.ADAMANT_FULL_HELM, 77, 125), + ADAMANT_LONGSWORD(ItemID.ADAMANT_LONGSWORD, 76, 125), ADAMANT_KNIFE(ItemID.ADAMANT_KNIFE, 77, 62.5f), + ADAMANT_FULL_HELM(ItemID.ADAMANT_FULL_HELM, 77, 125), ADAMANT_SQ_SHIELD(ItemID.ADAMANT_SQ_SHIELD, 78, 125), ADAMANT_WARHAMMER(ItemID.ADAMANT_WARHAMMER, 79, 187.5f), ADAMANT_BATTLEAXE(ItemID.ADAMANT_BATTLEAXE, 80, 187.5f), @@ -198,23 +198,23 @@ public boolean isMembers(final ItemManager itemManager) RUNITE_BAR(ItemID.RUNITE_BAR, 85, 50), RUNE_DAGGER(ItemID.RUNE_DAGGER, 85, 75), RUNE_AXE(ItemID.RUNE_AXE, 86, 75), - ADAMANT_PLATESKIRT(ItemID.ADAMANT_PLATESKIRT, 86, 187.5f), ADAMANT_PLATELEGS(ItemID.ADAMANT_PLATELEGS, 86, 187.5f), + ADAMANT_PLATESKIRT(ItemID.ADAMANT_PLATESKIRT, 86, 187.5f), RUNE_MACE(ItemID.RUNE_MACE, 87, 75), - RUNITE_BOLTS_UNF(ItemID.RUNITE_BOLTS_UNF, 88, 75), RUNE_MED_HELM(ItemID.RUNE_MED_HELM, 88, 75), + RUNITE_BOLTS_UNF(ItemID.RUNITE_BOLTS_UNF, 88, 75), ADAMANT_PLATEBODY(ItemID.ADAMANT_PLATEBODY, 88, 312.5f), - RUNE_SWORD(ItemID.RUNE_SWORD, 89, 75), - RUNE_NAILS(ItemID.RUNE_NAILS, 89, 75), RUNE_DART_TIP(ItemID.RUNE_DART_TIP, 89, 75), + RUNE_NAILS(ItemID.RUNE_NAILS, 89, 75), + RUNE_SWORD(ItemID.RUNE_SWORD, 89, 75), RUNE_ARROWTIPS(ItemID.RUNE_ARROWTIPS, 90, 75), - RUNE_SCIMITAR(ItemID.RUNE_SCIMITAR, 90, 150), RUNE_HASTA(ItemID.RUNE_HASTA, 90, 150), + RUNE_SCIMITAR(ItemID.RUNE_SCIMITAR, 90, 150), RUNE_SPEAR(ItemID.RUNE_SPEAR, 90, 150), DRAGONFIRE_SHIELD(ItemID.DRAGONFIRE_SHIELD, 90, 2000), - RUNE_LONGSWORD(ItemID.RUNE_LONGSWORD, 91, 150), RUNE_JAVELIN_HEADS(ItemID.RUNE_JAVELIN_HEADS, 91, 75), RUNITE_LIMBS(ItemID.RUNITE_LIMBS, 91, 75), + RUNE_LONGSWORD(ItemID.RUNE_LONGSWORD, 91, 150), RUNE_KNIFE(ItemID.RUNE_KNIFE, 92, 75), RUNE_FULL_HELM(ItemID.RUNE_FULL_HELM, 92, 150), RUNE_SQ_SHIELD(ItemID.RUNE_SQ_SHIELD, 93, 150), @@ -223,10 +223,10 @@ public boolean isMembers(final ItemManager itemManager) RUNE_CHAINBODY(ItemID.RUNE_CHAINBODY, 96, 225), RUNE_KITESHIELD(ItemID.RUNE_KITESHIELD, 97, 225), RUNE_CLAWS(ItemID.RUNE_CLAWS, 98, 150), - RUNE_PLATEBODY(ItemID.RUNE_PLATEBODY, 99, 375), - RUNE_PLATESKIRT(ItemID.RUNE_PLATESKIRT, 99, 225), - RUNE_PLATELEGS(ItemID.RUNE_PLATELEGS, 99, 225), RUNE_2H_SWORD(ItemID.RUNE_2H_SWORD, 99, 225), + RUNE_PLATELEGS(ItemID.RUNE_PLATELEGS, 99, 225), + RUNE_PLATESKIRT(ItemID.RUNE_PLATESKIRT, 99, 225), + RUNE_PLATEBODY(ItemID.RUNE_PLATEBODY, 99, 375), ; private final int itemId; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java index c048339bf8..dfd26e1b3d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java @@ -37,9 +37,9 @@ public enum ThievingAction implements NamedSkillAction WINTER_SQIRKJUICE("Winter Sq'irkjuice", 1, 350, ItemID.WINTER_SQIRKJUICE), VEGETABLE_STALL("Vegetable Stall", 2, 10, ItemID.CABBAGE), CAKE_STALL("Cake Stall", 5, 16, ItemID.CAKE), - TEA_STALL("Tea Stall", 5, 16, ItemID.CUP_OF_TEA_4242), CRAFTING_STALL("Crafting Stall", 5, 16, ItemID.CHISEL_5601), MONKEY_FOOD_STALL("Monkey Food Stall", 5, 16, ItemID.BANANA), + TEA_STALL("Tea Stall", 5, 16, ItemID.CUP_OF_TEA_4242), FARMER("Farmer", 10, 14.5f, ItemID.FARMER), HAM_MEMBER("H.A.M. Member", 15, 22.2f, ItemID.MALE_HAM), SILK_STALL("Silk Stall", 20, 24, ItemID.SILK), @@ -65,15 +65,15 @@ public enum ThievingAction implements NamedSkillAction KNIGHT("Knight", 55, 84.3f, ItemID.KNIGHT), POLLNIVNIAN_BANDIT("Pollnivnian Bandit", 55, 84.3f, ItemID.BANDIT_6781), STONE_CHEST("Stone Chest", 64, 280, ItemID.XERICIAN_FABRIC), + SPICES_STALL("Spices Stall", 65, 81, ItemID.SPICE), MAGIC_STALL("Magic Stall", 65, 100, ItemID.AIR_RUNE_6422), SCIMITAR_STALL("Scimitar Stall", 65, 100, ItemID.STEEL_SCIMITAR), MENAPHITE_THUG("Menaphite Thug", 65, 137.5f, ItemID.MENAPHITE_THUG), - SPICES_STALL("Spices Stall", 65, 81, ItemID.SPICE), YANILLE_WATCHMAN("Yanille Watchman", 65, 137.5f, ItemID.WATCHMAN), SUMMER_SQIRKJUICE("Summer Sq'irkjuice", 65, 3000, ItemID.SUMMER_SQIRKJUICE), PALADIN("Paladin", 70, 151.8f, ItemID.PALADIN), - GNOME("Gnome", 75, 198.5f, ItemID.GNOME), GEMS_STALL("Gems Stall", 75, 160, ItemID.SAPPHIRE), + GNOME("Gnome", 75, 198.5f, ItemID.GNOME), DORGESH_KAAN_RICH_CHEST("Dorgesh-Kaan Rich Chest", 78, 650, ItemID.MINING_HELMET), HERO("Hero", 80, 275, ItemID.HERO), VYRE("Vyre", 82, 306.9f, ItemID.VYRE), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingAction.java index c3e7d56ee3..f6eaf87f83 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingAction.java @@ -33,8 +33,8 @@ @Getter public enum WoodcuttingAction implements ItemSkillAction { - LOGS(ItemID.LOGS, 1, 25), ACHEY_TREE_LOGS(ItemID.ACHEY_TREE_LOGS, 1, 25), + LOGS(ItemID.LOGS, 1, 25), OAK_LOGS(ItemID.OAK_LOGS, 15, 37.5f), WILLOW_LOGS(ItemID.WILLOW_LOGS, 30, 67.5f), TEAK_LOGS(ItemID.TEAK_LOGS, 35, 85), diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/CalculatorTypeTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/CalculatorTypeTest.java index df5ee04041..aef2e1b068 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/CalculatorTypeTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/CalculatorTypeTest.java @@ -30,21 +30,42 @@ public class CalculatorTypeTest { + @Test - public void skillActionsInLevelOrder() + public void skillActionsInOrder() { for (final CalculatorType calculatorType : CalculatorType.values()) { - int level = 1; + final String skillName = calculatorType.getSkill().getName(); + int prevLevel = 0; + float prevXP = 0.0f; + String prevName = ""; for (final SkillAction skillAction : calculatorType.getSkillActions()) { - if (skillAction.getLevel() < level) + int currentLevel = skillAction.getLevel(); + float currentXP = skillAction.getXp(); + String currentName = skillAction.toString(); + + if (currentLevel < prevLevel) + { + fail(skillName + " skill action " + skillAction + " is not ordered by level."); + } + else if (currentLevel == prevLevel) { - fail("Skill action " + skillAction + " is out of order for " + calculatorType.getSkill().getName()); + if (currentXP < prevXP) + { + fail(skillName + " skill action " + skillAction + " is not ordered by xp among level " + currentLevel + " actions."); + } + else if (currentXP == prevXP && currentName.compareTo(prevName) < 0) + { + fail(skillName + " skill action " + skillAction + " is not ordered alphabetically among " + currentXP + "xp skills at level " + currentLevel + '.'); + } } - level = skillAction.getLevel(); + prevLevel = currentLevel; + prevXP = currentXP; + prevName = currentName; } } } From ec5fb1636fa580c77bf29d817634bd3c1ff659e8 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Thu, 11 Apr 2024 23:26:57 -0700 Subject: [PATCH 28/67] skill calculator: Add test for mutually-stacking bonuses --- .../skillcalculator/CalculatorTypeTest.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/CalculatorTypeTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/CalculatorTypeTest.java index aef2e1b068..355aa3fa8f 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/CalculatorTypeTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/CalculatorTypeTest.java @@ -25,6 +25,7 @@ package net.runelite.client.plugins.skillcalculator; import net.runelite.client.plugins.skillcalculator.skills.SkillAction; +import net.runelite.client.plugins.skillcalculator.skills.SkillBonus; import static org.junit.Assert.fail; import org.junit.Test; @@ -69,4 +70,30 @@ else if (currentXP == prevXP && currentName.compareTo(prevName) < 0) } } } + + @Test + public void testSkillBonusesMutuallyStack() + { + for (final CalculatorType calculatorType : CalculatorType.values()) + { + final SkillBonus[] skillBonuses = calculatorType.getSkillBonuses(); + if (skillBonuses == null) + { + continue; + } + + final String skillName = calculatorType.getSkill().getName(); + + for (final SkillBonus skillBonus : skillBonuses) + { + for (final SkillBonus stackedSkillBonus : skillBonus.getCanBeStackedWith()) + { + if (!stackedSkillBonus.getCanBeStackedWith().contains(skillBonus)) + { + fail(skillName + " skill bonus " + skillBonus + " is not mutually stacked with skill bonus " + stackedSkillBonus); + } + } + } + } + } } From 32bbf4ae7f6277a3dc66f5b955cb45533e8fa45f Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Thu, 11 Apr 2024 22:04:51 -0700 Subject: [PATCH 29/67] skill calculator: Fix skill action bonus multiplication When calculating the amount of xp given for a skill action after some bonus multiplier is applied, the result should be floored, not rounded, to match ingame behavior. Co-authored-by: Antony Linden Co-authored-by: Jordan Atwood --- .../client/plugins/skillcalculator/SkillCalculator.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java index 2b0143c103..afcb79312a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java @@ -408,7 +408,7 @@ private void calculate() bonus *= skillBonus.getValue(); } } - final int xp = Math.round(action.getXp() * bonus * 10f); + final int xp = (int) Math.floor(action.getXp() * 10f * bonus); if (neededXP > 0) { From ec2972a4000ed92c6e673905e356f7cb1b6cda43 Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Thu, 11 Apr 2024 22:26:09 -0700 Subject: [PATCH 30/67] skill calculator: Standardize bonus percentage format This changes all bonuses to read as a multiplicative change to the original action's xp value, (eg. "(102.5%)") rather than an additive amount, (eg. "(+2.5%)") as it makes more sense when applying multiple bonuses. Co-authored-by: Antony Linden Co-authored-by: Jordan Atwood --- .../skillcalculator/SkillCalculator.java | 25 +++++++++- .../skills/ConstructionBonus.java | 2 +- .../skillcalculator/skills/FarmingBonus.java | 2 +- .../skills/FiremakingBonus.java | 4 +- .../skillcalculator/skills/FishingBonus.java | 2 +- .../skillcalculator/skills/MiningBonus.java | 2 +- .../skillcalculator/skills/PrayerBonus.java | 18 +++---- .../skills/RunecraftBonus.java | 2 +- .../skills/WoodcuttingBonus.java | 2 +- .../skillcalculator/SkillCalculatorTest.java | 50 +++++++++++++++++++ 10 files changed, 91 insertions(+), 18 deletions(-) create mode 100644 runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorTest.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java index afcb79312a..6fa9f9aed7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/SkillCalculator.java @@ -25,6 +25,7 @@ */ package net.runelite.client.plugins.skillcalculator; +import com.google.common.annotations.VisibleForTesting; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; @@ -272,7 +273,7 @@ private void renderBonusOptions() private JPanel buildCheckboxPanel(SkillBonus bonus) { JPanel uiOption = new JPanel(new BorderLayout()); - JLabel uiLabel = new JLabel(bonus.getName()); + JLabel uiLabel = new JLabel(generateDisplayNameForBonus(bonus)); JCheckBox uiCheckbox = new JCheckBox(); uiLabel.setForeground(Color.WHITE); @@ -290,6 +291,28 @@ private JPanel buildCheckboxPanel(SkillBonus bonus) return uiOption; } + private static String generateDisplayNameForBonus(SkillBonus bonus) + { + return bonus.getName() + " (" + formatBonusPercentage(bonus.getValue()) + "%)"; + } + + @VisibleForTesting + static String formatBonusPercentage(float bonus) + { + final int bonusValue = Math.round(10_000 * bonus); + final float bonusPercent = bonusValue / 100f; + final int bonusPercentInt = (int) bonusPercent; + + if (bonusPercent == bonusPercentInt) + { + return String.valueOf(bonusPercentInt); + } + else + { + return String.valueOf(bonusPercent); + } + } + private void adjustCheckboxes(JCheckBox target, SkillBonus bonus) { // Check if target is stackable with any other bonuses diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionBonus.java index e5d1ca54d4..477cbb0e23 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionBonus.java @@ -31,7 +31,7 @@ @Getter(onMethod_ = @Override) public enum ConstructionBonus implements SkillBonus { - CARPENTERS_OUTFIT("Carpenter's Outfit (+2.5%)", 1.025f), + CARPENTERS_OUTFIT("Carpenter's Outfit", 1.025f), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FarmingBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FarmingBonus.java index 7328772073..e29ae4fa40 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FarmingBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FarmingBonus.java @@ -31,7 +31,7 @@ @Getter(onMethod_ = @Override) public enum FarmingBonus implements SkillBonus { - FARMERS_OUTFIT("Farmer's Outfit (+2.5%)", 1.025f), + FARMERS_OUTFIT("Farmer's Outfit", 1.025f), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingBonus.java index c153a2fea0..b20f6fca8b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FiremakingBonus.java @@ -33,8 +33,8 @@ @Getter(onMethod_ = @Override) public enum FiremakingBonus implements SkillBonus { - PYROMANCER_OUTFIT("Pyromancer Outfit (+2.5%)", 1.025f), - FORESTERS_CAMPFIRE("Forester's Campfire (33%)", 0.33333333f), + PYROMANCER_OUTFIT("Pyromancer Outfit", 1.025f), + FORESTERS_CAMPFIRE("Forester's Campfire", 0.33333333f), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingBonus.java index 832e7ff44f..b3b7567223 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingBonus.java @@ -31,7 +31,7 @@ @Getter(onMethod_ = @Override) public enum FishingBonus implements SkillBonus { - ANGLERS_OUTFIT("Angler's Outfit (+2.5%)", 1.025f), + ANGLERS_OUTFIT("Angler's Outfit", 1.025f), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningBonus.java index 910237ddc4..11d2ce7855 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningBonus.java @@ -31,7 +31,7 @@ @Getter(onMethod_ = @Override) public enum MiningBonus implements SkillBonus { - PROSPECTOR_KIT("Prospector Kit (+2.5%)", 1.025f), + PROSPECTOR_KIT("Prospector Kit", 1.025f), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java index 046f4098f0..c8cce15867 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java @@ -33,15 +33,15 @@ @Getter(onMethod_ = @Override) public enum PrayerBonus implements SkillBonus { - LIT_GILDED_ALTAR("Lit Gilded Altar (350%)", 3.5f), - ECTOFUNTUS("Ectofuntus (400%)", 4), - CHAOS_ALTAR("Chaos Altar (700%)", 7), - MORYTANIA_DIARY_3_SHADES("Morytania Diary 3 Shades (150%)", 1.5f), - BONECRUSHER("Bonecrusher (50%)", 0.5f), - SINISTER_OFFERING("Sinister Offering (300%)", 3), - DEMONIC_OFFERING("Demonic Offering (300%)", 3), - SACRED_BONE_BURNER("Sacred Bone Burner (300%)", 3), - ZEALOT_ROBES("Zealot Robes (105%)", 1.05f), + LIT_GILDED_ALTAR("Lit Gilded Altar", 3.5f), + ECTOFUNTUS("Ectofuntus", 4), + CHAOS_ALTAR("Chaos Altar", 7), + MORYTANIA_DIARY_3_SHADES("Morytania Diary 3 Shades", 1.5f), + BONECRUSHER("Bonecrusher", 0.5f), + SINISTER_OFFERING("Sinister Offering", 3), + DEMONIC_OFFERING("Demonic Offering", 3), + SACRED_BONE_BURNER("Sacred Bone Burner", 3), + ZEALOT_ROBES("Zealot Robes", 1.05f), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftBonus.java index 85bd47facd..c64fb13113 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftBonus.java @@ -31,7 +31,7 @@ @Getter(onMethod_ = @Override) public enum RunecraftBonus implements SkillBonus { - DAEYALT_ESSENCE("Daeyalt essence (+50%)", 1.5f), + DAEYALT_ESSENCE("Daeyalt essence", 1.5f), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingBonus.java index d4ded0e720..549bdc428d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingBonus.java @@ -31,7 +31,7 @@ @Getter(onMethod_ = @Override) public enum WoodcuttingBonus implements SkillBonus { - LUMBERJACK_OUTFIT("Lumberjack Outfit (+2.5%)", 1.025f), + LUMBERJACK_OUTFIT("Lumberjack Outfit", 1.025f), ; private final String name; diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorTest.java new file mode 100644 index 0000000000..4b9b36b964 --- /dev/null +++ b/runelite-client/src/test/java/net/runelite/client/plugins/skillcalculator/SkillCalculatorTest.java @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2024, Jordan Atwood + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.skillcalculator; + +import static net.runelite.client.plugins.skillcalculator.SkillCalculator.formatBonusPercentage; +import static org.junit.Assert.assertEquals; +import org.junit.Test; + +public class SkillCalculatorTest +{ + @Test + public void testFormatBonusPercentage() + { + assertEquals("33.33", formatBonusPercentage(0.33333333f)); + assertEquals("50", formatBonusPercentage(0.5f)); + assertEquals("102.5", formatBonusPercentage(1.025f)); + assertEquals("105", formatBonusPercentage(1.05f)); + assertEquals("110", formatBonusPercentage(1.1f)); + assertEquals("115", formatBonusPercentage(1.15f)); + assertEquals("120", formatBonusPercentage(1.2f)); + assertEquals("150", formatBonusPercentage(1.5f)); + assertEquals("250", formatBonusPercentage(2.5f)); + assertEquals("300", formatBonusPercentage(3f)); + assertEquals("350", formatBonusPercentage(3.5f)); + assertEquals("400", formatBonusPercentage(4f)); + assertEquals("700", formatBonusPercentage(7f)); + } +} From c91ccd68630d6c19233bfe9df40b787be0458a4b Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Thu, 11 Apr 2024 22:04:17 -0700 Subject: [PATCH 31/67] skill calculator: Add Goldsmith Gauntlets smithing bonus Co-authored-by: Antony Linden --- .../skillcalculator/CalculatorType.java | 2 +- .../skills/SmithingAction.java | 30 +++++++------- .../skillcalculator/skills/SmithingBonus.java | 39 +++++++++++++++++++ 3 files changed, 55 insertions(+), 16 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingBonus.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java index 8c27d8a509..510880183f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java @@ -36,7 +36,7 @@ enum CalculatorType { MINING(Skill.MINING, MiningBonus.values(), MiningAction.values()), AGILITY(Skill.AGILITY, null, AgilityAction.values()), - SMITHING(Skill.SMITHING, null, SmithingAction.values()), + SMITHING(Skill.SMITHING, SmithingBonus.values(), SmithingAction.values()), HERBLORE(Skill.HERBLORE, null, HerbloreAction.values()), FISHING(Skill.FISHING, FishingBonus.values(), FishingAction.values()), THIEVING(Skill.THIEVING, null, ThievingAction.values()), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingAction.java index e9c403c4a8..a2662c87cf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingAction.java @@ -24,10 +24,11 @@ */ package net.runelite.client.plugins.skillcalculator.skills; +import java.util.EnumSet; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.ItemID; -import net.runelite.client.game.ItemManager; @AllArgsConstructor @Getter @@ -117,20 +118,6 @@ public enum SmithingAction implements ItemSkillAction STEEL_SQ_SHIELD(ItemID.STEEL_SQ_SHIELD, 38, 75), STEEL_WARHAMMER(ItemID.STEEL_WARHAMMER, 39, 112.5f), GOLD_BAR(ItemID.GOLD_BAR, 40, 22.5f), - GOLD_BAR_GOLDSMITH_GAUNTLETS(ItemID.GOLD_BAR, 40, 56.2f) - { - @Override - public String getName(final ItemManager itemManager) - { - return "Gold bar (Goldsmith gauntlets)"; - } - - @Override - public boolean isMembers(final ItemManager itemManager) - { - return true; - } - }, STEEL_BATTLEAXE(ItemID.STEEL_BATTLEAXE, 40, 112.5f), STEEL_CHAINBODY(ItemID.STEEL_CHAINBODY, 41, 112.5f), STEEL_KITESHIELD(ItemID.STEEL_KITESHIELD, 42, 112.5f), @@ -232,4 +219,17 @@ public boolean isMembers(final ItemManager itemManager) private final int itemId; private final int level; private final float xp; + + @Override + public Set getExcludedSkillBonuses() + { + final EnumSet others = EnumSet.allOf(SmithingBonus.class); + + if (this == GOLD_BAR) + { + others.remove(SmithingBonus.GOLDSMITH_GAUNTLETS); + } + + return others; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingBonus.java new file mode 100644 index 0000000000..38b97bbb46 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/SmithingBonus.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024, DapperMickie + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.skillcalculator.skills; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter(onMethod_ = @Override) +public enum SmithingBonus implements SkillBonus +{ + GOLDSMITH_GAUNTLETS("Goldsmith Gauntlets", 2.5f), + ; + + private final String name; + private final float value; +} From cad48d2113ddec68785e27b0f2885359d444ff60 Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Thu, 11 Apr 2024 22:36:53 -0700 Subject: [PATCH 32/67] skill calculator: Add felling axe + rations woodcutting bonus Co-authored-by: Antony Linden --- .../skillcalculator/skills/WoodcuttingBonus.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingBonus.java index 549bdc428d..af21600d6b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingBonus.java @@ -24,6 +24,8 @@ */ package net.runelite.client.plugins.skillcalculator.skills; +import java.util.EnumSet; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; @@ -32,8 +34,17 @@ public enum WoodcuttingBonus implements SkillBonus { LUMBERJACK_OUTFIT("Lumberjack Outfit", 1.025f), + FELLING_AXE_RATIONS("Felling Axe + Rations", 1.1f), ; private final String name; private final float value; + + @Override + public Set getCanBeStackedWith() + { + final EnumSet others = EnumSet.allOf(WoodcuttingBonus.class); + others.remove(this); + return others; + } } From 772e08d15fa8f179e010452b556d04f791e86248 Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Thu, 11 Apr 2024 23:19:17 -0700 Subject: [PATCH 33/67] skill calculator: Improve prayer bonus ordering This commit groups bone bonuses together and orders them by their bonus amount. Co-authored-by: Antony Linden --- .../plugins/skillcalculator/skills/PrayerBonus.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java index c8cce15867..453e8e5031 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java @@ -33,14 +33,14 @@ @Getter(onMethod_ = @Override) public enum PrayerBonus implements SkillBonus { + BONECRUSHER("Bonecrusher", 0.5f), + SACRED_BONE_BURNER("Sacred Bone Burner", 3), + SINISTER_OFFERING("Sinister Offering", 3), LIT_GILDED_ALTAR("Lit Gilded Altar", 3.5f), ECTOFUNTUS("Ectofuntus", 4), CHAOS_ALTAR("Chaos Altar", 7), - MORYTANIA_DIARY_3_SHADES("Morytania Diary 3 Shades", 1.5f), - BONECRUSHER("Bonecrusher", 0.5f), - SINISTER_OFFERING("Sinister Offering", 3), DEMONIC_OFFERING("Demonic Offering", 3), - SACRED_BONE_BURNER("Sacred Bone Burner", 3), + MORYTANIA_DIARY_3_SHADES("Morytania Diary 3 Shades", 1.5f), ZEALOT_ROBES("Zealot Robes", 1.05f), ; From 34cf490afecf369f52d1987cc97d1108d486ceb8 Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Thu, 11 Apr 2024 23:20:17 -0700 Subject: [PATCH 34/67] skill calculator: Improve prayer bonus stacking Co-authored-by: Antony Linden --- .../skillcalculator/skills/PrayerBonus.java | 32 ++++--------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java index 453e8e5031..f3209f7088 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java @@ -50,38 +50,20 @@ public enum PrayerBonus implements SkillBonus @Override public Set getCanBeStackedWith() { - final Set others = EnumSet.noneOf(PrayerBonus.class); - switch (this) { case ECTOFUNTUS: case LIT_GILDED_ALTAR: case CHAOS_ALTAR: - others.add(ZEALOT_ROBES); - others.add(DEMONIC_OFFERING); - break; - case SACRED_BONE_BURNER: + case SINISTER_OFFERING: + return EnumSet.complementOf(EnumSet.of(ECTOFUNTUS, LIT_GILDED_ALTAR, CHAOS_ALTAR, SINISTER_OFFERING, SACRED_BONE_BURNER, BONECRUSHER)); case BONECRUSHER: - others.add(DEMONIC_OFFERING); - break; - case MORYTANIA_DIARY_3_SHADES: - others.add(DEMONIC_OFFERING); - others.add(SINISTER_OFFERING); - break; + case SACRED_BONE_BURNER: + return EnumSet.complementOf(EnumSet.of(ECTOFUNTUS, LIT_GILDED_ALTAR, CHAOS_ALTAR, SINISTER_OFFERING, SACRED_BONE_BURNER, BONECRUSHER, ZEALOT_ROBES)); case ZEALOT_ROBES: - others.add(ECTOFUNTUS); - others.add(LIT_GILDED_ALTAR); - others.add(CHAOS_ALTAR); - others.add(DEMONIC_OFFERING); - break; - case DEMONIC_OFFERING: - return EnumSet.complementOf(EnumSet.of(DEMONIC_OFFERING)); - case SINISTER_OFFERING: - others.add(MORYTANIA_DIARY_3_SHADES); - others.add(DEMONIC_OFFERING); - break; + return EnumSet.complementOf(EnumSet.of(BONECRUSHER, SACRED_BONE_BURNER, ZEALOT_ROBES)); + default: + return EnumSet.complementOf(EnumSet.of(this)); } - - return others; } } From e2bbeb4f63b31138f4eff8b57366d7681f61296c Mon Sep 17 00:00:00 2001 From: Antony Linden Date: Thu, 11 Apr 2024 23:33:11 -0700 Subject: [PATCH 35/67] skill calculator: Fix mahogany lectern icons Co-authored-by: DapperMickie Co-authored-by: Jordan Atwood --- .../plugins/skillcalculator/skills/ConstructionAction.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java index 88d0d63b62..7484286a20 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java @@ -179,8 +179,8 @@ public enum ConstructionAction implements NamedSkillAction GREATER_FOCUS("Greater Focus", 65, 500, ItemID.GREATER_FOCUS), TEAK_GARDEN_BENCH("Teak Garden Bench", 66, 540, ItemID.TEAK_GARDEN_BENCH), CRYSTAL_OF_POWER("Crystal of Power", 66, 890, ItemID.CRYSTAL_OF_POWER), - MAHOGANY_DEMON_LECTERN("Mahogany Demon Lectern", 67, 580, ItemID.TEAK_DEMON_LECTERN), - MAHOGANY_EAGLE_LECTERN("Mahogany Eagle Lectern", 67, 580, ItemID.TEAK_DEMON_LECTERN), + MAHOGANY_DEMON_LECTERN("Mahogany Demon Lectern", 67, 580, ItemID.MAHOGANY_DEMON), + MAHOGANY_EAGLE_LECTERN("Mahogany Eagle Lectern", 67, 580, ItemID.MAHOGANY_EAGLE), TEAK_SHELVES_2("Teak Shelves 2", 67, 930, ItemID.TEAK_SHELVES_2), CELESTIAL_GLOBE("Celestial Globe", 68, 570, ItemID.CELESTIAL_GLOBE), DUNGEON_ENTRANCE("Dungeon Entrance", 70, 500, ItemID.DUNGEON_ENTRANCE), From 91b94b1b9c3788d685b26ae32d0620fb847389e2 Mon Sep 17 00:00:00 2001 From: Antony Linden Date: Thu, 11 Apr 2024 23:34:03 -0700 Subject: [PATCH 36/67] skill calculator: Fix Mithril crossbow enum name Co-authored-by: DapperMickie --- .../client/plugins/skillcalculator/skills/FletchingAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java index a189711492..4931e3a622 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java @@ -90,7 +90,7 @@ public enum FletchingAction implements ItemSkillAction TOXIC_BLOWPIPE(ItemID.TOXIC_BLOWPIPE, 53, 120), MITHRIL_BOLTS(ItemID.MITHRIL_BOLTS, 54, 5), MAPLE_STOCK(ItemID.MAPLE_STOCK, 54, 32), - MITH_CROSSBOW(ItemID.MITHRIL_CROSSBOW, 54, 32), + MITHRIL_CROSSBOW(ItemID.MITHRIL_CROSSBOW, 54, 32), MITHRIL_CROSSBOW_U(ItemID.MITHRIL_CROSSBOW_U, 54, 64), BROAD_BOLTS(ItemID.BROAD_BOLTS, 55, 3), MAPLE_LONGBOW(ItemID.MAPLE_LONGBOW, 55, 58), From 38f6a9cc148d3933c01d15fce37faed36d046ca3 Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Thu, 11 Apr 2024 23:35:14 -0700 Subject: [PATCH 37/67] skill calculator: Fix Maple longbow xp value --- .../client/plugins/skillcalculator/skills/FletchingAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java index 4931e3a622..b4b2b517ca 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java @@ -93,7 +93,7 @@ public enum FletchingAction implements ItemSkillAction MITHRIL_CROSSBOW(ItemID.MITHRIL_CROSSBOW, 54, 32), MITHRIL_CROSSBOW_U(ItemID.MITHRIL_CROSSBOW_U, 54, 64), BROAD_BOLTS(ItemID.BROAD_BOLTS, 55, 3), - MAPLE_LONGBOW(ItemID.MAPLE_LONGBOW, 55, 58), + MAPLE_LONGBOW(ItemID.MAPLE_LONGBOW, 55, 58.2f), MAPLE_LONGBOW_U(ItemID.MAPLE_LONGBOW_U, 55, 58.3f), SAPPHIRE_BOLTS(ItemID.SAPPHIRE_BOLTS, 56, 4.7f), MAPLE_SHIELD(ItemID.MAPLE_SHIELD, 57, 116.5f), From c098ca9ea651c68f22763172aaa12bc82964751a Mon Sep 17 00:00:00 2001 From: Jordan Atwood Date: Thu, 11 Apr 2024 23:37:41 -0700 Subject: [PATCH 38/67] skill calculator: Use real levels for plank actions Now that the various types of planks are not all listed at the top of the construction skill actions, it doesn't make sense to claim they can all be used at level 1. This commit amends them to assume the lowest level of player-owned house furniture they can be used to construct. --- .../plugins/skillcalculator/skills/ConstructionAction.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java index 7484286a20..8f0197d78e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ConstructionAction.java @@ -39,10 +39,7 @@ public enum ConstructionAction implements NamedSkillAction PLANT("Plant", 1, 31, ItemID.PLANT), SHORT_PLANT("Short Plant", 1, 31, ItemID.SHORT_PLANT), CRUDE_WOODEN_CHAIR("Crude Wooden Chair", 1, 58, ItemID.CRUDE_WOODEN_CHAIR), - OAK_PLANK("Oak Plank", 1, 60, ItemID.OAK_PLANK), - TEAK_PLANK("Teak Plank", 1, 90, ItemID.TEAK_PLANK), EXIT_PORTAL("Exit Portal", 1, 100, ItemID.EXIT_PORTAL), - MAHOGANY_PLANK("Mahogany Plank", 1, 140, ItemID.MAHOGANY_PLANK), BROWN_RUG("Brown Rug", 2, 30, ItemID.BROWN_RUG), TORN_CURTAINS("Torn Curtains", 2, 132, ItemID.TORN_CURTAINS), CLAY_FIREPLACE("Clay Fireplace", 3, 30, ItemID.CLAY_FIREPLACE), @@ -74,6 +71,7 @@ public enum ConstructionAction implements NamedSkillAction WOODEN_SHELVES_2("Wooden Shelves 2", 12, 147, ItemID.WOODEN_SHELVES_2), RUG("Rug", 13, 60, ItemID.RUG), ROCKING_CHAIR("Rocking Chair", 14, 87, ItemID.ROCKING_CHAIR), + OAK_PLANK("Oak Plank", 15, 60, ItemID.OAK_PLANK), OAK_TREE("Oak Tree", 15, 70, ItemID.OAK_TREE), IMP_STATUE("Imp Statue", 15, 150, ItemID.IMP_STATUE), OAK_DECORATION("Oak Decoration", 16, 120, ItemID.OAK_DECORATION), @@ -113,6 +111,7 @@ public enum ConstructionAction implements NamedSkillAction STEEL_RANGE("Steel Range", 34, 120, ItemID.STEEL_RANGE), OAK_SHELVES_1("Oak Shelves 1", 34, 240, ItemID.OAK_SHELVES_1), LARGE_OAK_BED("Large Oak Bed", 34, 330, ItemID.LARGE_OAK_BED), + TEAK_PLANK("Teak Plank", 35, 90, ItemID.TEAK_PLANK), TEAK_ARMCHAIR("Teak Armchair", 35, 180, ItemID.TEAK_ARMCHAIR), TEAK_DECORATION("Teak Decoration", 36, 180, ItemID.TEAK_DECORATION), DRAGON_BITTER("Dragon Bitter", 36, 224, ItemID.DRAGON_BITTER), @@ -122,6 +121,7 @@ public enum ConstructionAction implements NamedSkillAction TEAK_TABLE("Teak Table", 38, 360, ItemID.TEAK_TABLE), OAK_WARDROBE("Oak Wardrobe", 39, 180, ItemID.OAK_WARDROBE), OAK_LECTERN("Oak Lectern", 40, 60, ItemID.OAK_LECTERN), + MAHOGANY_PLANK("Mahogany Plank", 40, 140, ItemID.MAHOGANY_PLANK), TEAK_BED("Teak Bed", 40, 300, ItemID.TEAK_BED), OPULENT_CURTAINS("Opulent Curtains", 40, 315, ItemID.OPULENT_CURTAINS), MAHOGANY_BOOKCASE("Mahogany Bookcase", 40, 420, ItemID.MAHOGANY_BOOKCASE), From 731297c22ff41f87904c275d225d584789044430 Mon Sep 17 00:00:00 2001 From: Antony Linden Date: Thu, 11 Apr 2024 23:40:18 -0700 Subject: [PATCH 39/67] skill calculator: Add Kourend Castle Teleport Co-authored-by: DapperMickie --- .../client/plugins/skillcalculator/skills/MagicAction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java index 36688d08de..9344a6f516 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java @@ -77,6 +77,7 @@ public enum MagicAction implements SkillAction WATER_BLAST("Water Blast", 47, 28.5f, SpriteID.SPELL_WATER_BLAST, false), SHADOW_VEIL("Shadow Veil", 47, 58, SpriteID.SPELL_SHADOW_VEIL, true), FENKENSTRAINS_CASTLE_TELEPORT("Fenkenstrain's Castle Teleport", 48, 50, SpriteID.SPELL_FENKENSTRAINS_CASTLE_TELEPORT, true), + KOUREND_CASTLE_TELEPORT("Kourend Castle Teleport", 48, 58, SpriteID.SPELL_TELEPORT_TO_KOUREND, true), ENCHANT_RUBY_BOLT("Enchant Ruby Bolt", 49, 59, SpriteID.SPELL_ENCHANT_CROSSBOW_BOLT, true), ENCHANT_RUBY_JEWELLERY("Enchant Ruby Jewellery", 49, 59, SpriteID.SPELL_LVL_3_ENCHANT, false), IBAN_BLAST("Iban Blast", 50, 30, SpriteID.SPELL_IBAN_BLAST, true), From 25b3e235a7af80f7a2f0ed40aa72b937b37e950e Mon Sep 17 00:00:00 2001 From: Antony Linden Date: Thu, 11 Apr 2024 23:41:43 -0700 Subject: [PATCH 40/67] skill calculator: Add Juniper logs Co-authored-by: DapperMickie --- .../client/plugins/skillcalculator/skills/WoodcuttingAction.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingAction.java index f6eaf87f83..cf219afb3b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/WoodcuttingAction.java @@ -38,6 +38,7 @@ public enum WoodcuttingAction implements ItemSkillAction OAK_LOGS(ItemID.OAK_LOGS, 15, 37.5f), WILLOW_LOGS(ItemID.WILLOW_LOGS, 30, 67.5f), TEAK_LOGS(ItemID.TEAK_LOGS, 35, 85), + JUNIPER_LOGS(ItemID.JUNIPER_LOGS, 42, 35), BARK(ItemID.BARK, 45, 82.5f), MAPLE_LOGS(ItemID.MAPLE_LOGS, 45, 100), MAHOGANY_LOGS(ItemID.MAHOGANY_LOGS, 50, 125), From dc6ad5c1bbff021ea4abce7593a2c690131bbc43 Mon Sep 17 00:00:00 2001 From: Antony Linden Date: Thu, 11 Apr 2024 23:42:32 -0700 Subject: [PATCH 41/67] skill calculator: Update Rogues' Castle chest xp value This chest was changed in the February 7, 2024 game update to give improved loot and more experience when opened. Co-authored-by: DapperMickie --- .../client/plugins/skillcalculator/skills/ThievingAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java index dfd26e1b3d..07ba867609 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java @@ -77,7 +77,7 @@ public enum ThievingAction implements NamedSkillAction DORGESH_KAAN_RICH_CHEST("Dorgesh-Kaan Rich Chest", 78, 650, ItemID.MINING_HELMET), HERO("Hero", 80, 275, ItemID.HERO), VYRE("Vyre", 82, 306.9f, ItemID.VYRE), - ROGUES_CASTLE_CHEST("Wilderness Rogues' Chest", 84, 100, ItemID.DRAGONSTONE), + ROGUES_CASTLE_CHEST("Wilderness Rogues' Chest", 84, 701.7f, ItemID.DRAGONSTONE), ELF("Elf", 85, 353, ItemID.ELF), TZHAAR_HUR("TzHaar-Hur", 90, 103.4f, ItemID.TZHAARHUR), ; From b788ef2271810552ab0c2db22eaf0daefebaaf32 Mon Sep 17 00:00:00 2001 From: Antony Linden Date: Thu, 11 Apr 2024 23:59:14 -0700 Subject: [PATCH 42/67] skill calculator: Add Varlamore skill actions and bonuses Co-authored-by: DapperMickie Co-authored-by: Jordan Atwood --- .../src/main/java/net/runelite/api/SpriteID.java | 4 +++- .../plugins/skillcalculator/skills/CookingAction.java | 10 ++++++++++ .../plugins/skillcalculator/skills/FishingAction.java | 1 + .../skillcalculator/skills/FletchingAction.java | 1 + .../plugins/skillcalculator/skills/HunterAction.java | 8 ++++++++ .../plugins/skillcalculator/skills/MagicAction.java | 1 + .../plugins/skillcalculator/skills/MiningAction.java | 8 ++++++++ .../plugins/skillcalculator/skills/PrayerAction.java | 10 +++++++++- .../plugins/skillcalculator/skills/PrayerBonus.java | 1 + .../skillcalculator/skills/RunecraftAction.java | 1 + .../plugins/skillcalculator/skills/ThievingAction.java | 1 + 11 files changed, 44 insertions(+), 2 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/SpriteID.java b/runelite-api/src/main/java/net/runelite/api/SpriteID.java index 3300aad143..bd3ac22ae1 100644 --- a/runelite-api/src/main/java/net/runelite/api/SpriteID.java +++ b/runelite-api/src/main/java/net/runelite/api/SpriteID.java @@ -380,7 +380,9 @@ public final class SpriteID public static final int SPELL_WATER_SURGE = 363; public static final int SPELL_EARTH_SURGE = 364; public static final int SPELL_FIRE_SURGE = 365; - /* Unmapped: 366, 367, 368 */ + /* Unmapped: 366 */ + public static final int SPELL_CIVITAS_ILLA_FORTIS_TELEPORT = 367; + /* Unmapped: 368 */ public static final int SPELL_BIND_DISABLED = 369; public static final int SPELL_SNARE_DISABLED = 370; public static final int SPELL_ENTANGLE_DISABLED = 371; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CookingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CookingAction.java index 669a0871c2..97803716f7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CookingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/CookingAction.java @@ -78,6 +78,7 @@ public enum CookingAction implements ItemSkillAction COOKED_CRAB_MEAT(ItemID.COOKED_CRAB_MEAT, 21, 100), FAT_SNAIL_MEAT(ItemID.FAT_SNAIL_MEAT, 22, 95), EGG_AND_TOMATO(ItemID.EGG_AND_TOMATO, 23, 50), + COOKED_WILD_KEBBIT(ItemID.COOKED_WILD_KEBBIT, 23, 73), ASGARNIAN_ALE(ItemID.ASGARNIAN_ALE, 24, 248, true), SALMON(ItemID.SALMON, 25, 90), STEW(ItemID.STEW, 25, 117), @@ -90,12 +91,16 @@ public enum CookingAction implements ItemSkillAction MUD_PIE(ItemID.MUD_PIE, 29, 128), CHEESE_AND_TOMATO_BATTA(ItemID.CHEESETOM_BATTA, 29, 158), GREENMANS_ALE(ItemID.GREENMANS_ALE, 29, 281), + COOKED_BREAM(ItemID.COOKED_BREAM, 30, 45), + COOKED_MOSS_LIZARD(ItemID.COOKED_MOSS_LIZARD, 30, 60), ROASTED_CHOMPY(ItemID.COOKED_CHOMPY, 30, 100), TUNA(ItemID.TUNA, 30, 100), APPLE_PIE(ItemID.APPLE_PIE, 30, 130), WORM_HOLE(ItemID.WORM_HOLE, 30, 170), COOKED_KARAMBWAN(ItemID.COOKED_KARAMBWAN, 30, 190), + COOKED_LARUPIA(ItemID.COOKED_LARUPIA, 31, 92), FISHCAKE(ItemID.COOKED_FISHCAKE, 31, 100), + COOKED_BARBTAILED_KEBBIT(ItemID.COOKED_BARBTAILED_KEBBIT, 32, 106), DRUNK_DRAGON(ItemID.DRUNK_DRAGON, 32, 160), TETRA(ItemID.TETRA, 33, 31), CHOC_SATURDAY(ItemID.CHOC_SATURDAY, 33, 170), @@ -113,6 +118,7 @@ public enum CookingAction implements ItemSkillAction LOBSTER(ItemID.LOBSTER, 40, 120), CAKE(ItemID.CAKE, 40, 180), TANGLED_TOADS_LEGS(ItemID.TANGLED_TOADS_LEGS, 40, 185), + COOKED_GRAAHK(ItemID.COOKED_GRAAHK, 41, 124), COOKED_JUBBLY(ItemID.COOKED_JUBBLY, 41, 160), CHILLI_POTATO(ItemID.CHILLI_POTATO, 41, 165.5f), FRIED_ONIONS(ItemID.FRIED_ONIONS, 42, 60), @@ -129,6 +135,7 @@ public enum CookingAction implements ItemSkillAction AXEMANS_FOLLY(ItemID.AXEMANS_FOLLY, 49, 413), COOKED_OOMLIE_WRAP(ItemID.COOKED_OOMLIE_WRAP, 50, 30), CHOCOLATE_CAKE(ItemID.CHOCOLATE_CAKE, 50, 210), + COOKED_KYATT(ItemID.COOKED_KYATT, 51, 143), EGG_POTATO(ItemID.EGG_POTATO, 51, 195.5f), BOTANICAL_PIE(ItemID.BOTANICAL_PIE, 52, 180), LAVA_EEL(ItemID.LAVA_EEL, 53, 30), @@ -145,16 +152,19 @@ public enum CookingAction implements ItemSkillAction PINEAPPLE_PIZZA(ItemID.PINEAPPLE_PIZZA, 65, 188), WINE_OF_ZAMORAK(ItemID.WINE_OF_ZAMORAK, 65, 200, true), TUNA_AND_CORN(ItemID.TUNA_AND_CORN, 67, 204), + COOKED_SUNLIGHT_ANTELOPE(ItemID.COOKED_SUNLIGHT_ANTELOPE, 68, 175), TUNA_POTATO(ItemID.TUNA_POTATO, 68, 309.5f), ADMIRAL_PIE(ItemID.ADMIRAL_PIE, 70, 210), SACRED_EEL(ItemID.SACRED_EEL, 72, 109), DRAGONFRUIT_PIE(ItemID.DRAGONFRUIT_PIE, 73, 220), SHARK(ItemID.SHARK, 80, 210), SEA_TURTLE(ItemID.SEA_TURTLE, 82, 211.3f), + COOKED_DASHING_KEBBIT(ItemID.COOKED_DASHING_KEBBIT, 82, 215), ANGLERFISH(ItemID.ANGLERFISH, 84, 230), WILD_PIE(ItemID.WILD_PIE, 85, 240), DARK_CRAB(ItemID.DARK_CRAB, 90, 215), MANTA_RAY(ItemID.MANTA_RAY, 91, 216.3f), + COOKED_MOONLIGHT_ANTELOPE(ItemID.COOKED_MOONLIGHT_ANTELOPE, 92, 220), SUMMER_PIE(ItemID.SUMMER_PIE, 95, 260), ; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingAction.java index ac5d904ca0..ba20354c81 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FishingAction.java @@ -40,6 +40,7 @@ public enum FishingAction implements ItemSkillAction RAW_ANCHOVIES(ItemID.RAW_ANCHOVIES, 15, 40), RAW_MACKEREL(ItemID.RAW_MACKEREL, 16, 20), RAW_CAVEFISH(ItemID.RAW_CAVEFISH, 20, 16), + RAW_BREAM(ItemID.RAW_BREAM, 20, 20), RAW_TROUT(ItemID.RAW_TROUT, 20, 50), RAW_COD(ItemID.RAW_COD, 23, 45), RAW_PIKE(ItemID.RAW_PIKE, 25, 60), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java index b4b2b517ca..f30e06e7df 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/FletchingAction.java @@ -98,6 +98,7 @@ public enum FletchingAction implements ItemSkillAction SAPPHIRE_BOLTS(ItemID.SAPPHIRE_BOLTS, 56, 4.7f), MAPLE_SHIELD(ItemID.MAPLE_SHIELD, 57, 116.5f), EMERALD_BOLTS(ItemID.EMERALD_BOLTS, 58, 5.5f), + HUNTERS_SPEAR(ItemID.HUNTERS_SPEAR, 60, 9.5f), ADAMANT_ARROW(ItemID.ADAMANT_ARROW, 60, 10), ADAMANT_BOLTS(ItemID.ADAMANT_BOLTS, 61, 7), ADAMANT_CROSSBOW(ItemID.ADAMANT_CROSSBOW, 61, 41), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HunterAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HunterAction.java index 415e467b5d..fc2b8d2370 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HunterAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/HunterAction.java @@ -46,6 +46,7 @@ public enum HunterAction implements NamedSkillAction RUBY_HARVEST("Ruby Harvest", 15, 24, ItemID.BUTTERFLY), BABY_IMPLING("Baby Impling", 17, 18, ItemID.BABY_IMPLING_JAR), TROPICAL_WAGTAIL("Tropical Wagtail", 19, 95, ItemID.TROPICAL_WAGTAIL), + MOSS_LIZARD("Moss Lizard", 20, 90, ItemID.RAW_MOSS_LIZARD), YOUNG_IMPLING("Young Impling", 22, 20, ItemID.YOUNG_IMPLING_JAR), WILD_KEBBIT("Wild Kebbit", 23, 128, ItemID.KEBBIT), WILLOW_BIRD_HOUSE("Willow Bird House", 24, 560, ItemID.WILLOW_BIRD_HOUSE), @@ -60,6 +61,7 @@ public enum HunterAction implements NamedSkillAction SNOWY_KNIGHT("Snowy Knight", 35, 44, ItemID.BUTTERFLY_9972), EARTH_IMPLING("Earth Impling", 36, 25, ItemID.EARTH_IMPLING_JAR), PRICKLY_KEBBIT("Prickly Kebbit", 37, 204, ItemID.KEBBIT_9957), + EMBERTAILED_JERBOA("Embertailed Jerboa", 39, 137, ItemID.EMBERTAILED_JERBOA), HORNED_GRAAHK("Horned Graahk", 41, 240, ItemID.GRAAHK_HEADDRESS), ESSENCE_IMPLING("Essence Impling", 42, 27, ItemID.ESSENCE_IMPLING_JAR), SPOTTED_KEBBIT("Spotted Kebbit", 43, 104, ItemID.KEBBIT_9960), @@ -73,24 +75,30 @@ public enum HunterAction implements NamedSkillAction CHINCHOMPA("Chinchompa", 53, 198.4f, ItemID.CHINCHOMPA), SABRE_TOOTHED_KYATT("Sabre-toothed Kyatt", 55, 300, ItemID.KYATT_HAT), DARK_KEBBIT("Dark Kebbit", 57, 132, ItemID.KEBBIT_9963), + PYRE_FOX("Pyre Fox", 57, 222, ItemID.PYRE_FOX), NATURE_IMPLING("Nature Impling", 58, 34, ItemID.NATURE_IMPLING_JAR), RED_SALAMANDER("Red Salamander", 59, 272, ItemID.RED_SALAMANDER), YEW_BIRD_HOUSE("Yew Bird House", 59, 1020, ItemID.YEW_BIRD_HOUSE), MANIACAL_MONKEY("Maniacal Monkey", 60, 1000, ItemID.MONKEY_19556), CARNIVOROUS_CHINCHOMPA("Carnivorous Chinchompa", 63, 265, ItemID.RED_CHINCHOMPA), MAGPIE_IMPLING("Magpie Impling", 65, 44, ItemID.MAGPIE_IMPLING_JAR), + SUNLIGHT_MOTH("Sunlight Moth", 65, 74, ItemID.SUNLIGHT_MOTH), MAGPIE_IMPLING_GIELINOR("Magpie Impling (Gielinor)", 65, 216, ItemID.MAGPIE_IMPLING_JAR), BLACK_SALAMANDER("Black Salamander", 67, 319.5f, ItemID.BLACK_SALAMANDER), DASHING_KEBBIT("Dashing Kebbit", 69, 156, ItemID.KEBBIT_9964), + SUNLIGHT_ANTELOPE("Sunlight Antelope", 72, 380, ItemID.SUNLIGHT_ANTELOPE), BLACK_CHINCHOMPA("Black Chinchompa", 73, 315, ItemID.BLACK_CHINCHOMPA), NINJA_IMPLING("Ninja Impling", 74, 52, ItemID.NINJA_IMPLING_JAR), NINJA_IMPLING_GIELINOR("Ninja Impling (Gielinor)", 74, 240, ItemID.NINJA_IMPLING_JAR), MAGIC_BIRD_HOUSE("Magic Bird House", 74, 1140, ItemID.MAGIC_BIRD_HOUSE), + MOONLIGHT_MOTH("Moonlight Moth", 75, 84, ItemID.MOONLIGHT_MOTH), + TECU_SALAMANDER("Tecu Salamander", 79, 344, ItemID.TECU_SALAMANDER), CRYSTAL_IMPLING("Crystal Impling", 80, 280, ItemID.CRYSTAL_IMPLING_JAR), DRAGON_IMPLING("Dragon Impling", 83, 65, ItemID.DRAGON_IMPLING_JAR), DRAGON_IMPLING_GIELINOR("Dragon Impling (Gielinor)", 83, 300, ItemID.DRAGON_IMPLING_JAR), LUCKY_IMPLING("Lucky Impling", 89, 380, ItemID.LUCKY_IMPLING_JAR), REDWOOD_BIRD_HOUSE("Redwood Bird House", 89, 1200, ItemID.REDWOOD_BIRD_HOUSE), + MOONLIGHT_ANTELOPE("Moonlight Antelope", 91, 450, ItemID.MOONLIGHT_ANTELOPE), ; private final String name; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java index 9344a6f516..620107e806 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MagicAction.java @@ -88,6 +88,7 @@ public enum MagicAction implements SkillAction ARDOUGNE_TELEPORT("Ardougne Teleport", 51, 61, SpriteID.SPELL_ARDOUGNE_TELEPORT, true), SHADOW_RUSH("Shadow Rush", 52, 31, SpriteID.SPELL_SHADOW_RUSH, true), EARTH_BLAST("Earth Blast", 53, 31.5f, SpriteID.SPELL_EARTH_BLAST, false), + CIVITAS_ILLA_FORTIS_TELEPORT("Civitas illa Fortis Teleport", 54, 64, SpriteID.SPELL_CIVITAS_ILLA_FORTIS_TELEPORT, true), PADDEWWA_TELEPORT("Paddewwa Teleport", 54, 64, SpriteID.SPELL_PADDEWWA_TELEPORT, true), HIGH_LEVEL_ALCHEMY("High Level Alchemy", 55, 65, SpriteID.SPELL_HIGH_LEVEL_ALCHEMY, false), BLOOD_RUSH("Blood Rush", 56, 33, SpriteID.SPELL_BLOOD_RUSH, true), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningAction.java index 6d3f824ac5..18d49f8c3b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/MiningAction.java @@ -73,6 +73,14 @@ public String getName(final ItemManager itemManager) } }, GOLD_ORE(ItemID.GOLD_ORE, 40, 65), + CALCIFIED_ROCKS(ItemID.BLESSED_BONE_SHARDS, 41, 33) + { + @Override + public String getName(final ItemManager itemManager) + { + return "Calcified Rocks"; + } + }, GRANITE_500G(ItemID.GRANITE_500G, 45, 50), GRANITE_2KG(ItemID.GRANITE_2KG, 45, 60), GRANITE_5KG(ItemID.GRANITE_5KG, 45, 75), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java index 62c7236fe4..9d43a15479 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerAction.java @@ -29,6 +29,7 @@ import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.ItemID; +import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.BLESSED_SUNFIRE_WINE; import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.DEMONIC_OFFERING; import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.MORYTANIA_DIARY_3_SHADES; import static net.runelite.client.plugins.skillcalculator.skills.PrayerBonus.ZEALOT_ROBES; @@ -49,6 +50,7 @@ public enum PrayerAction implements ItemSkillAction BIG_BONES(ItemID.BIG_BONES, 1, 15, PrayerMethod.BONES), JOGRE_BONES(ItemID.JOGRE_BONES, 1, 15, PrayerMethod.BONES), CATFISH(ItemID.CATFISH, 1, 16, PrayerMethod.PREPARED_FISH), + WYRMLING_BONES(ItemID.WYRMLING_BONES, 1, 21, PrayerMethod.BONES), ZOGRE_BONES(ItemID.ZOGRE_BONES, 1, 22.5f, PrayerMethod.BONES), SHAIKAHAN_BONES(ItemID.SHAIKAHAN_BONES, 1, 25, PrayerMethod.BONES), VILE_ASHES(ItemID.VILE_ASHES, 1, 25, PrayerMethod.DEMONIC_ASHES), @@ -95,6 +97,7 @@ public enum PrayerAction implements ItemSkillAction ENSOULED_AVIANSIE_HEAD(ItemID.ENSOULED_AVIANSIE_HEAD, 1, 1234, PrayerMethod.ENSOULED_HEAD), ENSOULED_ABYSSAL_HEAD(ItemID.ENSOULED_ABYSSAL_HEAD, 1, 1300, PrayerMethod.ENSOULED_HEAD), ENSOULED_DRAGON_HEAD(ItemID.ENSOULED_DRAGON_HEAD, 1, 1560, PrayerMethod.ENSOULED_HEAD), + BLESSED_BONE_SHARDS(ItemID.BLESSED_BONE_SHARDS, 30, 5, PrayerMethod.BLESSED_SUNFIRE_WINE), SUPERIOR_DRAGON_BONES(ItemID.SUPERIOR_DRAGON_BONES, 70, 150, PrayerMethod.BONES), ; @@ -105,14 +108,17 @@ private enum PrayerMethod ENSOULED_HEAD, PREPARED_FISH, SHADE_REMAINS, + BLESSED_SUNFIRE_WINE, } private static final Set EXCLUDED_BONUSES_FOR_BONES = EnumSet.of( MORYTANIA_DIARY_3_SHADES, - DEMONIC_OFFERING + DEMONIC_OFFERING, + BLESSED_SUNFIRE_WINE ); private static final Set EXCLUDED_BONUSES_FOR_ASHES = EnumSet.complementOf(EnumSet.of(DEMONIC_OFFERING)); private static final Set EXCLUDED_BONUSES_FOR_REMAINS = EnumSet.complementOf(EnumSet.of(MORYTANIA_DIARY_3_SHADES)); + private static final Set EXCLUDED_BONUSES_FOR_BLESSED_SUNFIRE_WINE = EnumSet.complementOf(EnumSet.of(ZEALOT_ROBES, BLESSED_SUNFIRE_WINE)); private static final Set EXCLUDE_ALL_EXCEPT_ZEALOT_ROBES = EnumSet.complementOf(EnumSet.of(ZEALOT_ROBES)); private final int itemId; @@ -134,6 +140,8 @@ public Set getExcludedSkillBonuses() return EXCLUDE_ALL_EXCEPT_ZEALOT_ROBES; case SHADE_REMAINS: return EXCLUDED_BONUSES_FOR_REMAINS; + case BLESSED_SUNFIRE_WINE: + return EXCLUDED_BONUSES_FOR_BLESSED_SUNFIRE_WINE; default: return EnumSet.allOf(PrayerBonus.class); } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java index f3209f7088..d512790c7a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/PrayerBonus.java @@ -39,6 +39,7 @@ public enum PrayerBonus implements SkillBonus LIT_GILDED_ALTAR("Lit Gilded Altar", 3.5f), ECTOFUNTUS("Ectofuntus", 4), CHAOS_ALTAR("Chaos Altar", 7), + BLESSED_SUNFIRE_WINE("Blessed Sunfire Wine", 1.2f), DEMONIC_OFFERING("Demonic Offering", 3), MORYTANIA_DIARY_3_SHADES("Morytania Diary 3 Shades", 1.5f), ZEALOT_ROBES("Zealot Robes", 1.05f), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java index c46e7cb87d..6820a7abc4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/RunecraftAction.java @@ -63,6 +63,7 @@ public enum RunecraftAction implements ItemSkillAction BODY_CORE(ItemID.BODY_CORE, 20, 75, true), LAVA_RUNE(ItemID.LAVA_RUNE, 23, 10.5f, false), COSMIC_RUNE(ItemID.COSMIC_RUNE, 27, 8, false, true), + SUNFIRE_RUNE(ItemID.SUNFIRE_RUNE, 33, 9, false), CHAOS_RUNE(ItemID.CHAOS_RUNE, 35, 8.5f, false, true), CHAOS_CORE(ItemID.CHAOS_CORE, 35, 85, true), ASTRAL_RUNE(ItemID.ASTRAL_RUNE, 40, 8.7f, false), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java index 07ba867609..eec464d34f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/ThievingAction.java @@ -61,6 +61,7 @@ public enum ThievingAction implements NamedSkillAction CROSSBOW_STALL("Crossbow Stall", 49, 52, ItemID.CROSSBOW), SILVER_STALL("Silver Stall", 50, 54, ItemID.SILVER_BAR), WALL_SAFE("Wall Safe", 50, 70, ItemID.STETHOSCOPE), + WEALTHY_CITIZEN("Wealthy Citizen", 50, 96, ItemID.WEALTHY_CITIZEN), DESERT_BANDIT("Desert Bandit", 53, 79.5f, ItemID.BANDIT), KNIGHT("Knight", 55, 84.3f, ItemID.KNIGHT), POLLNIVNIAN_BANDIT("Pollnivnian Bandit", 55, 84.3f, ItemID.BANDIT_6781), From 9bede307caaac29feae89a9f968da7f173fb04ce Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Fri, 12 Apr 2024 00:06:45 -0700 Subject: [PATCH 43/67] skill calculator: Fix Wilderness agility course xp value Co-authored-by: Antony Linden --- .../client/plugins/skillcalculator/skills/AgilityAction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java index 8beb9ec679..7cfce057bf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java @@ -47,7 +47,7 @@ public enum AgilityAction implements NamedSkillAction SHAYZIEN_ADVANCED_COURSE("Shayzien Advanced Course", 48, 474.3f, ItemID.SHAYZIEN_HELM_5), APE_ATOLL_COURSE("Ape Atoll", 48, 580, ItemID.GORILLA_GREEGREE), FALADOR_ROOFTOP("Falador Rooftop", 50, 440, ItemID.MARK_OF_GRACE), - WILDERNESS_AGILITY_COURSE("Wilderness Agility Course", 52, 571, ItemID.SKULL), + WILDERNESS_AGILITY_COURSE("Wilderness Agility Course", 52, 571.4f, ItemID.SKULL), HALLOWED_SEPULCHRE_FLOOR_1("Hallowed Sepulchre Floor 1", 52, 575, ItemID.RING_OF_ENDURANCE), SEERS_VILLAGE_ROOFTOP("Seers' Village Rooftop", 60, 570, ItemID.MARK_OF_GRACE), WEREWOLF_AGILITY_COURSE("Werewolf Agility Course", 60, 730, ItemID.STICK), From b3451542dce0440a6c2577e8f7e3783e05d03551 Mon Sep 17 00:00:00 2001 From: DapperMickie Date: Fri, 12 Apr 2024 00:14:06 -0700 Subject: [PATCH 44/67] skill calculator: Add Wilderness agility tickets Co-authored-by: Antony Linden Co-authored-by: Jordan Atwood --- .../skillcalculator/CalculatorType.java | 2 +- .../skillcalculator/skills/AgilityAction.java | 21 ++++++++++ .../skillcalculator/skills/AgilityBonus.java | 41 +++++++++++++++++++ 3 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityBonus.java diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java index 510880183f..43bb5f5991 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/CalculatorType.java @@ -35,7 +35,7 @@ enum CalculatorType { MINING(Skill.MINING, MiningBonus.values(), MiningAction.values()), - AGILITY(Skill.AGILITY, null, AgilityAction.values()), + AGILITY(Skill.AGILITY, AgilityBonus.values(), AgilityAction.values()), SMITHING(Skill.SMITHING, SmithingBonus.values(), SmithingAction.values()), HERBLORE(Skill.HERBLORE, null, HerbloreAction.values()), FISHING(Skill.FISHING, FishingBonus.values(), FishingAction.values()), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java index 7cfce057bf..4c31032db8 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityAction.java @@ -24,10 +24,15 @@ */ package net.runelite.client.plugins.skillcalculator.skills; +import java.util.EnumSet; +import java.util.Set; import lombok.AllArgsConstructor; import lombok.Getter; import net.runelite.api.ItemID; import net.runelite.client.game.ItemManager; +import static net.runelite.client.plugins.skillcalculator.skills.AgilityBonus.WILDERNESS_AGILITY_TICKET_101_PLUS; +import static net.runelite.client.plugins.skillcalculator.skills.AgilityBonus.WILDERNESS_AGILITY_TICKET_11_TO_50; +import static net.runelite.client.plugins.skillcalculator.skills.AgilityBonus.WILDERNESS_AGILITY_TICKET_51_TO_100; @AllArgsConstructor @Getter @@ -47,6 +52,7 @@ public enum AgilityAction implements NamedSkillAction SHAYZIEN_ADVANCED_COURSE("Shayzien Advanced Course", 48, 474.3f, ItemID.SHAYZIEN_HELM_5), APE_ATOLL_COURSE("Ape Atoll", 48, 580, ItemID.GORILLA_GREEGREE), FALADOR_ROOFTOP("Falador Rooftop", 50, 440, ItemID.MARK_OF_GRACE), + WILDERNESS_AGILITY_COURSE_TICKET("Wilderness Agility Ticket", 52, 200, ItemID.WILDERNESS_AGILITY_TICKET), WILDERNESS_AGILITY_COURSE("Wilderness Agility Course", 52, 571.4f, ItemID.SKULL), HALLOWED_SEPULCHRE_FLOOR_1("Hallowed Sepulchre Floor 1", 52, 575, ItemID.RING_OF_ENDURANCE), SEERS_VILLAGE_ROOFTOP("Seers' Village Rooftop", 60, 570, ItemID.MARK_OF_GRACE), @@ -72,4 +78,19 @@ public boolean isMembers(final ItemManager itemManager) { return true; } + + @Override + public Set getExcludedSkillBonuses() + { + final EnumSet others = EnumSet.allOf(AgilityBonus.class); + + if (this == WILDERNESS_AGILITY_COURSE_TICKET) + { + others.remove(WILDERNESS_AGILITY_TICKET_11_TO_50); + others.remove(WILDERNESS_AGILITY_TICKET_51_TO_100); + others.remove(WILDERNESS_AGILITY_TICKET_101_PLUS); + } + + return others; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityBonus.java b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityBonus.java new file mode 100644 index 0000000000..d84b78f7b7 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/skillcalculator/skills/AgilityBonus.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2024, DapperMickie + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.skillcalculator.skills; + +import lombok.AllArgsConstructor; +import lombok.Getter; + +@AllArgsConstructor +@Getter(onMethod_ = @Override) +public enum AgilityBonus implements SkillBonus +{ + WILDERNESS_AGILITY_TICKET_11_TO_50("11-50 Wilderness Tickets", 1.05f), + WILDERNESS_AGILITY_TICKET_51_TO_100("51-100 Wilderness Tickets", 1.1f), + WILDERNESS_AGILITY_TICKET_101_PLUS("101+ Wilderness Tickets", 1.15f), + ; + + private final String name; + private final float value; +} From 6dcbb05a40a937cfd864cb2efbb70b3a01395803 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 6 Apr 2024 10:08:32 -0400 Subject: [PATCH 45/67] gpu: add macos retina support Co-authored-by: khogeland --- runelite-client/pom.xml | 2 +- .../net/runelite/client/callback/Hooks.java | 4 +- .../client/plugins/gpu/GpuPlugin.java | 55 +++++++------------ .../runelite/client/util/ImageCapture.java | 3 +- 4 files changed, 22 insertions(+), 42 deletions(-) diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 311550a595..6f353d4044 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -140,7 +140,7 @@ net.runelite rlawt - 1.4 + 1.5 diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index 502848ea74..4b91e20840 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -79,7 +79,6 @@ import net.runelite.client.ui.overlay.infobox.InfoBoxManager; import net.runelite.client.util.DeferredEventBus; import net.runelite.client.util.LinkBrowser; -import net.runelite.client.util.OSType; import net.runelite.client.util.RSTimeUnit; /** @@ -472,8 +471,7 @@ public void draw(MainBufferProvider mainBufferProvider, Graphics graphics, int x private Image screenshot(Image src) { // scale source image to the size of the client ui - final AffineTransform transform = OSType.getOSType() == OSType.MacOS ? new AffineTransform() : - clientUi.getGraphicsConfiguration().getDefaultTransform(); + final AffineTransform transform = clientUi.getGraphicsConfiguration().getDefaultTransform(); int swidth = src.getWidth(null); int sheight = src.getHeight(null); int twidth = (int) (swidth * transform.getScaleX() + .5); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 0470bdf528..dacb546841 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -815,14 +815,11 @@ private void initUniformBuffer() private void initAAFbo(int width, int height, int aaSamples) { - if (OSType.getOSType() != OSType.MacOS) - { - final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); - final AffineTransform transform = graphicsConfiguration.getDefaultTransform(); + final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); + final AffineTransform transform = graphicsConfiguration.getDefaultTransform(); - width = getScaledValue(transform.getScaleX(), width); - height = getScaledValue(transform.getScaleY(), height); - } + width = getScaledValue(transform.getScaleX(), width); + height = getScaledValue(transform.getScaleY(), height); // Create and bind the FBO fboSceneHandle = GL43C.glGenFramebuffers(); @@ -1332,14 +1329,11 @@ public void draw(int overlayColor) int width = lastStretchedCanvasWidth; int height = lastStretchedCanvasHeight; - if (OSType.getOSType() != OSType.MacOS) - { - final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); - final AffineTransform transform = graphicsConfiguration.getDefaultTransform(); + final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); + final AffineTransform transform = graphicsConfiguration.getDefaultTransform(); - width = getScaledValue(transform.getScaleX(), width); - height = getScaledValue(transform.getScaleY(), height); - } + width = getScaledValue(transform.getScaleX(), width); + height = getScaledValue(transform.getScaleY(), height); GL43C.glBindFramebuffer(GL43C.GL_READ_FRAMEBUFFER, fboSceneHandle); GL43C.glBindFramebuffer(GL43C.GL_DRAW_FRAMEBUFFER, awtContext.getFramebuffer(false)); @@ -1458,13 +1452,10 @@ private Image screenshot() height = dim.height; } - if (OSType.getOSType() != OSType.MacOS) - { - final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); - final AffineTransform t = graphicsConfiguration.getDefaultTransform(); - width = getScaledValue(t.getScaleX(), width); - height = getScaledValue(t.getScaleY(), height); - } + final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); + final AffineTransform t = graphicsConfiguration.getDefaultTransform(); + width = getScaledValue(t.getScaleX(), width); + height = getScaledValue(t.getScaleY(), height); ByteBuffer buffer = ByteBuffer.allocateDirect(width * height * 4) .order(ByteOrder.nativeOrder()); @@ -1852,21 +1843,13 @@ private int getScaledValue(final double scale, final int value) private void glDpiAwareViewport(final int x, final int y, final int width, final int height) { - if (OSType.getOSType() == OSType.MacOS) - { - // macos handles DPI scaling for us already - GL43C.glViewport(x, y, width, height); - } - else - { - final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); - final AffineTransform t = graphicsConfiguration.getDefaultTransform(); - GL43C.glViewport( - getScaledValue(t.getScaleX(), x), - getScaledValue(t.getScaleY(), y), - getScaledValue(t.getScaleX(), width), - getScaledValue(t.getScaleY(), height)); - } + final GraphicsConfiguration graphicsConfiguration = clientUI.getGraphicsConfiguration(); + final AffineTransform t = graphicsConfiguration.getDefaultTransform(); + GL43C.glViewport( + getScaledValue(t.getScaleX(), x), + getScaledValue(t.getScaleY(), y), + getScaledValue(t.getScaleX(), width), + getScaledValue(t.getScaleY(), height)); } private int getDrawDistance() diff --git a/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java b/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java index fbaed6d8bb..959eadf167 100644 --- a/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java +++ b/runelite-client/src/main/java/net/runelite/client/util/ImageCapture.java @@ -113,8 +113,7 @@ public void takeScreenshot(@Nullable String subDir, String fileName, boolean inc public BufferedImage addClientFrame(Image image) { // create a new image, paint the client ui to it, and then draw the screenshot to that - final AffineTransform transform = OSType.getOSType() == OSType.MacOS ? new AffineTransform() : - clientUi.getGraphicsConfiguration().getDefaultTransform(); + final AffineTransform transform = clientUi.getGraphicsConfiguration().getDefaultTransform(); // on Windows the insets are the window border Insets insets = clientUi.getInsets(); From 8c4390baa2dd0e9f791976d9be6a232eb9fa5028 Mon Sep 17 00:00:00 2001 From: Hooder Date: Wed, 10 Apr 2024 00:27:41 +0200 Subject: [PATCH 46/67] gpu: fix nearest neighbor with integer DPI scaling Nearest neighbor filtering for the UI texture was only applied if stretched mode was enabled, but this is also useful when DPI scaling to integer multiples. --- .../net/runelite/client/plugins/gpu/GpuPlugin.java | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index dacb546841..41444d0466 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -1415,13 +1415,10 @@ private void drawUi(final int overlayColor, final int canvasHeight, final int ca // Set the sampling function used when stretching the UI. // This is probably better done with sampler objects instead of texture parameters, but this is easier and likely more portable. // See https://www.khronos.org/opengl/wiki/Sampler_Object for details. - if (client.isStretchedEnabled()) - { - // GL_NEAREST makes sampling for bicubic/xBR simpler, so it should be used whenever linear isn't - final int function = uiScalingMode == UIScalingMode.LINEAR ? GL43C.GL_LINEAR : GL43C.GL_NEAREST; - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MIN_FILTER, function); - GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAG_FILTER, function); - } + // GL_NEAREST makes sampling for bicubic/xBR simpler, so it should be used whenever linear isn't + final int function = uiScalingMode == UIScalingMode.LINEAR ? GL43C.GL_LINEAR : GL43C.GL_NEAREST; + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MIN_FILTER, function); + GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAG_FILTER, function); // Texture on UI GL43C.glBindVertexArray(vaoUiHandle); From 10501e01f3fb4f779ffca61900e6124e78b455d4 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 10 Apr 2024 20:22:58 -0400 Subject: [PATCH 47/67] api: add item despawn time --- .../src/main/java/net/runelite/api/TileItem.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/TileItem.java b/runelite-api/src/main/java/net/runelite/api/TileItem.java index 8c1b08541e..0a92f982a3 100644 --- a/runelite-api/src/main/java/net/runelite/api/TileItem.java +++ b/runelite-api/src/main/java/net/runelite/api/TileItem.java @@ -36,4 +36,17 @@ public interface TileItem extends Renderable int getId(); int getQuantity(); + + /** + * Get the time, in server ticks, when the item despawns, relative to the spawn time. + * @return + */ + int getDespawnTime(); + + /** + * Get the time, in server ticks, when the item becomes visible to other players, relative + * to the spawn time. + * @return + */ + int getVisibleTime(); } From 312776c5fc979365ce2e4218ee9d7b289414038a Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 10 Apr 2024 20:30:13 -0400 Subject: [PATCH 48/67] ground items: use TileItem despawn times --- .../plugins/grounditems/GroundItem.java | 3 + .../grounditems/GroundItemsOverlay.java | 119 ++---------------- .../grounditems/GroundItemsPlugin.java | 4 + 3 files changed, 15 insertions(+), 111 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java index 89eae64fcc..ea51c350b2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItem.java @@ -25,6 +25,7 @@ package net.runelite.client.plugins.grounditems; import java.awt.Color; +import java.time.Duration; import java.time.Instant; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -51,6 +52,8 @@ class GroundItem @Nullable private Instant spawnTime; private boolean stackable; + private Duration despawnTime; + private Duration visibleTime; // cached values derived from config boolean highlighted; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java index 7fe3a555c2..eb6da70f0e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsOverlay.java @@ -31,9 +31,7 @@ import java.awt.Graphics2D; import java.awt.Polygon; import java.awt.Rectangle; -import java.time.Duration; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.AbstractMap.SimpleEntry; import java.util.ArrayList; import java.util.Collection; @@ -72,23 +70,6 @@ public class GroundItemsOverlay extends Overlay private static final Color PUBLIC_TIMER_COLOR = Color.YELLOW; private static final Color PRIVATE_TIMER_COLOR = Color.GREEN; private static final int TIMER_OVERLAY_DIAMETER = 10; - private static final Duration DESPAWN_TIME_INFERNO = Duration.ofMinutes(180); - private static final Duration DESPAWN_TIME_INSTANCE = Duration.ofMinutes(30); - private static final Duration DESPAWN_TIME_LOOT = Duration.ofMinutes(2); - private static final Duration DESPAWN_TIME_DROP = Duration.ofMinutes(3); - private static final Duration DESPAWN_TIME_TABLE = Duration.ofMinutes(10); - private static final int KRAKEN_REGION = 9116; - private static final int CLAN_HALL_REGION = 6997; - private static final int KBD_NMZ_REGION = 9033; - private static final int ZILYANA_REGION = 11602; - private static final int GRAARDOR_REGION = 11347; - private static final int KRIL_TSUTSAROTH_REGION = 11603; - private static final int KREEARRA_REGION = 11346; - private static final int NEX_REGION = 11601; - private static final int NIGHTMARE_REGION = 15515; - private static final int TEMPOROSS_REGION = 12078; - private static final int KALPHITE_QUEEN_REGION = 13972; - private static final int INFERNO_REGION = 9043; private final Client client; private final GroundItemsPlugin plugin; @@ -398,92 +379,14 @@ else if (groundItemTimers == DespawnTimerMode.SECONDS || groundItemTimers == Des private Instant calculateDespawnTime(GroundItem groundItem) { - // We can only accurately guess despawn times for our own pvm loot, dropped items, - // and items we placed on tables - if (groundItem.getLootType() != LootType.PVM - && groundItem.getLootType() != LootType.DROPPED - && groundItem.getLootType() != LootType.TABLE) - { - return null; - } - - // Loot appears to others after 1 minute, and despawns after 2 minutes - // Dropped items appear to others after 1 minute, and despawns after 3 minutes - // Items in instances never appear to anyone and despawn after 30 minutes - Instant spawnTime = groundItem.getSpawnTime(); if (spawnTime == null) { return null; } - final Instant despawnTime; - Instant now = Instant.now(); - if (client.isInInstancedRegion()) - { - final int playerRegionID = WorldPoint.fromLocalInstance(client, client.getLocalPlayer().getLocalLocation()).getRegionID(); - if (playerRegionID == KRAKEN_REGION) - { - // Items in the Kraken instance never despawn - return null; - } - else if (playerRegionID == KBD_NMZ_REGION) - { - // NMZ and the KBD lair uses the same region ID but NMZ uses planes 1-3 and KBD uses plane 0 - if (client.getLocalPlayer().getWorldLocation().getPlane() == 0) - { - despawnTime = spawnTime.plus(DESPAWN_TIME_INSTANCE); - } - else - { - if (groundItem.getLootType() == LootType.DROPPED) - { - // Dropped items in the NMZ instance never despawn - return null; - } - else - { - despawnTime = spawnTime.plus(DESPAWN_TIME_LOOT); - } - } - } - else if (playerRegionID == INFERNO_REGION) - { - despawnTime = spawnTime.plus(DESPAWN_TIME_INFERNO); - } - else if (playerRegionID == ZILYANA_REGION || playerRegionID == GRAARDOR_REGION || - playerRegionID == KRIL_TSUTSAROTH_REGION || playerRegionID == KREEARRA_REGION || - playerRegionID == NEX_REGION || playerRegionID == KALPHITE_QUEEN_REGION || - playerRegionID == NIGHTMARE_REGION || playerRegionID == TEMPOROSS_REGION || - playerRegionID == CLAN_HALL_REGION) - { - // GWD, Kalphite Queen, Nightmare, and Tempoross instances use the normal despawn timers - despawnTime = spawnTime.plus(groundItem.getLootType() == LootType.DROPPED - ? DESPAWN_TIME_DROP - : DESPAWN_TIME_LOOT); - } - else - { - despawnTime = spawnTime.plus(DESPAWN_TIME_INSTANCE); - } - } - else - { - switch (groundItem.getLootType()) - { - case DROPPED: - despawnTime = spawnTime.plus(DESPAWN_TIME_DROP); - break; - case TABLE: - despawnTime = spawnTime.plus(DESPAWN_TIME_TABLE); - break; - default: - despawnTime = spawnTime.plus(DESPAWN_TIME_LOOT); - break; - } - } - - if (now.isBefore(spawnTime) || now.isAfter(despawnTime)) + Instant despawnTime = spawnTime.plus(groundItem.getDespawnTime()); + if (Instant.now().isAfter(despawnTime)) { // that's weird return null; @@ -494,15 +397,6 @@ else if (playerRegionID == ZILYANA_REGION || playerRegionID == GRAARDOR_REGION | private Color getItemTimerColor(GroundItem groundItem) { - // We can only accurately guess despawn times for our own pvm loot, dropped items, - // and items we placed on tables - if (groundItem.getLootType() != LootType.PVM - && groundItem.getLootType() != LootType.DROPPED - && groundItem.getLootType() != LootType.TABLE) - { - return null; - } - final Instant spawnTime = groundItem.getSpawnTime(); if (spawnTime == null) { @@ -510,16 +404,19 @@ private Color getItemTimerColor(GroundItem groundItem) } final Instant now = Instant.now(); + final Instant despawnTime = spawnTime.plus(groundItem.getDespawnTime()); + final Instant visibleTime = spawnTime.plus(groundItem.getVisibleTime()); - // If it has not yet been a minute, the item is private - if (client.isInInstancedRegion() || spawnTime.plus(1, ChronoUnit.MINUTES).isAfter(now)) + if (visibleTime != null && visibleTime.isAfter(now)) { return PRIVATE_TIMER_COLOR; } - else + if (despawnTime != null && despawnTime.isAfter(now)) { return PUBLIC_TIMER_COLOR; } + + return null; } private void drawTimerPieOverlay(Graphics2D graphics, int textX, int textY, GroundItem groundItem) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java index 404027fe54..02326f7036 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grounditems/GroundItemsPlugin.java @@ -38,6 +38,7 @@ import java.awt.Rectangle; import static java.lang.Boolean.FALSE; import static java.lang.Boolean.TRUE; +import java.time.Duration; import java.time.Instant; import java.util.ArrayList; import java.util.Collection; @@ -101,6 +102,7 @@ import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.util.ColorUtil; import net.runelite.client.util.QuantityFormatter; +import net.runelite.client.util.RSTimeUnit; import net.runelite.client.util.Text; @PluginDescriptor( @@ -447,6 +449,8 @@ private GroundItem buildGroundItem(final Tile tile, final TileItem item) .lootType(dropped ? LootType.DROPPED : (table ? LootType.TABLE : LootType.UNKNOWN)) .spawnTime(Instant.now()) .stackable(itemComposition.isStackable()) + .despawnTime(Duration.of(item.getDespawnTime(), RSTimeUnit.GAME_TICKS)) + .visibleTime(Duration.of(item.getVisibleTime(), RSTimeUnit.GAME_TICKS)) .build(); // Update item price in case it is coins From 807705ef2526a131a68086cdf7dfc0851a50e81f Mon Sep 17 00:00:00 2001 From: Felanbird <41973452+Felanbird@users.noreply.github.com> Date: Wed, 14 Feb 2024 19:33:23 -0500 Subject: [PATCH 49/67] chat commands: add more toa normal mode aliases --- .../client/plugins/chatcommands/ChatCommandsPlugin.java | 5 +++++ 1 file changed, 5 insertions(+) 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 b837fbd89b..84225fca8d 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 @@ -2190,6 +2190,11 @@ private static String longBossName(String boss) // Tombs of Amascut case "toa": + case "tombs": + case "amascut": + case "warden": + case "wardens": + case "raids 3": return "Tombs of Amascut"; case "toa 1": case "toa solo": From dce4722c504bab53d5f2f9c44c8e7fd87981bc9f Mon Sep 17 00:00:00 2001 From: Ourmond Date: Fri, 12 Apr 2024 17:03:35 -0700 Subject: [PATCH 50/67] idle notifier: Add Ent & Canoe chopping animations Co-authored-by: Jordan Atwood --- .../java/net/runelite/api/AnimationID.java | 27 ++++++++++++++++++ .../idlenotifier/IdleNotifierPlugin.java | 28 +++++++++++++++++++ 2 files changed, 55 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/AnimationID.java b/runelite-api/src/main/java/net/runelite/api/AnimationID.java index c5512b5142..d09ffdd2e2 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -59,6 +59,33 @@ public final class AnimationID public static final int WOODCUTTING_2H_CRYSTAL = 10072; public static final int WOODCUTTING_2H_CRYSTAL_INACTIVE = 10073; public static final int WOODCUTTING_2H_3A = 10074; + public static final int WOODCUTTING_ENT_BRONZE = 3291; + public static final int WOODCUTTING_ENT_IRON = 3290; + public static final int WOODCUTTING_ENT_STEEL = 3289; + public static final int WOODCUTTING_ENT_BLACK = 3288; + public static final int WOODCUTTING_ENT_MITHRIL = 3287; + public static final int WOODCUTTING_ENT_ADAMANT = 3286; + public static final int WOODCUTTING_ENT_RUNE = 3285; + public static final int WOODCUTTING_ENT_GILDED = 8305; + public static final int WOODCUTTING_ENT_DRAGON = 3292; + public static final int WOODCUTTING_ENT_DRAGON_OR = 23; + public static final int WOODCUTTING_ENT_INFERNAL = 2116; + public static final int WOODCUTTING_ENT_INFERNAL_OR = 8777; + public static final int WOODCUTTING_ENT_3A = 7266; + public static final int WOODCUTTING_ENT_CRYSTAL = 8323; + public static final int WOODCUTTING_ENT_CRYSTAL_INACTIVE = 8327; + public static final int WOODCUTTING_ENT_TRAILBLAZER = 8780; + public static final int WOODCUTTING_ENT_2H_BRONZE = 10517; + public static final int WOODCUTTING_ENT_2H_IRON = 10518; + public static final int WOODCUTTING_ENT_2H_STEEL = 10519; + public static final int WOODCUTTING_ENT_2H_BLACK = 10520; + public static final int WOODCUTTING_ENT_2H_MITHRIL = 10521; + public static final int WOODCUTTING_ENT_2H_ADAMANT = 10522; + public static final int WOODCUTTING_ENT_2H_RUNE = 10523; + public static final int WOODCUTTING_ENT_2H_DRAGON = 10524; + public static final int WOODCUTTING_ENT_2H_CRYSTAL = 10525; + public static final int WOODCUTTING_ENT_2H_CRYSTAL_INACTIVE = 10526; + public static final int WOODCUTTING_ENT_2H_3A = 10527; public static final int CONSUMING = 829; // consuming consumables public static final int FIREMAKING = 733; public static final int FIREMAKING_FORESTERS_CAMPFIRE_ARCTIC_PINE = 10563; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index 1626357731..0b2972db73 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -161,6 +161,34 @@ public void onAnimationChanged(AnimationChanged event) case WOODCUTTING_2H_CRYSTAL: case WOODCUTTING_2H_CRYSTAL_INACTIVE: case WOODCUTTING_2H_3A: + /* Woodcutting: Ents & Canoes */ + case WOODCUTTING_ENT_BRONZE: + case WOODCUTTING_ENT_IRON: + case WOODCUTTING_ENT_STEEL: + case WOODCUTTING_ENT_BLACK: + case WOODCUTTING_ENT_MITHRIL: + case WOODCUTTING_ENT_ADAMANT: + case WOODCUTTING_ENT_RUNE: + case WOODCUTTING_ENT_GILDED: + case WOODCUTTING_ENT_DRAGON: + case WOODCUTTING_ENT_DRAGON_OR: + case WOODCUTTING_ENT_INFERNAL: + case WOODCUTTING_ENT_INFERNAL_OR: + case WOODCUTTING_ENT_3A: + case WOODCUTTING_ENT_CRYSTAL: + case WOODCUTTING_ENT_CRYSTAL_INACTIVE: + case WOODCUTTING_ENT_TRAILBLAZER: + case WOODCUTTING_ENT_2H_BRONZE: + case WOODCUTTING_ENT_2H_IRON: + case WOODCUTTING_ENT_2H_STEEL: + case WOODCUTTING_ENT_2H_BLACK: + case WOODCUTTING_ENT_2H_MITHRIL: + case WOODCUTTING_ENT_2H_ADAMANT: + case WOODCUTTING_ENT_2H_RUNE: + case WOODCUTTING_ENT_2H_DRAGON: + case WOODCUTTING_ENT_2H_CRYSTAL: + case WOODCUTTING_ENT_2H_CRYSTAL_INACTIVE: + case WOODCUTTING_ENT_2H_3A: case BLISTERWOOD_JUMP_SCARE: /* Firemaking */ case FIREMAKING_FORESTERS_CAMPFIRE_ARCTIC_PINE: From 388c35df902334dbad70665daf61daea6ae0363a Mon Sep 17 00:00:00 2001 From: Macweese <50101641+Macweese@users.noreply.github.com> Date: Fri, 22 Mar 2024 01:11:02 +0100 Subject: [PATCH 51/67] login screen: add varlamore login screen --- .../loginscreen/LoginScreenOverride.java | 1 + .../client/plugins/loginscreen/varlamore.jpg | Bin 0 -> 57772 bytes 2 files changed, 1 insertion(+) create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/loginscreen/varlamore.jpg diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/loginscreen/LoginScreenOverride.java b/runelite-client/src/main/java/net/runelite/client/plugins/loginscreen/LoginScreenOverride.java index 7f1f250232..69f378ccb7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/loginscreen/LoginScreenOverride.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/loginscreen/LoginScreenOverride.java @@ -46,6 +46,7 @@ public enum LoginScreenOverride A_KINGDOM_DIVIDED("akd.jpg"), NEX("nex.jpg"), TOMBS_OF_AMASCUT("toa.jpg"), + VARLAMORE("varlamore.jpg"), CUSTOM, RANDOM; diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/loginscreen/varlamore.jpg b/runelite-client/src/main/resources/net/runelite/client/plugins/loginscreen/varlamore.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3b8a8a6ddf03a8505dfb3d6fa5bf1e142b15fca2 GIT binary patch literal 57772 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 literal 0 HcmV?d00001 From ac52b2b01dc6df3414b8bad2d44884ea2dff29e0 Mon Sep 17 00:00:00 2001 From: Adam Date: Sat, 13 Apr 2024 13:30:42 -0400 Subject: [PATCH 52/67] gpu: fix animated texture uv overflow After a number of days the tick value becomes large enough that it begins negatively affecting the precision of the texture animation. Just loop after 128 cycles since the smallest unit that the animated textures move is 1/128 per frame --- .../main/java/net/runelite/client/plugins/gpu/GpuPlugin.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java index 41444d0466..4e7f7afdb0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/gpu/GpuPlugin.java @@ -1270,7 +1270,7 @@ public void draw(int overlayColor) if (gameState == GameState.LOGGED_IN) { // avoid textures animating during loading - GL43C.glUniform1i(uniTick, client.getGameCycle()); + GL43C.glUniform1i(uniTick, client.getGameCycle() & 127); } // Calculate projection matrix From 8cdec03d882aa529716cf21d680195de55b2aa71 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 31 Mar 2024 13:12:42 -0400 Subject: [PATCH 53/67] plugins: use new notification system --- .../client/plugins/agility/AgilityConfig.java | 5 +- .../client/plugins/agility/AgilityPlugin.java | 5 +- .../client/plugins/boosts/BoostsConfig.java | 5 +- .../client/plugins/boosts/BoostsPlugin.java | 4 +- .../client/plugins/cannon/CannonConfig.java | 5 +- .../client/plugins/cannon/CannonPlugin.java | 9 +- .../ChatNotificationsConfig.java | 25 ++--- .../ChatNotificationsPlugin.java | 52 +++++----- .../plugins/devtools/DevToolsPanel.java | 11 ++- .../client/plugins/fishing/FishingConfig.java | 5 +- .../client/plugins/fishing/FishingPlugin.java | 4 +- .../grandexchange/GrandExchangeConfig.java | 9 +- .../grandexchange/GrandExchangePlugin.java | 8 +- .../client/plugins/hunter/HunterConfig.java | 5 +- .../client/plugins/hunter/HunterPlugin.java | 8 +- .../idlenotifier/IdleNotifierConfig.java | 17 ++-- .../idlenotifier/IdleNotifierPlugin.java | 18 ++-- .../plugins/itemcharges/ItemChargeConfig.java | 33 ++++--- .../plugins/itemcharges/ItemChargePlugin.java | 39 ++------ .../nightmarezone/NightmareZoneConfig.java | 25 ++--- .../nightmarezone/NightmareZonePlugin.java | 36 ++----- .../npcunaggroarea/NpcAggroAreaConfig.java | 5 +- .../npcunaggroarea/NpcAggroAreaPlugin.java | 5 +- .../randomevents/RandomEventConfig.java | 97 ++++++++++--------- .../randomevents/RandomEventPlugin.java | 92 +++++++++++------- .../plugins/runecraft/RunecraftConfig.java | 5 +- .../plugins/runecraft/RunecraftPlugin.java | 7 +- .../client/plugins/slayer/SlayerConfig.java | 5 +- .../client/plugins/slayer/SlayerPlugin.java | 4 +- .../specialcounter/SpecialCounterConfig.java | 5 +- .../specialcounter/SpecialCounterPlugin.java | 4 +- .../timetracking/TimeTrackingConfig.java | 5 +- .../timetracking/clocks/ClockManager.java | 5 +- .../woodcutting/WoodcuttingConfig.java | 37 +++---- .../woodcutting/WoodcuttingPlugin.java | 40 +++----- .../plugins/cannon/CannonPluginTest.java | 17 ++-- .../ChatNotificationsPluginTest.java | 29 +++++- .../GrandExchangePluginTest.java | 9 +- .../idlenotifier/IdleNotifierPluginTest.java | 19 ++-- .../plugins/slayer/SlayerPluginTest.java | 10 +- .../SpecialCounterPluginTest.java | 7 +- .../woodcutting/WoodcuttingPluginTest.java | 28 +++--- 42 files changed, 375 insertions(+), 388 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityConfig.java index 0e2af795d1..6df20b3015 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityConfig.java @@ -30,6 +30,7 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Notification; import net.runelite.client.config.Units; @ConfigGroup("agility") @@ -196,9 +197,9 @@ default Color getTrapColor() description = "Notify on ticket location change in Agility Arena", position = 13 ) - default boolean notifyAgilityArena() + default Notification notifyAgilityArena() { - return true; + return Notification.ON; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityPlugin.java index 96d11d2b43..c8a6ea1ba5 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/agility/AgilityPlugin.java @@ -290,10 +290,7 @@ public void onGameTick(GameTick tick) { log.debug("Ticked position moved from {} to {}", oldTickPosition, newTicketPosition); - if (config.notifyAgilityArena()) - { - notifier.notify("Ticket location changed"); - } + notifier.notify(config.notifyAgilityArena(), "Ticket location changed"); if (config.showAgilityArenaTimer()) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsConfig.java index 86b9629081..cf48c7dd92 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsConfig.java @@ -27,6 +27,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; @ConfigGroup("boosts") public interface BoostsConfig extends Config @@ -140,8 +141,8 @@ default int boostThreshold() description = "Configures whether or not a notification will be sent for boosted stats.", position = 13 ) - default boolean notifyOnBoost() + default Notification notifyOnBoost() { - return true; + return Notification.ON; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsPlugin.java index 05baee07e6..2f235cd16b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/boosts/BoostsPlugin.java @@ -221,14 +221,14 @@ public void onStatChanged(StatChanged statChanged) int boostThreshold = config.boostThreshold(); - if (boostThreshold != 0 && config.notifyOnBoost()) + if (boostThreshold != 0) { int real = client.getRealSkillLevel(skill); int lastBoost = last - real; int boost = cur - real; if (boost <= boostThreshold && boostThreshold < lastBoost) { - notifier.notify(skill.getName() + " level is getting low!"); + notifier.notify(config.notifyOnBoost(), skill.getName() + " level is getting low!"); } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonConfig.java index 3b7a5a3a1a..4dc87d8599 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonConfig.java @@ -29,6 +29,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; import net.runelite.client.config.Range; @ConfigGroup("cannon") @@ -40,9 +41,9 @@ public interface CannonConfig extends Config description = "Configures whether to notify you when your cannon is low on cannonballs", position = 1 ) - default boolean showCannonNotifications() + default Notification showCannonNotifications() { - return true; + return Notification.ON; } @Range( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java index 11d2dac240..bb602c7350 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cannon/CannonPlugin.java @@ -302,9 +302,9 @@ public void onVarbitChanged(VarbitChanged varbitChanged) { cballsLeft = varbitChanged.getValue(); - if (config.showCannonNotifications() && !cannonBallNotificationSent && cballsLeft > 0 && config.lowWarningThreshold() >= cballsLeft) + if (!cannonBallNotificationSent && cballsLeft > 0 && config.lowWarningThreshold() >= cballsLeft) { - notifier.notify(String.format("Your cannon has %d cannon balls remaining!", cballsLeft)); + notifier.notify(config.showCannonNotifications(), String.format("Your cannon has %d cannon balls remaining!", cballsLeft)); cannonBallNotificationSent = true; } } @@ -366,10 +366,7 @@ else if (event.getMessage().startsWith("You load the cannon with")) } else if (event.getMessage().contains("Your cannon is out of ammo!")) { - if (config.showCannonNotifications()) - { - notifier.notify("Your cannon is out of ammo!"); - } + notifier.notify(config.showCannonNotifications(), "Your cannon is out of ammo!"); } else if (event.getMessage().equals("This isn't your cannon!") || event.getMessage().equals("This is not your cannon.")) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsConfig.java index c2bc53909f..0bd5b44bac 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsConfig.java @@ -29,6 +29,7 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Notification; @ConfigGroup("chatnotification") public interface ChatNotificationsConfig extends Config @@ -81,9 +82,9 @@ default boolean highlightOwnName() name = "Notify on own name", description = "Notifies you whenever someone mentions you by name" ) - default boolean notifyOnOwnName() + default Notification notifyOnOwnName() { - return false; + return Notification.OFF; } @ConfigItem( @@ -92,9 +93,9 @@ default boolean notifyOnOwnName() name = "Notify on highlight", description = "Notifies you whenever a highlighted word is matched" ) - default boolean notifyOnHighlight() + default Notification notifyOnHighlight() { - return false; + return Notification.OFF; } @ConfigItem( @@ -103,9 +104,9 @@ default boolean notifyOnHighlight() name = "Notify on trade", description = "Notifies you whenever you are traded" ) - default boolean notifyOnTrade() + default Notification notifyOnTrade() { - return false; + return Notification.OFF; } @ConfigItem( @@ -114,9 +115,9 @@ default boolean notifyOnTrade() name = "Notify on duel", description = "Notifies you whenever you are challenged to a duel" ) - default boolean notifyOnDuel() + default Notification notifyOnDuel() { - return false; + return Notification.OFF; } @ConfigItem( @@ -125,9 +126,9 @@ default boolean notifyOnDuel() name = "Notify on broadcast", description = "Notifies you whenever you receive a broadcast message" ) - default boolean notifyOnBroadcast() + default Notification notifyOnBroadcast() { - return false; + return Notification.OFF; } @ConfigItem( @@ -136,8 +137,8 @@ default boolean notifyOnBroadcast() name = "Notify on private message", description = "Notifies you whenever you receive a private message" ) - default boolean notifyOnPM() + default Notification notifyOnPM() { - return false; + return Notification.OFF; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java index e26275a2eb..6648e7c188 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPlugin.java @@ -48,6 +48,7 @@ import net.runelite.client.Notifier; import net.runelite.client.chat.ChatColorType; import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.Notification; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.events.ConfigChanged; import net.runelite.client.plugins.Plugin; @@ -166,39 +167,33 @@ public void onChatMessage(ChatMessage chatMessage) switch (chatMessage.getType()) { case TRADEREQ: - if (chatMessage.getMessage().contains("wishes to trade with you.") && config.notifyOnTrade()) + if (chatMessage.getMessage().contains("wishes to trade with you.")) { - notifier.notify(chatMessage.getMessage()); + notifier.notify(config.notifyOnTrade(), chatMessage.getMessage()); } break; case CHALREQ_TRADE: - if (chatMessage.getMessage().contains("wishes to duel with you.") && config.notifyOnDuel()) + if (chatMessage.getMessage().contains("wishes to duel with you.")) { - notifier.notify(chatMessage.getMessage()); + notifier.notify(config.notifyOnDuel(), chatMessage.getMessage()); } break; case BROADCAST: - if (config.notifyOnBroadcast()) - { - // Some broadcasts have links attached, notated by `|` followed by a number, while others contain color tags. - // We don't want to see either in the printed notification. - String broadcast = chatMessage.getMessage(); - - int urlTokenIndex = broadcast.lastIndexOf('|'); - if (urlTokenIndex != -1) - { - broadcast = broadcast.substring(0, urlTokenIndex); - } + // Some broadcasts have links attached, notated by `|` followed by a number, while others contain color tags. + // We don't want to see either in the printed notification. + String broadcast = chatMessage.getMessage(); - notifier.notify(Text.removeFormattingTags(broadcast)); + int urlTokenIndex = broadcast.lastIndexOf('|'); + if (urlTokenIndex != -1) + { + broadcast = broadcast.substring(0, urlTokenIndex); } + + notifier.notify(config.notifyOnBroadcast(), Text.removeFormattingTags(broadcast)); break; case PRIVATECHAT: case MODPRIVATECHAT: - if (config.notifyOnPM()) - { - notifier.notify(Text.removeTags(chatMessage.getName()) + ": " + chatMessage.getMessage()); - } + notifier.notify(config.notifyOnPM(), Text.removeTags(chatMessage.getName()) + ": " + chatMessage.getMessage()); break; case PRIVATECHATOUT: case DIALOG: @@ -259,15 +254,15 @@ public void onChatMessage(ChatMessage chatMessage) messageNode.setValue(stringBuffer.toString()); update = true; - if (config.notifyOnOwnName() && (chatMessage.getType() == ChatMessageType.PUBLICCHAT + if (chatMessage.getType() == ChatMessageType.PUBLICCHAT || chatMessage.getType() == ChatMessageType.PRIVATECHAT || chatMessage.getType() == ChatMessageType.FRIENDSCHAT || chatMessage.getType() == ChatMessageType.MODCHAT || chatMessage.getType() == ChatMessageType.MODPRIVATECHAT || chatMessage.getType() == ChatMessageType.CLAN_CHAT - || chatMessage.getType() == ChatMessageType.CLAN_GUEST_CHAT)) + || chatMessage.getType() == ChatMessageType.CLAN_GUEST_CHAT) { - sendNotification(chatMessage); + sendNotification(config.notifyOnOwnName(), chatMessage); } } } @@ -313,10 +308,7 @@ public void onChatMessage(ChatMessage chatMessage) if (matchesHighlight) { messageNode.setValue(nodeValue); - if (config.notifyOnHighlight()) - { - sendNotification(chatMessage); - } + sendNotification(config.notifyOnHighlight(), chatMessage); } if (update) @@ -325,7 +317,7 @@ public void onChatMessage(ChatMessage chatMessage) } } - private void sendNotification(ChatMessage message) + private void sendNotification(Notification notification, ChatMessage message) { String name = Text.removeTags(message.getName()); String sender = message.getSender(); @@ -342,8 +334,8 @@ private void sendNotification(ChatMessage message) } stringBuilder.append(Text.removeTags(message.getMessage())); - String notification = stringBuilder.toString(); - notifier.notify(notification); + String m = stringBuilder.toString(); + notifier.notify(notification, m); } private String quoteAndIgnoreColor(String str) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java index b0f6d0df59..c751b25aaa 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/devtools/DevToolsPanel.java @@ -41,6 +41,7 @@ import net.runelite.api.MenuAction; import net.runelite.client.Notifier; import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.Notification; import net.runelite.client.ui.ColorScheme; import net.runelite.client.ui.PluginPanel; import net.runelite.client.ui.overlay.OverlayMenuEntry; @@ -144,9 +145,13 @@ private JPanel createOptionsPanel() final JButton notificationBtn = new JButton("Notification"); notificationBtn.addActionListener(e -> - { - scheduledExecutorService.schedule(() -> notifier.notify("Wow!", TrayIcon.MessageType.ERROR), 3, TimeUnit.SECONDS); - }); + scheduledExecutorService.schedule(() -> + { + var notif = new Notification() + .withEnabled(true) + .withTrayIconType(TrayIcon.MessageType.ERROR); + notifier.notify(notif, "Wow!"); + }, 3, TimeUnit.SECONDS)); container.add(notificationBtn); container.add(plugin.getScriptInspector()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java index f0f37fdbc5..bc327f7bef 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingConfig.java @@ -29,6 +29,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; import net.runelite.client.config.Units; @ConfigGroup("fishing") @@ -166,9 +167,9 @@ default boolean showMinnowOverlay() name = "Flying fish notification", description = "Send a notification when a flying fish spawns on your fishing spot." ) - default boolean flyingFishNotification() + default Notification flyingFishNotification() { - return true; + return Notification.ON; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java index d821ca5c05..2c60502b76 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/fishing/FishingPlugin.java @@ -205,9 +205,9 @@ public void onChatMessage(ChatMessage event) fishingSpotMinimapOverlay.setHidden(false); } - if (message.equals("A flying fish jumps up and eats some of your minnows!") && config.flyingFishNotification()) + if (message.equals("A flying fish jumps up and eats some of your minnows!")) { - notifier.notify("A flying fish is eating your minnows!"); + notifier.notify(config.flyingFishNotification(), "A flying fish is eating your minnows!"); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeConfig.java index b57d92a566..20f32b3812 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangeConfig.java @@ -27,6 +27,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; @ConfigGroup(GrandExchangeConfig.CONFIG_GROUP) public interface GrandExchangeConfig extends Config @@ -50,9 +51,9 @@ default boolean quickLookup() name = "Notify on offer update", description = "Configures whether to enable notifications when an offer updates" ) - default boolean enableNotifications() + default Notification enableNotifications() { - return true; + return Notification.ON; } @ConfigItem( @@ -61,9 +62,9 @@ default boolean enableNotifications() name = "Notify on offer complete", description = "Configures whether to enable notifications when an offer completes" ) - default boolean notifyOnOfferComplete() + default Notification notifyOnOfferComplete() { - return false; + return Notification.OFF; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java index 4a4755de5c..82f24b20f0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/grandexchange/GrandExchangePlugin.java @@ -606,13 +606,13 @@ public void onChatMessage(ChatMessage event) String message = Text.removeTags(event.getMessage()); - if (message.startsWith("Grand Exchange:") && config.enableNotifications()) + if (message.startsWith("Grand Exchange: Finished")) { - notifier.notify(message); + notifier.notify(config.notifyOnOfferComplete(), message); } - else if (message.startsWith("Grand Exchange: Finished") && config.notifyOnOfferComplete()) + else if (message.startsWith("Grand Exchange:")) { - notifier.notify(message); + notifier.notify(config.enableNotifications(), message); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterConfig.java index 66bf54660c..019dd6707c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterConfig.java @@ -29,6 +29,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; @ConfigGroup("hunterplugin") public interface HunterConfig extends Config @@ -87,8 +88,8 @@ default Color getTransTrapColor() name = "Maniacal monkey notification", description = "Send notification when maniacal monkey is caught or you fail to catch." ) - default boolean maniacalMonkeyNotify() + default Notification maniacalMonkeyNotify() { - return false; + return Notification.OFF; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java index 0322819ca3..a44a9eaf4c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/hunter/HunterPlugin.java @@ -200,9 +200,9 @@ public void onGameObjectSpawned(GameObjectSpawned event) myTrap.setState(HunterTrap.State.FULL); myTrap.resetTimer(); - if (config.maniacalMonkeyNotify() && myTrap.getObjectId() == ObjectID.MONKEY_TRAP) + if (myTrap.getObjectId() == ObjectID.MONKEY_TRAP) { - notifier.notify("You've caught part of a monkey's tail."); + notifier.notify(config.maniacalMonkeyNotify(), "You've caught part of a monkey's tail."); } } @@ -375,10 +375,10 @@ else if (containsBoulder) // For traps like deadfalls. This is different because log.debug("Special trap removed from personal trap collection, {} left", traps.size()); // Case we have notifications enabled and the action was not manual, throw notification - if (config.maniacalMonkeyNotify() && trap.getObjectId() == ObjectID.MONKEY_TRAP && + if (trap.getObjectId() == ObjectID.MONKEY_TRAP && !trap.getState().equals(HunterTrap.State.FULL) && !trap.getState().equals(HunterTrap.State.OPEN)) { - notifier.notify("The monkey escaped."); + notifier.notify(config.maniacalMonkeyNotify(), "The monkey escaped."); } } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierConfig.java index 99f0a5b55f..4b2a893ffe 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierConfig.java @@ -27,6 +27,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; import net.runelite.client.config.Range; import net.runelite.client.config.Units; @@ -39,9 +40,9 @@ public interface IdleNotifierConfig extends Config description = "Configures if idle animation notifications are enabled", position = 1 ) - default boolean animationIdle() + default Notification animationIdle() { - return true; + return Notification.ON; } @ConfigItem( @@ -50,9 +51,9 @@ default boolean animationIdle() description = "Configures if idle interaction notifications are enabled e.g. combat, fishing", position = 2 ) - default boolean interactionIdle() + default Notification interactionIdle() { - return true; + return Notification.ON; } @ConfigItem( @@ -61,9 +62,9 @@ default boolean interactionIdle() description = "Configures if idle movement notifications are enabled e.g. running, walking", position = 3 ) - default boolean movementIdle() + default Notification movementIdle() { - return false; + return Notification.OFF; } @ConfigItem( @@ -72,9 +73,9 @@ default boolean movementIdle() description = "Configures if the idle logout notifications are enabled", position = 4 ) - default boolean logoutIdle() + default Notification logoutIdle() { - return true; + return Notification.ON; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java index 1626357731..97aeccfd3e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPlugin.java @@ -484,9 +484,9 @@ public void onGameTick(GameTick event) return; } - if (config.logoutIdle() && checkIdleLogout()) + if (checkIdleLogout()) { - notifier.notify("You are about to log out from idling too long!"); + notifier.notify(config.logoutIdle(), "You are about to log out from idling too long!"); } if (check6hrLogout()) @@ -494,25 +494,25 @@ public void onGameTick(GameTick event) notifier.notify("You are about to log out from being online for 6 hours!"); } - if (config.animationIdle() && checkAnimationIdle(waitDuration, local)) + if (checkAnimationIdle(waitDuration, local)) { - notifier.notify("You are now idle!"); + notifier.notify(config.animationIdle(), "You are now idle!"); } - if (config.movementIdle() && checkMovementIdle(waitDuration, local)) + if (checkMovementIdle(waitDuration, local)) { - notifier.notify("You have stopped moving!"); + notifier.notify(config.movementIdle(), "You have stopped moving!"); } - if (config.interactionIdle() && checkInteractionIdle(waitDuration, local)) + if (checkInteractionIdle(waitDuration, local)) { if (lastInteractWasCombat) { - notifier.notify("You are now out of combat!"); + notifier.notify(config.interactionIdle(), "You are now out of combat!"); } else { - notifier.notify("You are now idle!"); + notifier.notify(config.interactionIdle(), "You are now idle!"); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeConfig.java index 876606ec71..6472c641c7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargeConfig.java @@ -31,6 +31,7 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Notification; @ConfigGroup(ItemChargeConfig.GROUP) public interface ItemChargeConfig extends Config @@ -138,9 +139,9 @@ default boolean showDodgyCount() position = 7, section = notificationSection ) - default boolean dodgyNotification() + default Notification dodgyNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -270,9 +271,9 @@ default boolean showAmuletOfBountyCharges() position = 18, section = notificationSection ) - default boolean recoilNotification() + default Notification recoilNotification() { - return false; + return Notification.OFF; } @ConfigItem( @@ -294,9 +295,9 @@ default boolean showBindingNecklaceCharges() position = 20, section = notificationSection ) - default boolean bindingNotification() + default Notification bindingNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -330,9 +331,9 @@ default boolean showRingOfForgingCount() position = 23, section = notificationSection ) - default boolean ringOfForgingNotification() + default Notification ringOfForgingNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -377,9 +378,9 @@ default boolean showBraceletOfSlaughterCharges() position = 27, section = notificationSection ) - default boolean slaughterNotification() + default Notification slaughterNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -401,9 +402,9 @@ default boolean showExpeditiousBraceletCharges() position = 29, section = notificationSection ) - default boolean expeditiousNotification() + default Notification expeditiousNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -449,9 +450,9 @@ default boolean showBraceletOfClayCharges() position = 32, section = notificationSection ) - default boolean braceletOfClayNotification() + default Notification braceletOfClayNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -461,8 +462,8 @@ default boolean braceletOfClayNotification() position = 33, section = notificationSection ) - default boolean amuletOfChemistryNotification() + default Notification amuletOfChemistryNotification() { - return true; + return Notification.ON; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java index dbf5059903..733ec51629 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/itemcharges/ItemChargePlugin.java @@ -271,16 +271,13 @@ public void onChatMessage(ChatMessage event) Matcher bloodEssenceExtractMatcher = BLOOD_ESSENCE_EXTRACT_PATTERN.matcher(message); Matcher braceletOfClayCheckMatcher = BRACELET_OF_CLAY_CHECK_PATTERN.matcher(message); - if (config.recoilNotification() && message.contains(RING_OF_RECOIL_BREAK_MESSAGE)) + if (message.contains(RING_OF_RECOIL_BREAK_MESSAGE)) { - notifier.notify("Your Ring of Recoil has shattered"); + notifier.notify(config.recoilNotification(), "Your Ring of Recoil has shattered"); } else if (dodgyBreakMatcher.find()) { - if (config.dodgyNotification()) - { - notifier.notify("Your dodgy necklace has crumbled to dust."); - } + notifier.notify(config.dodgyNotification(), "Your dodgy necklace has crumbled to dust."); updateDodgyNecklaceCharges(MAX_DODGY_CHARGES); } @@ -310,10 +307,7 @@ else if (amuletOfChemistryUsedMatcher.find()) } else if (amuletOfChemistryBreakMatcher.find()) { - if (config.amuletOfChemistryNotification()) - { - notifier.notify("Your Amulet of Chemistry has crumbled to dust."); - } + notifier.notify(config.amuletOfChemistryNotification(), "Your Amulet of Chemistry has crumbled to dust."); updateAmuletOfChemistryCharges(MAX_AMULET_OF_CHEMISTRY_CHARGES); } @@ -331,10 +325,7 @@ else if (message.equals(AMULET_OF_BOUNTY_BREAK_TEXT)) } else if (message.contains(BINDING_BREAK_TEXT)) { - if (config.bindingNotification()) - { - notifier.notify(BINDING_BREAK_TEXT); - } + notifier.notify(config.bindingNotification(), BINDING_BREAK_TEXT); // This chat message triggers before the used message so add 1 to the max charges to ensure proper sync updateBindingNecklaceCharges(MAX_BINDING_CHARGES + 1); @@ -389,10 +380,7 @@ else if (message.equals(RING_OF_FORGING_USED_TEXT) || message.equals(RING_OF_FOR } else if (message.equals(RING_OF_FORGING_BREAK_TEXT)) { - if (config.ringOfForgingNotification()) - { - notifier.notify("Your ring of forging has melted."); - } + notifier.notify(config.ringOfForgingNotification(), "Your ring of forging has melted."); // This chat message triggers before the used message so add 1 to the max charges to ensure proper sync updateRingOfForgingCharges(MAX_RING_OF_FORGING_CHARGES + 1); @@ -432,10 +420,7 @@ else if (slaughterActivateMatcher.find()) if (found == null) { updateBraceletOfSlaughterCharges(MAX_SLAYER_BRACELET_CHARGES); - if (config.slaughterNotification()) - { - notifier.notify(BRACELET_OF_SLAUGHTER_BREAK_TEXT); - } + notifier.notify(config.slaughterNotification(), BRACELET_OF_SLAUGHTER_BREAK_TEXT); } else { @@ -452,10 +437,7 @@ else if (expeditiousActivateMatcher.find()) if (found == null) { updateExpeditiousBraceletCharges(MAX_SLAYER_BRACELET_CHARGES); - if (config.expeditiousNotification()) - { - notifier.notify(EXPEDITIOUS_BRACELET_BREAK_TEXT); - } + notifier.notify(config.expeditiousNotification(), EXPEDITIOUS_BRACELET_BREAK_TEXT); } else { @@ -505,10 +487,7 @@ else if (message.equals(BRACELET_OF_CLAY_USE_TEXT) || message.equals(BRACELET_OF } else if (message.equals(BRACELET_OF_CLAY_BREAK_TEXT)) { - if (config.braceletOfClayNotification()) - { - notifier.notify("Your bracelet of clay has crumbled to dust"); - } + notifier.notify(config.braceletOfClayNotification(), "Your bracelet of clay has crumbled to dust"); updateBraceletOfClayCharges(MAX_BRACELET_OF_CLAY_CHARGES); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZoneConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZoneConfig.java index d808a21abf..84df55c157 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZoneConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZoneConfig.java @@ -28,6 +28,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; import net.runelite.client.config.Range; import net.runelite.client.config.Units; @@ -51,9 +52,9 @@ default boolean moveOverlay() description = "Toggles notifications when a power surge power-up appears", position = 2 ) - default boolean powerSurgeNotification() + default Notification powerSurgeNotification() { - return false; + return Notification.OFF; } @ConfigItem( @@ -62,9 +63,9 @@ default boolean powerSurgeNotification() description = "Toggles notifications when a recurrent damage power-up appears", position = 3 ) - default boolean recurrentDamageNotification() + default Notification recurrentDamageNotification() { - return false; + return Notification.OFF; } @ConfigItem( @@ -73,9 +74,9 @@ default boolean recurrentDamageNotification() description = "Toggles notifications when a zapper power-up appears", position = 4 ) - default boolean zapperNotification() + default Notification zapperNotification() { - return false; + return Notification.OFF; } @ConfigItem( @@ -84,9 +85,9 @@ default boolean zapperNotification() description = "Toggles notifications when an ultimate force power-up appears", position = 5 ) - default boolean ultimateForceNotification() + default Notification ultimateForceNotification() { - return false; + return Notification.OFF; } @ConfigItem( @@ -95,9 +96,9 @@ default boolean ultimateForceNotification() description = "Toggles notifications when your overload runs out", position = 6 ) - default boolean overloadNotification() + default Notification overloadNotification() { - return true; + return Notification.ON; } @Range( @@ -122,9 +123,9 @@ default int overloadEarlyWarningSeconds() description = "Toggles notifications when your absorption points gets below your threshold", position = 8 ) - default boolean absorptionNotification() + default Notification absorptionNotification() { - return true; + return Notification.ON; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZonePlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZonePlugin.java index 16aab455a5..499be796e3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZonePlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/nightmarezone/NightmareZonePlugin.java @@ -157,12 +157,9 @@ public void onGameTick(GameTick event) return; } - if (config.absorptionNotification()) - { - checkAbsorption(); - } + checkAbsorption(); - if (overloadNotificationSend && config.overloadNotification() && config.overloadEarlyWarningSeconds() > 0) + if (overloadNotificationSend && config.overloadEarlyWarningSeconds() > 0) { checkOverload(); } @@ -189,40 +186,25 @@ public void onChatMessage(ChatMessage event) // Prevents notification from being sent after overload expiry, if the user disables and re-enables warnings overloadNotificationSend = false; - if (config.overloadNotification()) - { - notifier.notify("Your overload has worn off"); - } + notifier.notify(config.overloadNotification(), "Your overload has worn off"); } else if (msg.contains("A power-up has spawned:")) { if (msg.contains("Power surge")) { - if (config.powerSurgeNotification()) - { - notifier.notify(msg); - } + notifier.notify(config.powerSurgeNotification(), msg); } else if (msg.contains("Recurrent damage")) { - if (config.recurrentDamageNotification()) - { - notifier.notify(msg); - } + notifier.notify(config.recurrentDamageNotification(), msg); } else if (msg.contains("Zapper")) { - if (config.zapperNotification()) - { - notifier.notify(msg); - } + notifier.notify(config.zapperNotification(), msg); } else if (msg.contains("Ultimate force")) { - if (config.ultimateForceNotification()) - { - notifier.notify(msg); - } + notifier.notify(config.ultimateForceNotification(), msg); } } else if (msg.contains("You drink some of your overload potion.")) @@ -237,7 +219,7 @@ private void checkOverload() if (Instant.now().isAfter(lastOverload.plus(OVERLOAD_DURATION). minus(Duration.ofSeconds(config.overloadEarlyWarningSeconds())))) { - notifier.notify("Your overload potion is about to expire!"); + notifier.notify(config.overloadNotification(), "Your overload potion is about to expire!"); overloadNotificationSend = false; } } @@ -250,7 +232,7 @@ private void checkAbsorption() { if (absorptionPoints < config.absorptionThreshold()) { - notifier.notify("Absorption points below: " + config.absorptionThreshold()); + notifier.notify(config.absorptionNotification(), "Absorption points below: " + config.absorptionThreshold()); absorptionNotificationSend = true; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaConfig.java index e830c1b0bb..691998050c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaConfig.java @@ -29,6 +29,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; @ConfigGroup("npcUnaggroArea") public interface NpcAggroAreaConfig extends Config @@ -113,9 +114,9 @@ default Color unaggroAreaColor() description = "Send a notification when the unaggressive timer expires", position = 7 ) - default boolean notifyExpire() + default Notification notifyExpire() { - return false; + return Notification.OFF; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaPlugin.java index 49e52b0b13..9237d8087c 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/npcunaggroarea/NpcAggroAreaPlugin.java @@ -340,10 +340,7 @@ public void onGameTick(GameTick event) if (active && notifyOnce && Instant.now().isAfter(endTime)) { - if (config.notifyExpire()) - { - notifier.notify("NPC aggression has expired!"); - } + notifier.notify(config.notifyExpire(), "NPC aggression has expired!"); notifyOnce = false; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/randomevents/RandomEventConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/randomevents/RandomEventConfig.java index 30c948071d..c45f8bd298 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/randomevents/RandomEventConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/randomevents/RandomEventConfig.java @@ -29,6 +29,7 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Notification; @ConfigGroup("randomevents") public interface RandomEventConfig extends Config @@ -58,9 +59,9 @@ default boolean removeMenuOptions() position = -2, section = notificationSection ) - default boolean notifyAllEvents() + default Notification notifyAllEvents() { - return false; + return Notification.OFF; } @ConfigItem( @@ -69,9 +70,9 @@ default boolean notifyAllEvents() description = "", section = notificationSection ) - default boolean notifyArnav() + default Notification notifyArnav() { - return false; + return Notification.OFF; } @ConfigItem( @@ -80,9 +81,9 @@ default boolean notifyArnav() description = "", section = notificationSection ) - default boolean notifyBeekeeper() + default Notification notifyBeekeeper() { - return false; + return Notification.OFF; } @ConfigItem( @@ -91,9 +92,9 @@ default boolean notifyBeekeeper() description = "", section = notificationSection ) - default boolean notifyBob() + default Notification notifyBob() { - return false; + return Notification.OFF; } @ConfigItem( @@ -102,9 +103,9 @@ default boolean notifyBob() description = "", section = notificationSection ) - default boolean notifyCerters() + default Notification notifyCerters() { - return false; + return Notification.OFF; } @ConfigItem( @@ -113,9 +114,9 @@ default boolean notifyCerters() description = "", section = notificationSection ) - default boolean notifyDemon() + default Notification notifyDemon() { - return false; + return Notification.OFF; } @ConfigItem( @@ -124,9 +125,9 @@ default boolean notifyDemon() description = "", section = notificationSection ) - default boolean notifyDunce() + default Notification notifyDunce() { - return false; + return Notification.OFF; } @ConfigItem( @@ -135,9 +136,9 @@ default boolean notifyDunce() description = "", section = notificationSection ) - default boolean notifyDwarf() + default Notification notifyDwarf() { - return false; + return Notification.OFF; } @ConfigItem( @@ -146,9 +147,9 @@ default boolean notifyDwarf() description = "", section = notificationSection ) - default boolean notifyForester() + default Notification notifyForester() { - return false; + return Notification.OFF; } @ConfigItem( @@ -157,9 +158,9 @@ default boolean notifyForester() description = "", section = notificationSection ) - default boolean notifyFlippa() + default Notification notifyFlippa() { - return false; + return Notification.OFF; } @ConfigItem( @@ -168,9 +169,9 @@ default boolean notifyFlippa() description = "", section = notificationSection ) - default boolean notifyFrog() + default Notification notifyFrog() { - return false; + return Notification.OFF; } @ConfigItem( @@ -179,9 +180,9 @@ default boolean notifyFrog() description = "", section = notificationSection ) - default boolean notifyGenie() + default Notification notifyGenie() { - return false; + return Notification.OFF; } @ConfigItem( @@ -190,9 +191,9 @@ default boolean notifyGenie() description = "", section = notificationSection ) - default boolean notifyGravedigger() + default Notification notifyGravedigger() { - return false; + return Notification.OFF; } @ConfigItem( @@ -201,9 +202,9 @@ default boolean notifyGravedigger() description = "", section = notificationSection ) - default boolean notifyJekyll() + default Notification notifyJekyll() { - return false; + return Notification.OFF; } @ConfigItem( @@ -212,9 +213,9 @@ default boolean notifyJekyll() description = "", section = notificationSection ) - default boolean notifyMaze() + default Notification notifyMaze() { - return false; + return Notification.OFF; } @ConfigItem( @@ -223,9 +224,9 @@ default boolean notifyMaze() description = "", section = notificationSection ) - default boolean notifyMime() + default Notification notifyMime() { - return false; + return Notification.OFF; } @ConfigItem( @@ -234,9 +235,9 @@ default boolean notifyMime() description = "", section = notificationSection ) - default boolean notifyMoM() + default Notification notifyMoM() { - return false; + return Notification.OFF; } @ConfigItem( @@ -245,9 +246,9 @@ default boolean notifyMoM() description = "", section = notificationSection ) - default boolean notifyPillory() + default Notification notifyPillory() { - return false; + return Notification.OFF; } @ConfigItem( @@ -256,9 +257,9 @@ default boolean notifyPillory() description = "", section = notificationSection ) - default boolean notifyPrison() + default Notification notifyPrison() { - return false; + return Notification.OFF; } @ConfigItem( @@ -267,9 +268,9 @@ default boolean notifyPrison() description = "", section = notificationSection ) - default boolean notifyQuiz() + default Notification notifyQuiz() { - return false; + return Notification.OFF; } @ConfigItem( @@ -278,9 +279,9 @@ default boolean notifyQuiz() description = "", section = notificationSection ) - default boolean notifySandwich() + default Notification notifySandwich() { - return false; + return Notification.OFF; } @ConfigItem( @@ -289,9 +290,9 @@ default boolean notifySandwich() description = "", section = notificationSection ) - default boolean notifyTurpentine() + default Notification notifyTurpentine() { - return false; + return Notification.OFF; } @ConfigItem( @@ -300,9 +301,9 @@ default boolean notifyTurpentine() description = "", section = notificationSection ) - default boolean notifyTwin() + default Notification notifyTwin() { - return false; + return Notification.OFF; } @ConfigItem( @@ -311,8 +312,8 @@ default boolean notifyTwin() description = "", section = notificationSection ) - default boolean notifyCountCheck() + default Notification notifyCountCheck() { - return false; + return Notification.OFF; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/randomevents/RandomEventPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/randomevents/RandomEventPlugin.java index 04f8b89b5f..9386462efd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/randomevents/RandomEventPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/randomevents/RandomEventPlugin.java @@ -42,6 +42,7 @@ import net.runelite.api.events.NpcDespawned; import net.runelite.client.Notifier; import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.Notification; import net.runelite.client.eventbus.Subscribe; import net.runelite.client.plugins.Plugin; import net.runelite.client.plugins.PluginDescriptor; @@ -136,10 +137,7 @@ public void onInteractingChanged(InteractingChanged event) { lastNotificationTick = client.getTickCount(); - if (shouldNotify(currentRandomEvent.getId())) - { - notifier.notify("Random event spawned: " + currentRandomEvent.getName()); - } + notifier.notify(shouldNotify(currentRandomEvent.getId()), "Random event spawned: " + currentRandomEvent.getName()); } } @@ -169,73 +167,97 @@ public void onMenuEntryAdded(MenuEntryAdded event) } } - private boolean shouldNotify(int id) + private Notification shouldNotify(int id) { - if (config.notifyAllEvents()) - { - return true; - } - + Notification notification = null; switch (id) { case NpcID.BEE_KEEPER_6747: - return config.notifyBeekeeper(); + notification = config.notifyBeekeeper(); + break; case NpcID.CAPT_ARNAV: - return config.notifyArnav(); + notification = config.notifyArnav(); + break; case NpcID.DRUNKEN_DWARF: - return config.notifyDwarf(); + notification = config.notifyDwarf(); + break; case NpcID.SERGEANT_DAMIEN_6743: - return config.notifyDemon(); + notification = config.notifyDemon(); + break; case NpcID.FREAKY_FORESTER_6748: - return config.notifyForester(); + notification = config.notifyForester(); + break; case NpcID.FROG_5429: - return config.notifyFrog(); + notification = config.notifyFrog(); + break; case NpcID.GENIE: case NpcID.GENIE_327: - return config.notifyGenie(); + notification = config.notifyGenie(); + break; case NpcID.GILES: case NpcID.GILES_5441: case NpcID.NILES: case NpcID.NILES_5439: case NpcID.MILES: case NpcID.MILES_5440: - return config.notifyCerters(); + notification = config.notifyCerters(); + break; case NpcID.DR_JEKYLL: case NpcID.DR_JEKYLL_314: - return config.notifyJekyll(); + notification = config.notifyJekyll(); + break; case NpcID.EVIL_BOB: - return config.notifyBob(); + notification = config.notifyBob(); + break; case NpcID.EVIL_BOB_6754: - return config.notifyPrison(); + notification = config.notifyPrison(); + break; case NpcID.LEO_6746: - return config.notifyGravedigger(); + notification = config.notifyGravedigger(); + break; case NpcID.MYSTERIOUS_OLD_MAN_6750: case NpcID.MYSTERIOUS_OLD_MAN_6751: - return config.notifyMoM(); + notification = config.notifyMoM(); + break; case NpcID.MYSTERIOUS_OLD_MAN_6752: - return config.notifyMaze(); + notification = config.notifyMaze(); + break; case NpcID.MYSTERIOUS_OLD_MAN_6753: - return config.notifyMime(); + notification = config.notifyMime(); + break; case NpcID.PILLORY_GUARD: - return config.notifyPillory(); + notification = config.notifyPillory(); + break; case NpcID.POSTIE_PETE_6738: - return config.notifyTwin(); + notification = config.notifyTwin(); + break; case NpcID.QUIZ_MASTER_6755: - return config.notifyQuiz(); + notification = config.notifyQuiz(); + break; case NpcID.RICK_TURPENTINE: case NpcID.RICK_TURPENTINE_376: - return config.notifyTurpentine(); + notification = config.notifyTurpentine(); + break; case NpcID.DUNCE_6749: - return config.notifyDunce(); + notification = config.notifyDunce(); + break; case NpcID.SANDWICH_LADY: - return config.notifySandwich(); + notification = config.notifySandwich(); + break; case NpcID.FLIPPA_6744: - return config.notifyFlippa(); + notification = config.notifyFlippa(); + break; case NpcID.COUNT_CHECK_12551: case NpcID.COUNT_CHECK_12552: - return config.notifyCountCheck(); - default: - return false; + notification = config.notifyCountCheck(); + break; } + + if (notification != null && notification.isEnabled()) + { + return notification; + } + + return config.notifyAllEvents(); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftConfig.java index cddc8da594..b0f263cbfc 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftConfig.java @@ -29,6 +29,7 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Notification; @ConfigGroup(RunecraftConfig.GROUP) public interface RunecraftConfig extends Config @@ -239,8 +240,8 @@ default boolean hightlightDarkMage() description = "Send a notification when a pouch degrades", position = 19 ) - default boolean degradingNotification() + default Notification degradingNotification() { - return true; + return Notification.ON; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java index 55339b884a..563658238a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/runecraft/RunecraftPlugin.java @@ -130,12 +130,9 @@ public void onChatMessage(ChatMessage event) return; } - if (config.degradingNotification()) + if (event.getMessage().contains(POUCH_DECAYED_MESSAGE)) { - if (event.getMessage().contains(POUCH_DECAYED_MESSAGE)) - { - notifier.notify(POUCH_DECAYED_NOTIFICATION_MESSAGE); - } + notifier.notify(config.degradingNotification(), POUCH_DECAYED_NOTIFICATION_MESSAGE); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java index f8b7551ee8..4e37d88ba7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerConfig.java @@ -30,6 +30,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; import net.runelite.client.config.Units; @ConfigGroup(SlayerConfig.GROUP_NAME) @@ -73,9 +74,9 @@ default boolean showItemOverlay() name = "Superior foe notification", description = "Toggles notifications on superior foe encounters" ) - default boolean showSuperiorNotification() + default Notification showSuperiorNotification() { - return true; + return Notification.ON; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java index 46de4c2154..93d98e015a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/slayer/SlayerPlugin.java @@ -458,9 +458,9 @@ public void onChatMessage(ChatMessage event) String chatMsg = Text.removeTags(event.getMessage()); //remove color and linebreaks - if (chatMsg.equals(CHAT_SUPERIOR_MESSAGE) && config.showSuperiorNotification()) + if (chatMsg.equals(CHAT_SUPERIOR_MESSAGE)) { - notifier.notify(CHAT_SUPERIOR_MESSAGE); + notifier.notify(config.showSuperiorNotification(), CHAT_SUPERIOR_MESSAGE); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java index 9773544618..8af77dd7cd 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterConfig.java @@ -29,6 +29,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; @ConfigGroup("specialcounter") public interface SpecialCounterConfig extends Config @@ -39,9 +40,9 @@ public interface SpecialCounterConfig extends Config name = "Threshold Notifications", description = "Sends a notification when your special attack counter exceeds the threshold" ) - default boolean thresholdNotification() + default Notification thresholdNotification() { - return false; + return Notification.OFF; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java index f0acc918c9..23a6e1e6bf 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/specialcounter/SpecialCounterPlugin.java @@ -446,9 +446,9 @@ private void updateCounter(SpecialWeapon specialWeapon, String name, int hit) private void sendNotification(SpecialWeapon weapon, SpecialCounter counter) { int threshold = weapon.getThreshold().apply(config); - if (threshold > 0 && counter.getCount() >= threshold && config.thresholdNotification()) + if (threshold > 0 && counter.getCount() >= threshold) { - notifier.notify(weapon.getName() + " special attack threshold reached!"); + notifier.notify(config.thresholdNotification(), weapon.getName() + " special attack threshold reached!"); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java index e569d82b11..77ed4af5b6 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/TimeTrackingConfig.java @@ -27,6 +27,7 @@ import net.runelite.client.config.Config; import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; +import net.runelite.client.config.Notification; import net.runelite.client.config.Units; @ConfigGroup("timetracking") @@ -62,9 +63,9 @@ default TimeFormatMode timeFormatMode() description = "Notify you whenever a timer has finished counting down", position = 2 ) - default boolean timerNotification() + default Notification timerNotification() { - return false; + return Notification.OFF; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockManager.java b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockManager.java index 30d16ff573..d43c295809 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockManager.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/timetracking/clocks/ClockManager.java @@ -121,10 +121,7 @@ public boolean checkCompletion() timer.pause(); changed = true; - if (config.timerNotification()) - { - notifier.notify("[" + timer.getName() + "] has finished counting down."); - } + notifier.notify(config.timerNotification(), "[" + timer.getName() + "] has finished counting down."); if (timer.isLoop()) { diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingConfig.java index d668b8ab96..2a08d2d9e4 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingConfig.java @@ -28,6 +28,7 @@ import net.runelite.client.config.ConfigGroup; import net.runelite.client.config.ConfigItem; import net.runelite.client.config.ConfigSection; +import net.runelite.client.config.Notification; import net.runelite.client.config.Units; import net.runelite.client.plugins.woodcutting.config.ClueNestTier; @@ -59,9 +60,9 @@ default int statTimeout() name = "Bird nest notification", description = "Configures whether to notify you of a bird nest spawn" ) - default boolean showNestNotification() + default Notification showNestNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -127,9 +128,9 @@ default boolean forestryLeprechaunNotification() description = "Configures whether to notify you of a Rising Roots event", section = forestrySection ) - default boolean forestryRisingRootsNotification() + default Notification forestryRisingRootsNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -139,9 +140,9 @@ default boolean forestryRisingRootsNotification() description = "Configures whether to notify you of a Struggling Sapling event", section = forestrySection ) - default boolean forestryStrugglingSaplingNotification() + default Notification forestryStrugglingSaplingNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -151,9 +152,9 @@ default boolean forestryStrugglingSaplingNotification() description = "Configures whether to notify you of a Flowering Tree event", section = forestrySection ) - default boolean forestryFloweringTreeNotification() + default Notification forestryFloweringTreeNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -163,9 +164,9 @@ default boolean forestryFloweringTreeNotification() description = "Configures whether to notify you of a Poachers event", section = forestrySection ) - default boolean forestryPoachersNotification() + default Notification forestryPoachersNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -175,9 +176,9 @@ default boolean forestryPoachersNotification() description = "Configures whether to notify you of a Pheasant Control event", section = forestrySection ) - default boolean forestryPheasantControlNotification() + default Notification forestryPheasantControlNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -187,9 +188,9 @@ default boolean forestryPheasantControlNotification() description = "Configures whether to notify you of a Bee Hive event", section = forestrySection ) - default boolean forestryBeeHiveNotification() + default Notification forestryBeeHiveNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -199,9 +200,9 @@ default boolean forestryBeeHiveNotification() description = "Configures whether to notify you of an Enchantment Ritual event", section = forestrySection ) - default boolean forestryEnchantmentRitualNotification() + default Notification forestryEnchantmentRitualNotification() { - return true; + return Notification.ON; } @ConfigItem( @@ -211,9 +212,9 @@ default boolean forestryEnchantmentRitualNotification() description = "Configures whether to notify you of a Friendly Ent event", section = forestrySection ) - default boolean forestryFriendlyEntNotification() + default Notification forestryFriendlyEntNotification() { - return true; + return Notification.ON; } @ConfigItem( diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java index d4e721e038..1ec3069d45 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/woodcutting/WoodcuttingPlugin.java @@ -263,11 +263,11 @@ public void onChatMessage(ChatMessage event) session.incrementBark(num); } - if (msg.contains("A bird's nest falls out of the tree") && config.showNestNotification()) + if (msg.contains("A bird's nest falls out of the tree")) { if (clueTierSpawned == null || clueTierSpawned.ordinal() >= config.clueNestNotifyTier().ordinal()) { - notifier.notify("A bird nest has spawned!"); + notifier.notify(config.showNestNotification(), "A bird nest has spawned!"); } // Clear the clue tier that has previously spawned clueTierSpawned = null; @@ -340,9 +340,9 @@ public void onGameObjectSpawned(final GameObjectSpawned event) case ObjectID.TREE_ROOTS: case ObjectID.ANIMAINFUSED_TREE_ROOTS: - if (roots.isEmpty() && config.forestryRisingRootsNotification()) + if (roots.isEmpty()) { - notifier.notify("A Rising Roots Forestry event spawned!"); + notifier.notify(config.forestryRisingRootsNotification(), "A Rising Roots Forestry event spawned!"); } roots.add(gameObject); @@ -353,10 +353,7 @@ public void onGameObjectSpawned(final GameObjectSpawned event) case ObjectID.STRUGGLING_SAPLING_47488: case ObjectID.STRUGGLING_SAPLING_47490: case ObjectID.STRUGGLING_SAPLING_47491: - if (config.forestryStrugglingSaplingNotification()) - { - notifier.notify("A Struggling Sapling Forestry event spawned!"); - } + notifier.notify(config.forestryStrugglingSaplingNotification(), "A Struggling Sapling Forestry event spawned!"); break; case ObjectID.ROTTING_LEAVES: case ObjectID.GREEN_LEAVES: @@ -641,9 +638,9 @@ public void onNpcSpawned(NpcSpawned event) var id = npc.getId(); if (isFloweringBush(id)) { - if (flowers.isEmpty() && config.forestryFloweringTreeNotification()) + if (flowers.isEmpty()) { - notifier.notify("A Flowering Tree Forestry event spawned!"); + notifier.notify(config.forestryFloweringTreeNotification(), "A Flowering Tree Forestry event spawned!"); } flowers.add(npc); @@ -652,9 +649,9 @@ else if (id == NpcID.WOODCUTTING_LEPRECHAUN && config.forestryLeprechaunNotifica { notifier.notify("A Leprechaun event spawned!"); } - else if ((id == NpcID.FRIGHTENED_FOX || id == NpcID.FRIGHTENED_FOX_12560) && config.forestryPoachersNotification()) + else if ((id == NpcID.FRIGHTENED_FOX || id == NpcID.FRIGHTENED_FOX_12560)) { - notifier.notify("A Poachers event spawned!"); + notifier.notify(config.forestryPoachersNotification(), "A Poachers event spawned!"); } else if (id == NpcID.FOX_TRAP) { @@ -664,17 +661,11 @@ else if (id == NpcID.FREAKY_FORESTER_12536) { freakyForester = npc; - if (config.forestryPheasantControlNotification()) - { - notifier.notify("A Pheasant Control event has spawned!"); - } + notifier.notify(config.forestryPheasantControlNotification(), "A Pheasant Control event has spawned!"); } else if (id == NpcID.WILD_BEEHIVE) { - if (config.forestryBeeHiveNotification()) - { - notifier.notify("A Bee Hive event has spawned!"); - } + notifier.notify(config.forestryBeeHiveNotification(), "A Bee Hive event has spawned!"); } else if (id == NpcID.UNFINISHED_BEEHIVE || id == NpcID.UNFINISHED_BEEHIVE_12516) { @@ -686,17 +677,14 @@ else if (id >= NpcID.RITUAL_CIRCLE_GREEN && id <= NpcID.RITUAL_CIRCLE_RED_12535) } else if (id == NpcID.DRYAD_12519) { - if (config.forestryEnchantmentRitualNotification()) - { - notifier.notify("An Enchantment Ritual event has spawned!"); - } + notifier.notify(config.forestryEnchantmentRitualNotification(), "An Enchantment Ritual event has spawned!"); } else if (id == NpcID.ENTLING) { entlings.add(npc); - if (entlings.size() == 1 && config.forestryFriendlyEntNotification()) + if (entlings.size() == 1) { - notifier.notify("A Friendly Ent event has spawned!"); + notifier.notify(config.forestryFriendlyEntNotification(), "A Friendly Ent event has spawned!"); } } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java index e63dbf8a2c..a13e29e046 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/cannon/CannonPluginTest.java @@ -35,6 +35,7 @@ import net.runelite.api.events.ChatMessage; import net.runelite.api.events.VarbitChanged; import net.runelite.client.Notifier; +import net.runelite.client.config.Notification; import net.runelite.client.game.ItemManager; import net.runelite.client.ui.overlay.OverlayManager; import net.runelite.client.ui.overlay.infobox.InfoBoxManager; @@ -44,6 +45,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; import org.mockito.Mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; @@ -138,7 +140,7 @@ public void testCannonInfoBox() @Test public void testThresholdNotificationShouldNotify() { - when(config.showCannonNotifications()).thenReturn(true); + when(config.showCannonNotifications()).thenReturn(Notification.ON); when(config.lowWarningThreshold()).thenReturn(10); cannonAmmoChanged.setValue(30); @@ -146,13 +148,13 @@ public void testThresholdNotificationShouldNotify() cannonAmmoChanged.setValue(10); plugin.onVarbitChanged(cannonAmmoChanged); - verify(notifier, times(1)).notify("Your cannon has 10 cannon balls remaining!"); + verify(notifier, times(1)).notify(Notification.ON, "Your cannon has 10 cannon balls remaining!"); } @Test public void testThresholdNotificationShouldNotifyOnce() { - when(config.showCannonNotifications()).thenReturn(true); + when(config.showCannonNotifications()).thenReturn(Notification.ON); when(config.lowWarningThreshold()).thenReturn(10); for (int cballs = 15; cballs >= 8; --cballs) @@ -161,13 +163,12 @@ public void testThresholdNotificationShouldNotifyOnce() plugin.onVarbitChanged(cannonAmmoChanged); } - verify(notifier, times(1)).notify("Your cannon has 10 cannon balls remaining!"); + verify(notifier, times(1)).notify(Notification.ON, "Your cannon has 10 cannon balls remaining!"); } @Test public void testThresholdNotificationsShouldNotNotify() { - when(config.showCannonNotifications()).thenReturn(true); when(config.lowWarningThreshold()).thenReturn(0); cannonAmmoChanged.setValue(30); @@ -175,17 +176,17 @@ public void testThresholdNotificationsShouldNotNotify() cannonAmmoChanged.setValue(10); plugin.onVarbitChanged(cannonAmmoChanged); - verify(notifier, never()).notify("Your cannon has 10 cannon balls remaining!"); + verify(notifier, never()).notify(any(Notification.class), eq("Your cannon has 10 cannon balls remaining!")); } @Test public void testCannonOutOfAmmo() { - when(config.showCannonNotifications()).thenReturn(true); + when(config.showCannonNotifications()).thenReturn(Notification.ON); ChatMessage cannonOutOfAmmo = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", "Your cannon is out of ammo!", "", 0); plugin.onChatMessage(cannonOutOfAmmo); - verify(notifier, times(1)).notify("Your cannon is out of ammo!"); + verify(notifier, times(1)).notify(Notification.ON, "Your cannon is out of ammo!"); } } diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java index 1ec30ad41f..5dde2fcb81 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/chatnotifications/ChatNotificationsPluginTest.java @@ -86,12 +86,15 @@ public void onChatMessage() { when(config.highlightWordsString()).thenReturn("Deathbeam, Deathbeam OSRS , test"); + var message = "Deathbeam, Deathbeam OSRS"; MessageNode messageNode = mock(MessageNode.class); - when(messageNode.getValue()).thenReturn("Deathbeam, Deathbeam OSRS"); + when(messageNode.getValue()).thenReturn(message); ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); // load highlight config chatNotificationsPlugin.onChatMessage(chatMessage); @@ -104,12 +107,15 @@ public void testRegexMultiplePatternsMessage() { when(config.highlightRegexString()).thenReturn("brandie+\ntest"); + var message = "brandieeee testing"; MessageNode messageNode = mock(MessageNode.class); - when(messageNode.getValue()).thenReturn("brandieeee testing"); + when(messageNode.getValue()).thenReturn(message); ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); chatNotificationsPlugin.onChatMessage(chatMessage); @@ -122,12 +128,15 @@ public void testRegexMultiplePatternsWithOnlyOneMatch() { when(config.highlightRegexString()).thenReturn("brandie+\nwillNotMatch"); + var message = "brandieeee testing"; MessageNode messageNode = mock(MessageNode.class); - when(messageNode.getValue()).thenReturn("brandieeee testing"); + when(messageNode.getValue()).thenReturn(message); ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); chatNotificationsPlugin.onChatMessage(chatMessage); @@ -147,6 +156,8 @@ public void testLtGt() ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); // load highlight config chatNotificationsPlugin.onChatMessage(chatMessage); @@ -166,6 +177,8 @@ public void testMatchEntireMessage() ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); // load highlight config chatNotificationsPlugin.onChatMessage(chatMessage); @@ -185,6 +198,8 @@ public void testFullStop() ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); // load highlight config chatNotificationsPlugin.onChatMessage(chatMessage); @@ -204,6 +219,8 @@ public void testColor() ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); // load highlight config chatNotificationsPlugin.onChatMessage(chatMessage); @@ -223,6 +240,8 @@ public void testPrecedingColor() ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); // load highlight config chatNotificationsPlugin.onChatMessage(chatMessage); @@ -242,6 +261,8 @@ public void testEmoji() ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setMessage(message); + chatMessage.setName(""); chatNotificationsPlugin.startUp(); // load highlight config chatNotificationsPlugin.onChatMessage(chatMessage); @@ -261,6 +282,8 @@ public void testNonMatchedColors() ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.PUBLICCHAT); chatMessage.setMessageNode(messageNode); + chatMessage.setName(""); + chatMessage.setMessage(message); chatNotificationsPlugin.startUp(); // load highlight config chatNotificationsPlugin.onChatMessage(chatMessage); diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/grandexchange/GrandExchangePluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/grandexchange/GrandExchangePluginTest.java index 71fec9339f..0b7b0a900d 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/grandexchange/GrandExchangePluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/grandexchange/GrandExchangePluginTest.java @@ -49,6 +49,7 @@ import net.runelite.client.Notifier; import net.runelite.client.account.SessionManager; import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.Notification; import net.runelite.client.config.RuneLiteConfig; import net.runelite.client.game.ItemManager; import net.runelite.client.input.KeyManager; @@ -337,7 +338,7 @@ public void testLogin() @Test public void testNotifyPartial() { - when(grandExchangeConfig.enableNotifications()).thenReturn(true); + when(grandExchangeConfig.enableNotifications()).thenReturn(Notification.ON); ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.GAMEMESSAGE); @@ -345,13 +346,13 @@ public void testNotifyPartial() grandExchangePlugin.onChatMessage(chatMessage); - verify(notifier).notify(anyString()); + verify(notifier).notify(any(Notification.class), anyString()); } @Test public void testNotifyComplete() { - when(grandExchangeConfig.notifyOnOfferComplete()).thenReturn(true); + when(grandExchangeConfig.notifyOnOfferComplete()).thenReturn(Notification.ON); ChatMessage chatMessage = new ChatMessage(); chatMessage.setType(ChatMessageType.GAMEMESSAGE); @@ -359,6 +360,6 @@ public void testNotifyComplete() grandExchangePlugin.onChatMessage(chatMessage); - verify(notifier).notify(anyString()); + verify(notifier).notify(any(Notification.class), anyString()); } } \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java index 1991b0d1ff..7a41b880a6 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/idlenotifier/IdleNotifierPluginTest.java @@ -47,6 +47,7 @@ import net.runelite.api.events.HitsplatApplied; import net.runelite.api.events.InteractingChanged; import net.runelite.client.Notifier; +import net.runelite.client.config.Notification; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; @@ -123,9 +124,9 @@ public void setUp() when(client.getLocalPlayer()).thenReturn(player); // Mock config - when(config.logoutIdle()).thenReturn(true); - when(config.animationIdle()).thenReturn(true); - when(config.interactionIdle()).thenReturn(true); + when(config.logoutIdle()).thenReturn(Notification.ON); + when(config.animationIdle()).thenReturn(Notification.ON); + when(config.interactionIdle()).thenReturn(Notification.ON); when(config.getIdleNotificationDelay()).thenReturn(0); when(config.getHitpointsThreshold()).thenReturn(42); when(config.getPrayerThreshold()).thenReturn(42); @@ -148,7 +149,7 @@ public void checkAnimationIdle() when(player.getAnimation()).thenReturn(AnimationID.IDLE); plugin.onAnimationChanged(animationChanged); plugin.onGameTick(new GameTick()); - verify(notifier).notify("You are now idle!"); + verify(notifier).notify(Notification.ON, "You are now idle!"); } @Test @@ -204,7 +205,7 @@ public void checkCombatIdle() when(player.getInteracting()).thenReturn(null); plugin.onInteractingChanged(new InteractingChanged(player, null)); plugin.onGameTick(new GameTick()); - verify(notifier).notify("You are now out of combat!"); + verify(notifier).notify(Notification.ON, "You are now out of combat!"); } @Test @@ -270,7 +271,7 @@ public void doubleNotifyOnMouseReset() plugin.onGameTick(new GameTick()); plugin.onGameTick(new GameTick()); - verify(notifier, times(1)).notify(any()); + verify(notifier, times(1)).notify(any(Notification.class), any()); } @Test @@ -295,7 +296,7 @@ public void testSendOneNotificationForAnimationAndInteract() plugin.onInteractingChanged(new InteractingChanged(player, null)); plugin.onGameTick(new GameTick()); - verify(notifier).notify("You are now idle!"); + verify(notifier).notify(Notification.ON, "You are now idle!"); } @Test @@ -315,7 +316,7 @@ public void testSpecRegen() @Test public void testMovementIdle() { - when(config.movementIdle()).thenReturn(true); + when(config.movementIdle()).thenReturn(Notification.ON); when(player.getWorldLocation()).thenReturn(new WorldPoint(0, 0, 0)); plugin.onGameTick(new GameTick()); @@ -324,6 +325,6 @@ public void testMovementIdle() // No movement here plugin.onGameTick(new GameTick()); - verify(notifier).notify(eq("You have stopped moving!")); + verify(notifier).notify(Notification.ON, "You have stopped moving!"); } } \ No newline at end of file diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java index 0fd2c1c632..7f36e52e4d 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/slayer/SlayerPluginTest.java @@ -51,6 +51,7 @@ import net.runelite.client.chat.ChatClient; import net.runelite.client.chat.ChatCommandManager; import net.runelite.client.config.ConfigManager; +import net.runelite.client.config.Notification; import net.runelite.client.game.ItemManager; import net.runelite.client.game.npcoverlay.NpcOverlayService; import net.runelite.client.ui.overlay.OverlayManager; @@ -71,7 +72,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -181,13 +181,9 @@ public void testSuperiorNotification() { ChatMessage chatMessageEvent = new ChatMessage(null, GAMEMESSAGE, "Superior", SUPERIOR_MESSAGE, null, 0); - when(slayerConfig.showSuperiorNotification()).thenReturn(true); + when(slayerConfig.showSuperiorNotification()).thenReturn(Notification.ON); slayerPlugin.onChatMessage(chatMessageEvent); - verify(notifier).notify(SUPERIOR_MESSAGE); - - when(slayerConfig.showSuperiorNotification()).thenReturn(false); - slayerPlugin.onChatMessage(chatMessageEvent); - verifyNoMoreInteractions(notifier); + verify(notifier).notify(Notification.ON, SUPERIOR_MESSAGE); } @Test diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java index 6d83d32766..a1c26dc38a 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/specialcounter/SpecialCounterPluginTest.java @@ -48,6 +48,7 @@ import net.runelite.api.events.VarbitChanged; import net.runelite.client.Notifier; import net.runelite.client.callback.ClientThread; +import net.runelite.client.config.Notification; import net.runelite.client.game.ItemManager; import net.runelite.client.party.PartyService; import net.runelite.client.party.WSClient; @@ -194,7 +195,7 @@ public void testNotification() when(client.getLocalPlayer()).thenReturn(player); when(specialCounterConfig.bandosGodswordThreshold()).thenReturn(2); - when(specialCounterConfig.thresholdNotification()).thenReturn(true); + when(specialCounterConfig.thresholdNotification()).thenReturn(Notification.ON); when(client.getTickCount()).thenReturn(0); @@ -240,7 +241,7 @@ public void testNotification() specialCounterPlugin.onGameTick(new GameTick()); - verify(notifier).notify("Bandos Godsword special attack threshold reached!"); + verify(notifier).notify(Notification.ON, "Bandos Godsword special attack threshold reached!"); } @Test @@ -253,7 +254,7 @@ public void testNotificationNotThreshold() when(client.getLocalPlayer()).thenReturn(player); when(player.getInteracting()).thenReturn(target); when(specialCounterConfig.bandosGodswordThreshold()).thenReturn(3); - lenient().when(specialCounterConfig.thresholdNotification()).thenReturn(true); + lenient().when(specialCounterConfig.thresholdNotification()).thenReturn(Notification.ON); when(client.getTickCount()).thenReturn(0); diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/woodcutting/WoodcuttingPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/woodcutting/WoodcuttingPluginTest.java index 59ef70d7d6..36fb61f468 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/woodcutting/WoodcuttingPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/woodcutting/WoodcuttingPluginTest.java @@ -41,6 +41,7 @@ import net.runelite.api.events.GameObjectSpawned; import net.runelite.api.events.ItemSpawned; import net.runelite.client.Notifier; +import net.runelite.client.config.Notification; import net.runelite.client.plugins.woodcutting.config.ClueNestTier; import net.runelite.client.ui.overlay.OverlayManager; import static org.junit.Assert.assertEquals; @@ -52,7 +53,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyNoInteractions; -import static org.mockito.Mockito.verifyNoMoreInteractions; import static org.mockito.Mockito.when; import org.mockito.junit.MockitoJUnitRunner; @@ -131,13 +131,9 @@ public void testBirdsNest() { ChatMessage chatMessage = new ChatMessage(null, ChatMessageType.GAMEMESSAGE, "", BIRDS_NEST_MESSAGE, "", 0); - when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.showNestNotification()).thenReturn(Notification.ON); woodcuttingPlugin.onChatMessage(chatMessage); - verify(notifier).notify("A bird nest has spawned!"); - - when(woodcuttingConfig.showNestNotification()).thenReturn(false); - woodcuttingPlugin.onChatMessage(chatMessage); - verifyNoMoreInteractions(notifier); + verify(notifier).notify(Notification.ON, "A bird nest has spawned!"); } @Test @@ -149,12 +145,12 @@ public void testClueNestConfigSameAsSpawn() when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER); ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem); - when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.showNestNotification()).thenReturn(Notification.ON); when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER); woodcuttingPlugin.onItemSpawned(beginnerClueSpawned); woodcuttingPlugin.onChatMessage(nestChatMessage); woodcuttingPlugin.onGameTick(null); - verify(notifier).notify("A bird nest has spawned!"); + verify(notifier).notify(Notification.ON, "A bird nest has spawned!"); } @Test @@ -166,12 +162,12 @@ public void testClueNestConfigSmallerThanSpawn() when(eliteTileItem.getId()).thenReturn(ItemID.CLUE_NEST_ELITE); ItemSpawned eliteClueSpawned = new ItemSpawned(tile, eliteTileItem); - when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.showNestNotification()).thenReturn(Notification.ON); when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER); woodcuttingPlugin.onItemSpawned(eliteClueSpawned); woodcuttingPlugin.onChatMessage(nestChatMessage); woodcuttingPlugin.onGameTick(null); - verify(notifier).notify("A bird nest has spawned!"); + verify(notifier).notify(Notification.ON, "A bird nest has spawned!"); } @Test @@ -183,7 +179,6 @@ public void testClueNestDisabledConfig() when(eliteTileItem.getId()).thenReturn(ItemID.CLUE_NEST_ELITE); ItemSpawned eliteClueSpawned = new ItemSpawned(tile, eliteTileItem); - when(woodcuttingConfig.showNestNotification()).thenReturn(true); when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.DISABLED); woodcuttingPlugin.onItemSpawned(eliteClueSpawned); woodcuttingPlugin.onChatMessage(nestChatMessage); @@ -200,7 +195,6 @@ public void testClueNestConfigLargerThanSpawn() when(beginnerTileItem.getId()).thenReturn(ItemID.CLUE_NEST_BEGINNER); ItemSpawned beginnerClueSpawned = new ItemSpawned(tile, beginnerTileItem); - when(woodcuttingConfig.showNestNotification()).thenReturn(true); when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.HARD); woodcuttingPlugin.onItemSpawned(beginnerClueSpawned); woodcuttingPlugin.onChatMessage(nestChatMessage); @@ -220,7 +214,7 @@ public void testClueNestPlayerDrop() when(nestTileItem.getId()).thenReturn(ItemID.BIRD_NEST_22798); ItemSpawned regularNestSpawned = new ItemSpawned(tile, nestTileItem); - when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.showNestNotification()).thenReturn(Notification.ON); // Player drops clue nest woodcuttingPlugin.onItemSpawned(beginnerClueSpawned); @@ -230,7 +224,7 @@ public void testClueNestPlayerDrop() woodcuttingPlugin.onItemSpawned(regularNestSpawned); woodcuttingPlugin.onChatMessage(nestChatMessage); woodcuttingPlugin.onGameTick(null); - verify(notifier).notify("A bird nest has spawned!"); + verify(notifier).notify(Notification.ON, "A bird nest has spawned!"); } @Test @@ -244,7 +238,7 @@ public void testClueNestOtherItemSpawn() TileItem anotherItemTileItem = mock(TileItem.class); ItemSpawned anotherItemSpawned = new ItemSpawned(tile, anotherItemTileItem); - when(woodcuttingConfig.showNestNotification()).thenReturn(true); + when(woodcuttingConfig.showNestNotification()).thenReturn(Notification.ON); when(woodcuttingConfig.clueNestNotifyTier()).thenReturn(ClueNestTier.BEGINNER); woodcuttingPlugin.onItemSpawned(beginnerClueSpawned); @@ -252,7 +246,7 @@ public void testClueNestOtherItemSpawn() woodcuttingPlugin.onChatMessage(nestChatMessage); woodcuttingPlugin.onGameTick(null); - verify(notifier).notify("A bird nest has spawned!"); + verify(notifier).notify(Notification.ON, "A bird nest has spawned!"); } @Test From 38f0725986f3720a4b84e13795ce5a04cecaa81d Mon Sep 17 00:00:00 2001 From: YvesW <7929021+YvesW@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:13:56 +0200 Subject: [PATCH 54/67] world map: add Fortis Colosseum teleport (#17727) --- .../runelite/client/plugins/worldmap/TeleportLocationData.java | 1 + 1 file changed, 1 insertion(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java index d09cec0d5c..9297b8f910 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/worldmap/TeleportLocationData.java @@ -81,6 +81,7 @@ enum TeleportLocationData PVP_ARENA(TeleportType.JEWELLERY, "Ring of Dueling" , "Al Kharid PvP Arena", new WorldPoint(3315, 3235, 0), "ring_of_dueling_teleport_icon.png"), FEROX_ENCLAVE(TeleportType.JEWELLERY, "Ring of Dueling" , "Ferox Enclave", new WorldPoint(3151, 3636, 0), "ring_of_dueling_teleport_icon.png"), CASTLE_WARS(TeleportType.JEWELLERY, "Ring of Dueling" , "Castle Wars", new WorldPoint(2441, 3091, 0), "ring_of_dueling_teleport_icon.png"), + FORTIS_COLOSSEUM(TeleportType.JEWELLERY, "Ring of Dueling" , "Fortis Colosseum", new WorldPoint(1793, 3107, 0), "ring_of_dueling_teleport_icon.png"), WARRIORS_GUILD(TeleportType.JEWELLERY, "Combat Bracelet" , "Warriors' Guild", new WorldPoint(2883, 3549, 0), "combat_bracelet_teleport_icon.png"), CHAMPIONS_GUILD(TeleportType.JEWELLERY, "Combat Bracelet" , "Champions' Guild", new WorldPoint(3189, 3368, 0), "combat_bracelet_teleport_icon.png"), EDGEVILLE_MONASTERY(TeleportType.JEWELLERY, "Combat Bracelet" , "Edgeville Monastery", new WorldPoint(3053, 3487, 0), "combat_bracelet_teleport_icon.png"), From fcfb9e7480ee677aa8b46170409936468171cce6 Mon Sep 17 00:00:00 2001 From: Macweese <50101641+Macweese@users.noreply.github.com> Date: Tue, 16 Apr 2024 08:28:22 +0200 Subject: [PATCH 55/67] game: add isDying override for tutorial island giant rat (#17733) --- .../src/main/java/net/runelite/client/game/NpcUtil.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/game/NpcUtil.java b/runelite-client/src/main/java/net/runelite/client/game/NpcUtil.java index 9b3abb0045..c459829992 100644 --- a/runelite-client/src/main/java/net/runelite/client/game/NpcUtil.java +++ b/runelite-client/src/main/java/net/runelite/client/game/NpcUtil.java @@ -132,6 +132,8 @@ public boolean isDying(final NPC npc) case NpcID.RUNITE_ROCKS: case NpcID.STRANGE_CREATURE_12076: // Secrets of the North transitioning to Jhallan case NpcID.BOUNCER_3509: + // Tutorial island giant rats respawn instantly. + case NpcID.GIANT_RAT_3313: // Agrith Naar restores health upon reaching 0hp if the player does not have Silverlight // equipped, or moved away immediately after applying the killing blow. case NpcID.AGRITH_NAAR: From 501a1b31daa5da5d92ddad5f17cde6f045363861 Mon Sep 17 00:00:00 2001 From: Anton Olsson Date: Sat, 23 Mar 2024 12:44:33 +0100 Subject: [PATCH 56/67] clues: remove butterfly jar requirement --- .../client/plugins/cluescrolls/clues/SkillChallengeClue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java index 8e7878c271..ab08b6f68d 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/SkillChallengeClue.java @@ -196,7 +196,7 @@ enum ChallengeType .orElseGet(Stream::empty) .map(ItemRequirements::item) .toArray(SingleItemRequirement[]::new))), - new SkillChallengeClue("Catch a black warlock.", item(ItemID.BUTTERFLY_JAR), any("Butterfly Net", item(ItemID.BUTTERFLY_NET), item(ItemID.MAGIC_BUTTERFLY_NET))), + new SkillChallengeClue("Catch a black warlock.", any("Butterfly Net", item(ItemID.BUTTERFLY_NET), item(ItemID.MAGIC_BUTTERFLY_NET))), new SkillChallengeClue("Catch a red chinchompa.", item(ItemID.BOX_TRAP)), new SkillChallengeClue("Mine a mithril ore.", ANY_PICKAXE), new SkillChallengeClue("Smith a mithril 2h sword.", ANY_HAMMER, xOfItem(ItemID.MITHRIL_BAR, 3)), From 6b793fc0e5095c7f63eeeda4ae091d691e23c7b3 Mon Sep 17 00:00:00 2001 From: Macweese <50101641+Macweese@users.noreply.github.com> Date: Tue, 16 Apr 2024 00:22:52 -0700 Subject: [PATCH 57/67] discord: add varlamore regions Co-authored-by: Jordan Atwood --- .../client/plugins/discord/DiscordGameEventType.java | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java index 10184a56f7..dc215ecfb7 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/discord/DiscordGameEventType.java @@ -108,8 +108,10 @@ enum DiscordGameEventType CITY_BRIMHAVEN("Brimhaven" , DiscordAreaType.CITIES, 11057, 11058), CITY_BURGH_DE_ROTT("Burgh de Rott" , DiscordAreaType.CITIES, 13874, 13873, 14130, 14129), CITY_BURTHORPE("Burthorpe" , DiscordAreaType.CITIES, 11319, 11575), + CITY_CAM_TORUM("Cam Torum" , DiscordAreaType.CITIES, 5525, 5780, 5781, 6037), CITY_CANIFIS("Canifis" , DiscordAreaType.CITIES, 13878), CITY_CATHERBY("Catherby" , DiscordAreaType.CITIES, 11317, 11318, 11061), + CITY_CIVITAS_ILLA_FORTIS("Civitas Illa Fortis" , DiscordAreaType.CITIES, 6448, 6449, 6704, 6705, 6960, 6961), CITY_CORSAIR_COVE("Corsair Cove" , DiscordAreaType.CITIES, 10028, 10284), CITY_DARKMEYER("Darkmeyer", DiscordAreaType.CITIES, 14388, 14644), CITY_DORGESH_KAAN("Dorgesh-Kaan" , DiscordAreaType.CITIES, 10835, 10834), @@ -189,6 +191,7 @@ enum DiscordGameEventType DUNGEON_CHASM_OF_FIRE("Chasm of Fire", DiscordAreaType.DUNGEONS, 5789), DUNGEON_CHASM_OF_TEARS("Chasm of Tears", DiscordAreaType.DUNGEONS, 12948), DUNGEON_CHINCHOMPA("Chinchompa Hunting Ground", DiscordAreaType.DUNGEONS, 10129), + DUNGEON_CIVITAS_ILLA_FORTIS("Civitas illa Fortis Underground", DiscordAreaType.DUNGEONS, 6549, 6804, 6805), DUNGEON_CLOCK_TOWER("Clock Tower Basement", DiscordAreaType.DUNGEONS, 10390), DUNGEON_CORSAIR_COVE("Corsair Cove Dungeon", DiscordAreaType.DUNGEONS, 8076, 8332), DUNGEON_CRABCLAW_CAVES("Crabclaw Caves", DiscordAreaType.DUNGEONS, 6553, 6809), @@ -266,6 +269,7 @@ enum DiscordGameEventType DUNGEON_TEMPLE_OF_IKOV("Temple of Ikov", DiscordAreaType.DUNGEONS, 10649, 10905, 10650), DUNGEON_TEMPLE_OF_LIGHT("Temple of Light", DiscordAreaType.DUNGEONS, 7496), DUNGEON_TEMPLE_OF_MARIMBO("Temple of Marimbo", DiscordAreaType.DUNGEONS, 11151), + DUNGEON_THE_BURROW("The Burrow", DiscordAreaType.DUNGEONS, 6291), DUNGEON_THE_WARRENS("The Warrens", DiscordAreaType.DUNGEONS, 7070, 7326), DUNGEON_TOLNA("Dungeon of Tolna", DiscordAreaType.DUNGEONS, 13209), DUNGEON_TOWER_OF_LIFE("Tower of Life Basement", DiscordAreaType.DUNGEONS, 12100), @@ -297,6 +301,8 @@ enum DiscordGameEventType MG_CLAN_WARS("Clan Wars", DiscordAreaType.MINIGAMES, 12621, 12622, 12623, 13130, 13131, 13133, 13134, 13135, 13386, 13387, 13390, 13641, 13642, 13643, 13644, 13645, 13646, 13647, 13899, 13900, 14155, 14156), MG_PVP_ARENA("PvP Arena", DiscordAreaType.MINIGAMES, 13362, 13363), MG_FISHING_TRAWLER("Fishing Trawler", DiscordAreaType.MINIGAMES, 7499), + MG_FORTIS_COLOSSEUM("Fortis Colosseum", DiscordAreaType.MINIGAMES, 7216), + MG_FORTIS_COLOSSEUM_LOBBY("Fortis Colosseum Lobby", DiscordAreaType.MINIGAMES, 7316), MG_GAUNTLET("The Gauntlet", DiscordAreaType.MINIGAMES, 12127, 7512), MG_CORRUPTED_GAUNTLET("Corrupted Gauntlet", DiscordAreaType.MINIGAMES, 7768), MG_GIANTS_FOUNDRY("Giants' Foundry", DiscordAreaType.MINIGAMES, 13491), @@ -339,6 +345,7 @@ enum DiscordGameEventType REGION_APE_ATOLL("Ape Atoll" , DiscordAreaType.REGIONS, 10794, 10795, 10974, 11050), REGION_ARANDAR("Arandar", DiscordAreaType.REGIONS, 9266, 9267, 9523), REGION_ASGARNIA("Asgarnia", DiscordAreaType.REGIONS, 11825, 11829, 11830, 12085, 12086), + REGION_AVIUM_SAVANNAH("Avium Savannah", DiscordAreaType.REGIONS, 5935, 5936, 5937, 6189, 6445, 6446, 6447, 6701, 6702, 6703, 6957, 6958, 6959, 7215), REGION_BATTLEFIELD("Battlefield", DiscordAreaType.REGIONS, 10034), REGION_BATTLEFRONT("Battlefront", DiscordAreaType.REGIONS, 5433, 5434), REGION_BLAST_MINE("Blast Mine", DiscordAreaType.REGIONS, 5948), @@ -386,6 +393,7 @@ enum DiscordGameEventType REGION_GWD("God Wars Dungeon", DiscordAreaType.REGIONS, 11578), REGION_HARMONY("Harmony Island", DiscordAreaType.REGIONS, 15148), REGION_HAZELMERE("Hazelmere's Island", DiscordAreaType.REGIONS, 10544), + REGION_HUNTER_GUILD("Hunter Guild", DiscordAreaType.REGIONS, 6191), REGION_ICE_PATH("Ice Path", DiscordAreaType.REGIONS, 11322, 11323), REGION_ICEBERG("Iceberg", DiscordAreaType.REGIONS, 10558, 10559), REGION_ICYENE_GRAVEYARD("Icyene Graveyard", DiscordAreaType.REGIONS, 14641, 14897, 14898), @@ -425,6 +433,7 @@ enum DiscordGameEventType REGION_NORTHERN_TUNDRAS("Northern Tundras", DiscordAreaType.REGIONS, 6204, 6205, 6717), REGION_OBSERVATORY("Observatory", DiscordAreaType.REGIONS, 9777), REGION_ODD_ONE_OUT("Odd One Out", DiscordAreaType.REGIONS, 7754), + REGION_ORTUS_FARM("Ortus Farm", DiscordAreaType.REGIONS, 6192, 6193), REGION_OTTOS_GROTTO("Otto's Grotto", DiscordAreaType.REGIONS, 10038), REGION_OURANIA_HUNTER("Ourania Hunter Area", DiscordAreaType.REGIONS, 9778), REGION_PIRATES_COVE("Pirates' Cove", DiscordAreaType.REGIONS, 8763), @@ -434,6 +443,7 @@ enum DiscordGameEventType REGION_PORT_TYRAS("Port Tyras", DiscordAreaType.REGIONS, 8496), REGION_PURO_PURO("Puro Puro", DiscordAreaType.REGIONS, 10307), REGION_QUARRY("Quarry", DiscordAreaType.REGIONS, 12589), + REGION_RALOS_RISE("Ralos' Rise", DiscordAreaType.REGIONS, 5424, 5425, 5679, 5680, 5681, 5682), REGION_RANGING_GUILD("Ranging Guild", DiscordAreaType.REGIONS, 10549), REGION_RATCATCHERS_MANSION("Ratcatchers Mansion", DiscordAreaType.REGIONS, 11343), REGION_RUINS_OF_UNKAH("Ruins of Unkah", DiscordAreaType.REGIONS, 12588), @@ -449,6 +459,7 @@ enum DiscordGameEventType REGION_SLAYER_TOWER("Slayer Tower", DiscordAreaType.REGIONS, 13623, 13723), REGION_SOUL_ALTAR("Soul Altar", DiscordAreaType.REGIONS, 7228), REGION_STRANGLEWOOD_TEMPLE("Stranglewood Temple", DiscordAreaType.REGIONS, 4761), + REGION_SUNSET_COAST("Sunset Coast", DiscordAreaType.REGIONS, 5934, 6190), REGION_THE_SCAR("The Scar", DiscordAreaType.REGIONS, 8036, 8292), REGION_THE_STRANGLEWOOD("The Stranglewood", DiscordAreaType.REGIONS, 4403, 4404, 4659, 4660, 4661, 4916, 4917), REGION_TROLL_ARENA("Troll Arena", DiscordAreaType.REGIONS, 11576), From 466bb1f16820d0226b1539bdf25eec0b5c5a8bcc Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Apr 2024 15:47:34 -0400 Subject: [PATCH 58/67] api: add cs2 arrays --- .../src/main/java/net/runelite/api/Client.java | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index d94aa3382f..d4848cbe8f 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -1415,6 +1415,21 @@ Projectile createProjectile(int id, int plane, int startX, int startY, int start */ String[] getStringStack(); + /** + * Get the size of one of the cs2 vm's arrays. + * @param arrayId the array id + * @return + */ + int getArraySizes(int arrayId); + + /** + * Get one of the cs2 vm's arrays. Use {@link #getArraySizes(int)} to get + * the array length. + * @param arrayId the array id + * @return + */ + int[] getArray(int arrayId); + /** * Gets the cs2 vm's active widget * From 0d4680e6dc787fcc236a4d4251a2b94f656fc0ad Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Apr 2024 15:50:59 -0400 Subject: [PATCH 59/67] config: show config panel for any plugin with config descriptor This allows the "Reset" option to be used even if the plugin has no configurable settings. --- .../client/plugins/config/PluginConfigurationDescriptor.java | 5 ----- .../net/runelite/client/plugins/config/PluginListItem.java | 2 +- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java index 5a753baba7..05408ee192 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginConfigurationDescriptor.java @@ -57,11 +57,6 @@ class PluginConfigurationDescriptor @Nullable private final List conflicts; - boolean hasConfigurables() - { - return configDescriptor != null && !configDescriptor.getItems().stream().allMatch(item -> item.getItem().hidden()); - } - PluginConfigurationDescriptor(String name, String description, String[] tags, Config config, ConfigDescriptor configDescriptor) { this(name, description, tags, null, config, configDescriptor, null); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java index 880da7877c..91436e2b57 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/config/PluginListItem.java @@ -128,7 +128,7 @@ class PluginListItem extends JPanel implements SearchablePlugin add(buttonPanel, BorderLayout.LINE_END); JMenuItem configMenuItem = null; - if (pluginConfig.hasConfigurables()) + if (pluginConfig.getConfigDescriptor() != null) { JButton configButton = new JButton(ConfigPanel.CONFIG_ICON); SwingUtil.removeButtonDecorations(configButton); From 2ecf0a5b991ea05ccef9091cc862e181b5a3e823 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Apr 2024 15:57:07 -0400 Subject: [PATCH 60/67] add spellbook plugin This allows reordering and hiding spells --- .../src/main/interfaces/interfaces.toml | 2 + .../main/java/net/runelite/api/EnumID.java | 3 + .../main/java/net/runelite/api/ParamID.java | 4 + .../main/java/net/runelite/api/ScriptID.java | 3 + .../main/java/net/runelite/api/Varbits.java | 3 + .../plugins/spellbook/SpellbookConfig.java | 34 + .../plugins/spellbook/SpellbookPlugin.java | 479 +++++++++ .../main/scripts/MagicSpellbookRedraw.hash | 1 + .../main/scripts/MagicSpellbookRedraw.rs2asm | 954 ++++++++++++++++++ 9 files changed, 1483 insertions(+) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookConfig.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookPlugin.java create mode 100644 runelite-client/src/main/scripts/MagicSpellbookRedraw.hash create mode 100644 runelite-client/src/main/scripts/MagicSpellbookRedraw.rs2asm diff --git a/runelite-api/src/main/interfaces/interfaces.toml b/runelite-api/src/main/interfaces/interfaces.toml index 74c6d4c615..9b1bf2ad4f 100644 --- a/runelite-api/src/main/interfaces/interfaces.toml +++ b/runelite-api/src/main/interfaces/interfaces.toml @@ -723,6 +723,7 @@ emote_icon=48 music_icon=49 inventory_tab=55 prayer_tab=57 +magic_tab=58 cmb_icon=59 skills_icon=60 quests_icon=61 @@ -800,6 +801,7 @@ inventory_item_container=0 [spellbook] id=218 +parent=0 kourend_home_teleport=4 catherby_home_teleport=5 lumbridge_home_teleport=7 diff --git a/runelite-api/src/main/java/net/runelite/api/EnumID.java b/runelite-api/src/main/java/net/runelite/api/EnumID.java index 478aeffccf..c8618f6d1e 100644 --- a/runelite-api/src/main/java/net/runelite/api/EnumID.java +++ b/runelite-api/src/main/java/net/runelite/api/EnumID.java @@ -90,4 +90,7 @@ public final class EnumID * val: enum */ public static final int WEAPON_STYLES = 3908; + + public static final int SPELLBOOKS = 1981; + public static final int SPELLBOOKS_SUB = 5280; } diff --git a/runelite-api/src/main/java/net/runelite/api/ParamID.java b/runelite-api/src/main/java/net/runelite/api/ParamID.java index 00babf8053..cebb1e63e8 100644 --- a/runelite-api/src/main/java/net/runelite/api/ParamID.java +++ b/runelite-api/src/main/java/net/runelite/api/ParamID.java @@ -67,4 +67,8 @@ public final class ParamID public static final int SLAYER_TASK_NAME = 1801; public static final int ATTACK_STYLE_NAME = 1407; + + public static final int SPELL_BUTTON = 596; + public static final int SPELL_NAME = 601; + public static final int SPELL_LEVELREQ = 604; } diff --git a/runelite-api/src/main/java/net/runelite/api/ScriptID.java b/runelite-api/src/main/java/net/runelite/api/ScriptID.java index ec9691a639..062ae1a9c4 100644 --- a/runelite-api/src/main/java/net/runelite/api/ScriptID.java +++ b/runelite-api/src/main/java/net/runelite/api/ScriptID.java @@ -452,4 +452,7 @@ public final class ScriptID @ScriptArguments(integer = 6) public static final int EQUIPMENT_SET_STAT_BONUS_SETUP = 3517; + + @ScriptArguments(integer = 12, string = 2) + public static final int MAGIC_SPELLBOOK_INITIALISESPELLS = 2616; } diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index f538fd62e8..2384a2c96f 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -886,4 +886,7 @@ public final class Varbits * 1 = active */ public static final int SPELLBOOK_SWAP = 3617; + + public static final int SPELLBOOK = 4070; + public static final int SPELLBOOK_SUBMENU = 9730; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookConfig.java new file mode 100644 index 0000000000..e11b9d70f0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookConfig.java @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2024, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.spellbook; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; + +@ConfigGroup(SpellbookConfig.GROUP) +public interface SpellbookConfig extends Config +{ + String GROUP = "spellbook"; +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookPlugin.java new file mode 100644 index 0000000000..d45e16d3e6 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/spellbook/SpellbookPlugin.java @@ -0,0 +1,479 @@ +/* + * Copyright (c) 2024, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.spellbook; + +import com.google.inject.Provides; +import java.util.Arrays; +import java.util.Comparator; +import java.util.stream.IntStream; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.EnumComposition; +import net.runelite.api.EnumID; +import net.runelite.api.ItemComposition; +import net.runelite.api.ParamID; +import net.runelite.api.ScriptID; +import net.runelite.api.Varbits; +import net.runelite.api.events.DraggingWidgetChanged; +import net.runelite.api.events.ScriptCallbackEvent; +import net.runelite.api.events.ScriptPreFired; +import net.runelite.api.widgets.ComponentID; +import net.runelite.api.widgets.InterfaceID; +import net.runelite.api.widgets.JavaScriptCallback; +import net.runelite.api.widgets.Widget; +import static net.runelite.api.widgets.WidgetConfig.DRAG; +import static net.runelite.api.widgets.WidgetConfig.DRAG_ON; +import net.runelite.api.widgets.WidgetUtil; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.menus.MenuManager; +import net.runelite.client.menus.WidgetMenuOption; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; + +@PluginDescriptor( + name = "Spellbook", + description = "Reorder and hide spells" +) +@Slf4j +public class SpellbookPlugin extends Plugin +{ + private static final String LOCK = "Disable spell reordering"; + private static final String UNLOCK = "Enable spell reordering"; + // [proc,magic_spellbook_modifyops] shifts around ops, but seems to only use + // 1 2 3 4 5 10. So use 6 for Hide/Unhide. + private static final int HIDE_UNHIDE_OP = 6; + + private static final WidgetMenuOption FIXED_MAGIC_TAB_LOCK = new WidgetMenuOption(LOCK, + "", ComponentID.FIXED_VIEWPORT_MAGIC_TAB); + + private static final WidgetMenuOption FIXED_MAGIC_TAB_UNLOCK = new WidgetMenuOption(UNLOCK, + "", ComponentID.FIXED_VIEWPORT_MAGIC_TAB); + + private static final WidgetMenuOption RESIZABLE_MAGIC_TAB_LOCK = new WidgetMenuOption(LOCK, + "", ComponentID.RESIZABLE_VIEWPORT_MAGIC_TAB); + + private static final WidgetMenuOption RESIZABLE_MAGIC_TAB_UNLOCK = new WidgetMenuOption(UNLOCK, + "", ComponentID.RESIZABLE_VIEWPORT_MAGIC_TAB); + + private static final WidgetMenuOption RESIZABLE_BOTTOM_LINE_MAGIC_TAB_LOCK = new WidgetMenuOption(LOCK, + "", ComponentID.RESIZABLE_VIEWPORT_BOTTOM_LINE_MAGIC_TAB); + + private static final WidgetMenuOption RESIZABLE_BOTTOM_LINE_MAGIC_TAB_UNLOCK = new WidgetMenuOption(UNLOCK, + "", ComponentID.RESIZABLE_VIEWPORT_BOTTOM_LINE_MAGIC_TAB); + + @Inject + private Client client; + + @Inject + private ClientThread clientThread; + + @Inject + private MenuManager menuManager; + + @Inject + private ChatMessageManager chatMessageManager; + + @Inject + private ConfigManager configManager; + + private boolean reordering; + + @Provides + SpellbookConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(SpellbookConfig.class); + } + + @Override + protected void startUp() + { + refreshReorderMenus(); + clientThread.invokeLater(this::reinitializeSpellbook); + } + + @Override + protected void shutDown() + { + clearReoderMenus(); + clientThread.invokeLater(this::reinitializeSpellbook); + } + + @Override + public void resetConfiguration() + { + for (var key : configManager.getConfigurationKeys(SpellbookConfig.GROUP + ".spell_")) + { + String[] str = key.split("\\.", 2); + if (str.length == 2) + { + configManager.unsetConfiguration(str[0], str[1]); + } + } + + clientThread.invokeLater(this::redrawSpellbook); + + log.debug("Reset spellbook"); + } + + private void clearReoderMenus() + { + menuManager.removeManagedCustomMenu(FIXED_MAGIC_TAB_LOCK); + menuManager.removeManagedCustomMenu(RESIZABLE_MAGIC_TAB_LOCK); + menuManager.removeManagedCustomMenu(RESIZABLE_BOTTOM_LINE_MAGIC_TAB_LOCK); + menuManager.removeManagedCustomMenu(FIXED_MAGIC_TAB_UNLOCK); + menuManager.removeManagedCustomMenu(RESIZABLE_MAGIC_TAB_UNLOCK); + menuManager.removeManagedCustomMenu(RESIZABLE_BOTTOM_LINE_MAGIC_TAB_UNLOCK); + } + + private void refreshReorderMenus() + { + clearReoderMenus(); + if (reordering) + { + menuManager.addManagedCustomMenu(FIXED_MAGIC_TAB_LOCK, e -> reordering(false)); + menuManager.addManagedCustomMenu(RESIZABLE_MAGIC_TAB_LOCK, e -> reordering(false)); + menuManager.addManagedCustomMenu(RESIZABLE_BOTTOM_LINE_MAGIC_TAB_LOCK, e -> reordering(false)); + } + else + { + menuManager.addManagedCustomMenu(FIXED_MAGIC_TAB_UNLOCK, e -> reordering(true)); + menuManager.addManagedCustomMenu(RESIZABLE_MAGIC_TAB_UNLOCK, e -> reordering(true)); + menuManager.addManagedCustomMenu(RESIZABLE_BOTTOM_LINE_MAGIC_TAB_UNLOCK, e -> reordering(true)); + } + } + + private void reordering(boolean state) + { + reordering = state; + + var message = reordering ? + "Spell book reordering is now enabled." : + "Spell book reordering is now disabled."; + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.CONSOLE) + .runeLiteFormattedMessage(message) + .build()); + + refreshReorderMenus(); + + redrawSpellbook(); + } + + @Subscribe + public void onScriptPreFired(ScriptPreFired event) + { + if (event.getScriptId() == ScriptID.MAGIC_SPELLBOOK_INITIALISESPELLS) + { + int[] stack = client.getIntStack(); + int sz = client.getIntStackSize(); + int spellBookEnum = stack[sz - 12]; // eg 1982, 5285, 1983, 1984, 1985 + clientThread.invokeLater(() -> initializeSpells(spellBookEnum)); + } + } + + @Subscribe + public void onDraggingWidgetChanged(DraggingWidgetChanged event) + { + if (event.isDraggingWidget() && client.getMouseCurrentButton() == 0) + { + Widget draggedWidget = client.getDraggedWidget(); + Widget draggedOnWidget = client.getDraggedOnWidget(); + if (draggedWidget == null || draggedOnWidget == null) + { + return; + } + + int draggedGroupId = WidgetUtil.componentToInterface(draggedWidget.getId()); + int draggedOnGroupId = WidgetUtil.componentToInterface(draggedOnWidget.getId()); + if (draggedGroupId != InterfaceID.SPELLBOOK || draggedOnGroupId != InterfaceID.SPELLBOOK) + { + return; + } + + // from ~magic_spellbook_redraw + int subSpellbookId = client.getEnum(EnumID.SPELLBOOKS_SUB).getIntValue(client.getVarbitValue(Varbits.SPELLBOOK)); + int spellbookId = client.getEnum(subSpellbookId).getIntValue(client.getVarbitValue(Varbits.SPELLBOOK_SUBMENU)); + + EnumComposition spellbook = client.getEnum(spellbookId); + int[] order = calculateSpellbookOrder(spellbookId, spellbook); // in enum indices + + int fromIdx = findSpellIdxForComponent(spellbook, order, draggedWidget); + int toIdx = findSpellIdxForComponent(spellbook, order, draggedOnWidget); + + ItemComposition fromSpell = client.getItemDefinition(spellbook.getIntValue(order[fromIdx])); + ItemComposition toSpell = client.getItemDefinition(spellbook.getIntValue(order[toIdx])); + + log.debug("Insert {} ({}) at {} ({}) spellbook {}", + fromSpell.getStringValue(ParamID.SPELL_NAME), fromIdx, + toSpell.getStringValue(ParamID.SPELL_NAME), toIdx, + spellbookId); + + log.debug("Set {} to {}", client.getItemDefinition(spellbook.getIntValue(order[fromIdx])).getStringValue(ParamID.SPELL_NAME), toIdx); + setPosition(spellbookId, spellbook.getIntValue(order[fromIdx]), toIdx); + if (fromIdx < toIdx) + { + for (int i = fromIdx + 1; i <= toIdx; ++i) + { + log.debug("Set {} to {}", client.getItemDefinition(spellbook.getIntValue(order[i])).getStringValue(ParamID.SPELL_NAME), i - 1); + setPosition(spellbookId, spellbook.getIntValue(order[i]), i - 1); + } + } + else + { + for (int i = toIdx; i < fromIdx; ++i) + { + log.debug("Set {} to {}", client.getItemDefinition(spellbook.getIntValue(order[i])).getStringValue(ParamID.SPELL_NAME), i + 1); + setPosition(spellbookId, spellbook.getIntValue(order[i]), i + 1); + } + } + + redrawSpellbook(); + } + } + + private int findSpellIdxForComponent(EnumComposition spellbook, int[] spells, Widget c) + { + for (int i = 0; i < spells.length; ++i) + { + ItemComposition spellObj = client.getItemDefinition(spellbook.getIntValue(spells[i])); + Widget w = client.getWidget(spellObj.getIntValue(ParamID.SPELL_BUTTON)); + if (w == c) + { + return i; + } + } + return -1; + } + + @Subscribe + public void onScriptCallbackEvent(ScriptCallbackEvent event) + { + if (!"spellbookSort".equals(event.getEventName())) + { + return; + } + + // this is called after ~magic_spellbook_redraw has built and sorted the array of visible spells + // based on the vanilla filtering + + int[] stack = client.getIntStack(); + int size = client.getIntStackSize(); + + int spellbookEnumId = stack[size - 3]; + int spellArrayId = stack[size - 2]; + int numSpells = stack[size - 1]; + + EnumComposition spellbookEnum = client.getEnum(spellbookEnumId); + int[] spells = client.getArray(spellArrayId); // enum indices + int[] newSpells = new int[numSpells]; + int numNewSpells = 0; + for (int i = 0; i < numSpells; ++i) + { + ItemComposition spellObj = client.getItemDefinition(spellbookEnum.getIntValue(spells[i])); + Widget w = client.getWidget(spellObj.getIntValue(ParamID.SPELL_BUTTON)); + boolean hidden = isHidden(spellbookEnumId, spellObj.getId()); + + int widgetConfig = w.getClickMask(); + if (reordering) + { + if (hidden) + { + w.setOpacity(100); + w.setAction(HIDE_UNHIDE_OP, "Unhide"); + } + else + { + w.setOpacity(0); + w.setAction(HIDE_UNHIDE_OP, "Hide"); + } + + newSpells[numNewSpells++] = spells[i]; + widgetConfig |= DRAG | DRAG_ON; + } + else + { + if (hidden) + { + w.setHidden(true); + } + else + { + newSpells[numNewSpells++] = spells[i]; + w.setOpacity(0); + w.setAction(HIDE_UNHIDE_OP, null); + } + + widgetConfig &= ~(DRAG | DRAG_ON); + } + w.setClickMask(widgetConfig); + } + + // Sort newSpells based on their configured order + int[] order = calculateSpellbookOrder(spellbookEnumId, spellbookEnum); + int[] indices = new int[order.length]; + for (int i = 0; i < order.length; ++i) + { + indices[order[i]] = i; + } + newSpells = Arrays.stream(newSpells, 0, numNewSpells) + .boxed() + .sorted(Comparator.comparingInt(i -> indices[i])) + .mapToInt(i -> i) + .toArray(); + + System.arraycopy(newSpells, 0, spells, 0, numNewSpells); + stack[size - 1] = numSpells = numNewSpells; + } + + private void initializeSpells(int spellbookEnum) + { + EnumComposition spellbook = client.getEnum(spellbookEnum); + for (int i = 0; i < spellbook.size(); ++i) + { + int spellObjId = spellbook.getIntValue(i); + ItemComposition spellObj = client.getItemDefinition(spellObjId); + int spellComponent = spellObj.getIntValue(ParamID.SPELL_BUTTON); + Widget w = client.getWidget(spellComponent); + + // spells with no target mask have an existing op listener, capture it to + // call it later + Object[] opListener = w.getOnOpListener(); + w.setOnOpListener((JavaScriptCallback) e -> + { + if (e.getOp() == HIDE_UNHIDE_OP + 1) + { + Widget s = e.getSource(); + + boolean hidden = isHidden(spellbookEnum, spellObjId); + hidden = !hidden; + + log.debug("Changing {} to hidden: {}", s.getName(), hidden); + setHidden(spellbookEnum, spellObjId, hidden); + + s.setOpacity(hidden ? 100 : 0); + s.setAction(HIDE_UNHIDE_OP, hidden ? "Unhide" : "Hide"); + return; + } + + if (opListener != null) + { + client.runScript(opListener); + } + }); + } + } + + private void reinitializeSpellbook() + { + Widget w = client.getWidget(ComponentID.SPELLBOOK_PARENT); + if (w != null && w.getOnLoadListener() != null) + { + client.createScriptEvent(w.getOnLoadListener()) + .setSource(w) + .run(); + } + } + + private void redrawSpellbook() + { + Widget w = client.getWidget(ComponentID.SPELLBOOK_PARENT); + if (w != null && w.getOnInvTransmitListener() != null) + { + client.createScriptEvent(w.getOnInvTransmitListener()) + .setSource(w) + .run(); + } + } + + private int[] calculateSpellbookOrder(int spellbookId, EnumComposition spellbook) + { + int[] spells = defaultSpellbookOrder(spellbook); + int[] indices = new int[spells.length]; // spell to desired index + for (int i = 0; i < spells.length; ++i) + { + int pos = getPosition(spellbookId, spellbook.getIntValue(spells[i])); + indices[spells[i]] = pos != -1 ? pos : i; + } + + // sort by desired index + return Arrays.stream(spells) + .boxed() + .sorted(Comparator.comparingInt(i -> indices[i])) + .mapToInt(i -> i) + .toArray(); + } + + private int[] defaultSpellbookOrder(EnumComposition spellbook) + { + return IntStream.range(0, spellbook.size()) + .boxed() + .sorted((idx1, idx2) -> + { + var i1 = client.getItemDefinition(spellbook.getIntValue(idx1)); + var i2 = client.getItemDefinition(spellbook.getIntValue(idx2)); + int l1 = i1.getIntValue(ParamID.SPELL_LEVELREQ); + int l2 = i2.getIntValue(ParamID.SPELL_LEVELREQ); + return Integer.compare(l1, l2); + }) + .mapToInt(i -> i) + .toArray(); + } + + private boolean isHidden(int spellbook, int spell) + { + Boolean b = configManager.getConfiguration(SpellbookConfig.GROUP, "spell_hidden_book_" + spellbook + "_" + spell, boolean.class); + return b == Boolean.TRUE; + } + + private void setHidden(int spellbook, int spell, boolean hidden) + { + if (hidden) + { + configManager.setConfiguration(SpellbookConfig.GROUP, "spell_hidden_book_" + spellbook + "_" + spell, true); + } + else + { + configManager.unsetConfiguration(SpellbookConfig.GROUP, "spell_hidden_book_" + spellbook + "_" + spell); + } + } + + private int getPosition(int spellbook, int spell) + { + Integer pos = configManager.getConfiguration(SpellbookConfig.GROUP, "spell_pos_book_" + spellbook + "_" + spell, int.class); + return pos == null ? -1 : pos; + } + + private void setPosition(int spellbook, int spell, int position) + { + configManager.setConfiguration(SpellbookConfig.GROUP, "spell_pos_book_" + spellbook + "_" + spell, position); + } +} diff --git a/runelite-client/src/main/scripts/MagicSpellbookRedraw.hash b/runelite-client/src/main/scripts/MagicSpellbookRedraw.hash new file mode 100644 index 0000000000..1d586f5167 --- /dev/null +++ b/runelite-client/src/main/scripts/MagicSpellbookRedraw.hash @@ -0,0 +1 @@ +ACCA9A2E3AE4280269643C9319582C301F5DFE90E03D11EE124824DB4D6E4C17 \ No newline at end of file diff --git a/runelite-client/src/main/scripts/MagicSpellbookRedraw.rs2asm b/runelite-client/src/main/scripts/MagicSpellbookRedraw.rs2asm new file mode 100644 index 0000000000..2d46babee6 --- /dev/null +++ b/runelite-client/src/main/scripts/MagicSpellbookRedraw.rs2asm @@ -0,0 +1,954 @@ +.id 2611 +.int_stack_count 11 +.string_stack_count 2 +.int_var_count 39 +.string_var_count 2 + iconst 190 + istore 11 + iconst 261 + istore 12 + iconst 0 + istore 13 + iload 10 + iconst 1 + if_icmpeq LABEL10 + jump LABEL76 +LABEL10: + iconst 6 + iconst 240 + iconst 1 + iconst 0 + iload 3 + if_setsize + iconst 190 + iconst 6 + sub + iconst 240 + istore 12 + istore 11 + iconst 0 + iconst 0 + iconst 1 + iconst 0 + iload 3 + if_setposition + iconst 0 + iload 4 + if_sethide + sload 0 + iconst 190 + iconst 494 + parawidth + sload 1 + iconst 190 + iconst 494 + parawidth + invoke 1045 + iconst 14 + add + istore 13 + iload 13 + iconst 0 + iconst 0 + iconst 1 + iload 5 + if_setsize + iload 13 + iconst 0 + iconst 0 + iconst 1 + iload 6 + if_setsize + iconst 190 + iload 13 + iconst 2 + multiply + sub + iconst 3 + div + istore 13 + iload 13 + iconst 0 + iconst 0 + iconst 1 + iload 5 + if_setposition + iload 13 + iconst 0 + iconst 2 + iconst 1 + iload 6 + if_setposition + jump LABEL147 +LABEL76: + get_varbit 6718 + iconst 1 + if_icmpeq LABEL80 + jump LABEL109 +LABEL80: + iconst 0 + iconst 0 + iconst 1 + iconst 1 + iload 3 + if_setsize + iconst 0 + iconst 0 + iconst 1 + iconst 1 + iload 3 + if_setposition + iconst 1 + iload 4 + if_sethide + iconst -1 + iload 10 + iload 5 + iload 6 + iload 0 + iload 1 + iload 7 + iload 8 + iload 9 + iload 2 + sload 0 + sload 1 + invoke 2603 + jump LABEL147 +LABEL109: + iconst 6 + iconst 240 + iconst 1 + iconst 0 + iload 3 + if_setsize + iconst 190 + iconst 6 + sub + iconst 240 + istore 12 + istore 11 + iconst 0 + iconst 0 + iconst 1 + iconst 0 + iload 3 + if_setposition + iconst 0 + iload 4 + if_sethide + sload 1 + iconst 190 + iconst 494 + parawidth + iconst 14 + add + iconst 0 + iconst 0 + iconst 1 + iload 6 + if_setsize + iconst 0 + iconst 0 + iconst 1 + iconst 1 + iload 6 + if_setposition +LABEL147: + iload 0 + cc_deleteall + iload 1 + cc_deleteall + iload 2 + cc_deleteall + iconst 105 + iconst 103 + iconst 1981 + get_varbit 4070 + enum + istore 14 + iconst 105 + iconst 103 + iconst 5280 + get_varbit 4070 + enum + istore 15 + get_varbit 9730 + iconst 0 + if_icmpge LABEL169 + jump LABEL175 +LABEL169: + iconst 105 + iconst 103 + iload 15 + get_varbit 9730 + enum + istore 14 +LABEL175: + iconst 0 + istore 16 + iconst 1981 + enum_getoutputcount + istore 17 + iconst 1 + istore 18 + iconst 0 + istore 19 + iconst -1 + istore 20 +LABEL186: + iload 16 + iload 17 + if_icmplt LABEL190 + jump LABEL239 +LABEL190: + iconst 105 + iconst 103 + iconst 1981 + iload 16 + enum + istore 20 + iload 20 + iload 14 + if_icmpne LABEL200 + jump LABEL202 +LABEL200: + iload 20 + invoke 2618 +LABEL202: + iconst 105 + iconst 103 + iconst 5280 + iload 16 + enum + istore 15 + iconst 1 + istore 18 + iload 15 + enum_getoutputcount + istore 19 +LABEL213: + iload 18 + iload 19 + if_icmple LABEL217 + jump LABEL234 +LABEL217: + iconst 105 + iconst 103 + iload 15 + iload 18 + enum + istore 20 + iload 20 + iload 14 + if_icmpne LABEL227 + jump LABEL229 +LABEL227: + iload 20 + invoke 2618 +LABEL229: + iload 18 + iconst 1 + add + istore 18 + jump LABEL213 +LABEL234: + iload 16 + iconst 1 + add + istore 16 + jump LABEL186 +LABEL239: + iload 14 + iconst -1 + if_icmpeq LABEL243 + jump LABEL244 +LABEL243: + return +LABEL244: + iload 14 + enum_getoutputcount + istore 21 + iload 21 + define_array 105 + iconst 0 + istore 22 + iconst -1 + istore 23 + iconst 0 + istore 24 + iload 10 + iconst 0 + if_icmpeq LABEL259 + jump LABEL298 +LABEL259: + get_varbit 6718 + iconst 1 + if_icmpeq LABEL263 + jump LABEL298 +LABEL263: + iload 24 + iload 21 + if_icmplt LABEL267 + jump LABEL297 +LABEL267: + iconst 105 + iconst 111 + iload 14 + iload 24 + enum + istore 23 + invoke 3160 + iconst 1 + if_icmpeq LABEL277 + jump LABEL280 +LABEL277: + iload 23 + invoke 3159 + istore 23 +LABEL280: + iconst 0 + iload 23 + iconst 596 + oc_param + if_sethide + iload 22 + iload 24 + set_array_int + iload 22 + iconst 1 + add + istore 22 + iload 24 + iconst 1 + add + istore 24 + jump LABEL263 +LABEL297: + jump LABEL343 +LABEL298: + iload 24 + iload 21 + if_icmplt LABEL302 + jump LABEL343 +LABEL302: + iconst 105 + iconst 111 + iload 14 + iload 24 + enum + istore 23 + invoke 3160 + iconst 1 + if_icmpeq LABEL312 + jump LABEL315 +LABEL312: + iload 23 + invoke 3159 + istore 23 +LABEL315: + iload 23 + invoke 2619 + iconst 1 + if_icmpeq LABEL320 + jump LABEL333 +LABEL320: + iconst 0 + iload 23 + iconst 596 + oc_param + if_sethide + iload 22 + iload 24 + set_array_int + iload 22 + iconst 1 + add + istore 22 + jump LABEL338 +LABEL333: + iconst 1 + iload 23 + iconst 596 + oc_param + if_sethide +LABEL338: + iload 24 + iconst 1 + add + istore 24 + jump LABEL298 +LABEL343: + iconst 0 + istore 25 + iload 22 + iconst 2 + if_icmpge LABEL349 + jump LABEL357 +LABEL349: + iconst 0 + iconst 0 + iload 22 + iconst 1 + sub + iload 14 + invoke 2621 + + ; after sorting the spell array + iload 14 ; spellbook enum + iconst 0 ; spell array id + iload 22 ; number of spells + sconst "spellbookSort" + runelite_callback + istore 22 ; number of spells + pop_int ; spell array id + pop_int ; spellbook enum + + jump LABEL392 +LABEL357: + iload 22 + iconst 0 + if_icmple LABEL361 + jump LABEL392 +LABEL361: + iload 0 + iconst 4 + iload 25 + cc_create + iconst 0 + iconst 0 + iconst 1 + iconst 1 + cc_setsize + iconst 0 + iconst 0 + iconst 1 + iconst 1 + cc_setposition + iconst 16750623 + cc_setcolour + iconst 495 + cc_settextfont + iconst 1 + cc_settextshadow + iconst 1 + iconst 1 + iconst 0 + cc_settextalign + sconst "No spells match your selected filters." + cc_settext + iload 25 + iconst 1 + add + istore 25 + return +LABEL392: + iconst 24 + istore 26 + iconst 0 + istore 27 + iconst 0 + istore 28 + iconst 0 + istore 29 + iconst 0 + istore 30 + iload 12 + istore 31 + iload 10 + iconst 0 + if_icmpeq LABEL408 + jump LABEL458 +LABEL408: + get_varbit 6718 + iconst 1 + if_icmpeq LABEL412 + jump LABEL458 +LABEL412: + get_varbit 4070 + invoke 6419 + istore 29 + istore 30 + istore 28 + istore 27 + get_varbit 4070 + switch + 1: LABEL427 + 2: LABEL434 + 3: LABEL441 + iconst 1 + iconst 15 + iconst 1 + iconst 0 + iload 0 + if_setposition + jump LABEL447 +LABEL427: + iconst 2 + iconst 8 + iconst 1 + iconst 0 + iload 0 + if_setposition + jump LABEL447 +LABEL434: + iconst 0 + iconst 8 + iconst 1 + iconst 0 + iload 0 + if_setposition + jump LABEL447 +LABEL441: + iconst 0 + iconst 0 + iconst 1 + iconst 0 + iload 0 + if_setposition +LABEL447: + iload 28 + iload 26 + multiply + iload 28 + iconst 1 + sub + iload 29 + multiply + add + istore 31 + jump LABEL641 +LABEL458: + get_varbit 6548 + iconst 1 + if_icmpeq LABEL472 + iload 10 + iconst 0 + if_icmpeq LABEL465 + jump LABEL552 +LABEL465: + get_varbit 8121 + iconst 1 + if_icmpeq LABEL472 + get_varbit 6549 + iconst 1 + if_icmpeq LABEL472 + jump LABEL552 +LABEL472: + iload 22 + iconst 28 + if_icmple LABEL476 + jump LABEL479 +LABEL476: + iconst 4 + istore 27 + jump LABEL489 +LABEL479: + iconst 4 + iconst 7 + iload 22 + iconst 8 + add + iconst 9 + div + invoke 1046 + invoke 1045 + istore 27 +LABEL489: + iconst 0 + iload 26 + iload 11 + iload 26 + iload 27 + multiply + sub + iload 27 + iconst 1 + sub + div + invoke 1046 + invoke 1045 + istore 30 + iconst 1 + iload 22 + iload 27 + iconst 1 + sub + add + iload 27 + div + invoke 1045 + istore 28 + iload 28 + iconst 2 + if_icmpge LABEL517 + jump LABEL531 +LABEL517: + iconst 0 + iload 30 + iload 12 + iload 26 + iload 28 + multiply + sub + iload 28 + iconst 1 + sub + div + invoke 1046 + invoke 1045 + istore 29 +LABEL531: + iload 28 + iload 26 + multiply + iload 28 + iconst 1 + sub + iload 29 + multiply + add + iload 12 + iconst 30 + sub + invoke 1045 + istore 31 + iconst 0 + iconst 0 + iconst 1 + iconst 1 + iload 0 + if_setposition + jump LABEL641 +LABEL552: + iload 22 + iconst 15 + if_icmple LABEL556 + jump LABEL561 +LABEL556: + iconst 40 + iconst 3 + istore 27 + istore 26 + jump LABEL580 +LABEL561: + iload 22 + iconst 20 + if_icmple LABEL565 + jump LABEL570 +LABEL565: + iconst 40 + iconst 4 + istore 27 + istore 26 + jump LABEL580 +LABEL570: + iconst 4 + iconst 7 + iload 22 + iconst 8 + add + iconst 9 + div + invoke 1046 + invoke 1045 + istore 27 +LABEL580: + iconst 0 + iconst 5 + iconst 7 + iload 26 + scale + iload 11 + iload 26 + iload 27 + multiply + sub + iload 27 + iconst 1 + sub + div + invoke 1046 + invoke 1045 + istore 30 + iconst 1 + iload 22 + iload 27 + iconst 1 + sub + add + iload 27 + div + invoke 1045 + istore 28 + iload 28 + iconst 2 + if_icmpge LABEL611 + jump LABEL625 +LABEL611: + iconst 0 + iload 30 + iload 12 + iload 26 + iload 28 + multiply + sub + iload 28 + iconst 1 + sub + div + invoke 1046 + invoke 1045 + istore 29 +LABEL625: + iload 28 + iload 26 + multiply + iload 28 + iconst 1 + sub + iload 29 + multiply + add + istore 31 + iconst 0 + iconst 0 + iconst 1 + iconst 1 + iload 0 + if_setposition +LABEL641: + iload 27 + iload 26 + multiply + iload 27 + iconst 1 + sub + iload 30 + multiply + add + iload 31 + iconst 0 + iconst 0 + iload 0 + if_setsize + iconst 0 + istore 32 + iconst -1 + istore 33 + iload 26 + iload 30 + add + istore 34 + iload 26 + iload 29 + add + istore 35 + iconst -1 + istore 36 + iconst 0 + istore 37 + iconst 0 + istore 38 + iconst 0 + istore 24 +LABEL675: + iload 24 + iload 22 + if_icmplt LABEL679 + jump LABEL856 +LABEL679: + iconst 105 + iconst 111 + iload 14 + iload 24 + get_array_int + enum + istore 23 + invoke 3160 + iconst 1 + if_icmpeq LABEL690 + jump LABEL693 +LABEL690: + iload 23 + invoke 3159 + istore 23 +LABEL693: + iload 23 + iconst 596 + oc_param + istore 33 + iload 26 + iload 26 + iconst 0 + iconst 0 + iload 33 + if_setsize + iload 24 + iload 27 + mod + iload 34 + multiply + iload 24 + iload 27 + div + iload 35 + multiply + istore 38 + istore 37 + iload 37 + iload 38 + iconst 0 + iconst 0 + iload 33 + if_setposition + iload 23 + iload 33 + iload 26 + invoke 2614 + istore 36 + istore 32 + iload 32 + iconst 1 + if_icmpeq LABEL731 + jump LABEL747 +LABEL731: + iload 26 + iconst 40 + if_icmpge LABEL735 + jump LABEL741 +LABEL735: + iload 23 + iconst 599 + oc_param + iload 33 + if_setgraphic + jump LABEL746 +LABEL741: + iload 23 + iconst 597 + oc_param + iload 33 + if_setgraphic +LABEL746: + jump LABEL786 +LABEL747: + iload 26 + iconst 40 + if_icmpge LABEL751 + jump LABEL757 +LABEL751: + iload 23 + iconst 600 + oc_param + iload 33 + if_setgraphic + jump LABEL762 +LABEL757: + iload 23 + iconst 598 + oc_param + iload 33 + if_setgraphic +LABEL762: + iload 36 + iconst -1 + if_icmpne LABEL766 + jump LABEL786 +LABEL766: + iload 0 + iconst 5 + iload 25 + cc_create + iload 26 + iload 26 + iconst 0 + iconst 0 + cc_setsize + iload 37 + iload 38 + iconst 0 + iconst 0 + cc_setposition + iload 36 + cc_setgraphic + iload 25 + iconst 1 + add + istore 25 +LABEL786: + iload 33 + invoke 2615 + iload 10 + iconst 1 + if_icmpeq LABEL792 + jump LABEL819 +LABEL792: + iload 1 + iconst 5 + iload 24 + cc_create + iload 26 + iload 26 + iconst 0 + iconst 0 + cc_setsize + iload 37 + iload 38 + iconst 0 + iconst 0 + cc_setposition + iload 23 + iconst 1 + cc_setobject + iconst 255 + cc_settrans + iconst 2612 + iload 23 + iload 1 + iload 2 + iload 12 + sconst "oIIi" + cc_setonclick + jump LABEL851 +LABEL819: + iconst 2622 + iconst 1 + iload 23 + iconst -2147483645 + iconst -1 + iload 2 + iload 12 + iconst 94 + iconst 3 + inv_getobj + iconst 94 + iconst 5 + inv_getobj + sconst "1oIiIioo" + iload 33 + if_setonmouserepeat + iconst 2622 + iconst 0 + iload 23 + iconst -2147483645 + iconst -1 + iload 2 + iload 12 + iconst 94 + iconst 3 + inv_getobj + iconst 94 + iconst 5 + inv_getobj + sconst "1oIiIioo" + iload 33 + if_setonmouseleave +LABEL851: + iload 24 + iconst 1 + add + istore 24 + jump LABEL675 +LABEL856: + return From 92e85beb53a0605a82f9a6ce36e1c2769cd238b3 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Apr 2024 15:29:40 -0400 Subject: [PATCH 61/67] api: add menuAction --- runelite-api/src/main/java/net/runelite/api/Client.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/Client.java b/runelite-api/src/main/java/net/runelite/api/Client.java index d4848cbe8f..6aef7976c8 100644 --- a/runelite-api/src/main/java/net/runelite/api/Client.java +++ b/runelite-api/src/main/java/net/runelite/api/Client.java @@ -2162,4 +2162,6 @@ Projectile createProjectile(int id, int plane, int startX, int startY, int start void setMinimapTileDrawer(TileFunction drawTile); Rasterizer getRasterizer(); + + void menuAction(int p0, int p1, MenuAction action, int id, int itemId, String option, String target); } From aafd1d6c39f913b3a08267957c9697d8ee730550 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 10 Apr 2024 10:36:29 -0400 Subject: [PATCH 62/67] menuaction: add widget submenu parent This is to pass the getWidget() checks in MES, and also be higher priority (<1000) so it doesn't get resorted. --- runelite-api/src/main/java/net/runelite/api/MenuAction.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/runelite-api/src/main/java/net/runelite/api/MenuAction.java b/runelite-api/src/main/java/net/runelite/api/MenuAction.java index 1a590c0d9b..41bb1af3f2 100644 --- a/runelite-api/src/main/java/net/runelite/api/MenuAction.java +++ b/runelite-api/src/main/java/net/runelite/api/MenuAction.java @@ -244,6 +244,12 @@ public enum MenuAction */ WIDGET_TARGET_ON_WIDGET(58), + /** + * Submenu parent that is also a widget. + * see {@link #RUNELITE_SUBMENU} + */ + RUNELITE_SUBMENU_WIDGET(998), + /** * Menu action for high priority runelite options */ From 86756066ce95d4a1e630801341886193d94d842f Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 9 Apr 2024 15:33:16 -0400 Subject: [PATCH 63/67] menu swapper: add teleport submenus This adds submenus for teleports on a few capes that otherwise use one or several dialogs. --- .../src/main/interfaces/interfaces.toml | 3 + .../MenuEntrySwapperConfig.java | 11 + .../MenuEntrySwapperPlugin.java | 325 +++++++++++++++++- .../menuentryswapper/TeleportSwap.java | 63 ++++ .../MenuEntrySwapperPluginTest.java | 9 +- 5 files changed, 393 insertions(+), 18 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/TeleportSwap.java diff --git a/runelite-api/src/main/interfaces/interfaces.toml b/runelite-api/src/main/interfaces/interfaces.toml index 9b1bf2ad4f..9857d04926 100644 --- a/runelite-api/src/main/interfaces/interfaces.toml +++ b/runelite-api/src/main/interfaces/interfaces.toml @@ -7,9 +7,11 @@ id=741 title=2 text=3 +# this is actually just a generic menu [adventure_log] id=187 container=0 +options=3 [agility_arena] id=5 @@ -295,6 +297,7 @@ emote_scrollbar=4 [equipment] id=387 inventory_item_container=0 +cape=16 [equipment_bonuses] id=84 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 7069265d76..a41428b61e 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 @@ -424,6 +424,17 @@ default boolean swapTeleportItem() return false; } + @ConfigItem( + keyName = "teleportSubmenus", + name = "Teleport submenus", + description = "Use submenus for max, construction, and diary cape teleports", + section = itemSection + ) + default boolean teleportSubmenus() + { + return false; + } + @ConfigItem( keyName = "swapTeleToPoh", name = "Tele to POH", 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 7ea832c367..57499bc180 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 @@ -27,10 +27,12 @@ package net.runelite.client.plugins.menuentryswapper; import com.google.common.annotations.VisibleForTesting; +import com.google.common.base.Preconditions; import static com.google.common.base.Predicates.alwaysTrue; import static com.google.common.base.Predicates.equalTo; import com.google.common.base.Strings; import com.google.common.collect.ArrayListMultimap; +import com.google.common.collect.HashMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.LinkedHashMultimap; @@ -45,11 +47,13 @@ import java.util.function.Consumer; import java.util.function.Predicate; import java.util.function.Supplier; +import java.util.stream.Collectors; import javax.inject.Inject; import lombok.extern.slf4j.Slf4j; import net.runelite.api.ChatMessageType; import net.runelite.api.Client; import net.runelite.api.ItemComposition; +import net.runelite.api.ItemID; import net.runelite.api.KeyCode; import net.runelite.api.MenuAction; import net.runelite.api.MenuEntry; @@ -57,10 +61,13 @@ import net.runelite.api.NPCComposition; import net.runelite.api.ObjectComposition; import net.runelite.api.ParamID; +import net.runelite.api.annotations.Component; import net.runelite.api.events.ClientTick; +import net.runelite.api.events.MenuEntryAdded; import net.runelite.api.events.MenuOpened; import net.runelite.api.events.PostItemComposition; import net.runelite.api.events.PostMenuSort; +import net.runelite.api.widgets.ComponentID; import net.runelite.api.widgets.InterfaceID; import net.runelite.api.widgets.Widget; import net.runelite.api.widgets.WidgetConfig; @@ -158,6 +165,7 @@ public class MenuEntrySwapperPlugin extends Plugin private final Multimap swaps = LinkedHashMultimap.create(); private final ArrayListMultimap optionIndexes = ArrayListMultimap.create(); + private final Multimap teleportSwaps = HashMultimap.create(); @Provides MenuEntrySwapperConfig provideConfig(ConfigManager configManager) @@ -169,6 +177,7 @@ MenuEntrySwapperConfig provideConfig(ConfigManager configManager) public void startUp() { setupSwaps(); + setupTeleportSwaps(); removeOldSwaps(); } @@ -176,6 +185,7 @@ public void startUp() public void shutDown() { swaps.clear(); + teleportSwaps.clear(); } @VisibleForTesting @@ -895,6 +905,31 @@ private void configureWornItems(MenuOpened event) } } + var subSwaps = teleportSwaps.get(itemComposition.getId()) + .stream() + .filter(ts -> ts.worn) + .collect(Collectors.toList()); + for (TeleportSwap top : subSwaps) + { + for (TeleportSub sub : top.subs) + { + if (leftClickOp == null || leftClickOp != sub.option.hashCode()) + { + leftClickMenus.add(client.createMenuEntry(idx) + .setOption(sub.option) + .setType(MenuAction.RUNELITE) + .onClick(wornItemConsumer(itemComposition, sub.option, sub.option.hashCode(), false))); + } + if (shiftClickOp == null || shiftClickOp != sub.option.hashCode()) + { + shiftClickMenus.add(client.createMenuEntry(idx) + .setOption(sub.option) + .setType(MenuAction.RUNELITE) + .onClick(wornItemConsumer(itemComposition, sub.option, sub.option.hashCode(), true))); + } + } + } + if (leftClickOp != null) { leftClickMenus.add(client.createMenuEntry(idx) @@ -1053,6 +1088,31 @@ private void configureItems(MenuOpened event) } } + var subSwaps = teleportSwaps.get(itemComposition.getId()) + .stream() + .filter(ts -> ts.held) + .collect(Collectors.toList()); + for (TeleportSwap top : subSwaps) + { + for (TeleportSub sub : top.subs) + { + if (leftClickOp == null || leftClickOp != sub.option.hashCode()) + { + leftClickMenus.add(client.createMenuEntry(idx) + .setOption(sub.option) + .setType(MenuAction.RUNELITE) + .onClick(heldItemConsumer(itemComposition, sub.option, sub.option.hashCode(), false))); + } + if (shiftClickOp == null || shiftClickOp != sub.option.hashCode()) + { + shiftClickMenus.add(client.createMenuEntry(idx) + .setOption(sub.option) + .setType(MenuAction.RUNELITE) + .onClick(heldItemConsumer(itemComposition, sub.option, sub.option.hashCode(), true))); + } + } + } + if (leftClickOp != null && config.leftClickCustomization()) { leftClickMenus.add(client.createMenuEntry(idx) @@ -1420,20 +1480,25 @@ private void swapMenuEntry(MenuEntry[] menuEntries, int index, MenuEntry menuEnt final String option = Text.removeTags(menuEntry.getOption()).toLowerCase(); final String target = Text.removeTags(menuEntry.getTarget()).toLowerCase(); - final boolean itemOp = menuEntry.isItemOp(); + final Widget w = menuEntry.getParent() != null ? menuEntry.getParent().getWidget() : menuEntry.getWidget(); // Custom shift-click item swap - if (shiftModifier() && itemOp) + if (shiftModifier() && w != null && WidgetUtil.componentToInterface(w.getId()) == InterfaceID.INVENTORY) { - // Special case use shift click due to items not actually containing a "Use" option, making - // the client unable to perform the swap itself. if (config.shiftClickCustomization() && !option.equals("use")) { - Integer customOption = getItemSwapConfig(true, menuEntry.getItemId()); + Integer customOption = getItemSwapConfig(true, w.getItemId()); + // Special case use shift click due to items not actually containing a "Use" option, making + // the client unable to perform the swap itself. if (customOption != null && customOption == -1) { swap(menuEntries, "use", target, index, true); } + else if (customOption != null && menuEntry.getParent() != null && menuEntry.getOption().hashCode() == customOption) + { + swap(optionIndexes, menuEntries, index, menuEntries.length - 1); + menuEntry.setParent(null); + } } // don't perform swaps on items when shift is held; instead prefer the client menu swap, which @@ -1442,29 +1507,27 @@ private void swapMenuEntry(MenuEntry[] menuEntries, int index, MenuEntry menuEnt } // Custom left-click item swap - if (itemOp && config.leftClickCustomization()) + if (w != null && WidgetUtil.componentToInterface(w.getId()) == InterfaceID.INVENTORY + && config.leftClickCustomization()) { - Integer swapIndex = getItemSwapConfig(false, menuEntry.getItemId()); + Integer swapIndex = getItemSwapConfig(false, w.getItemId()); if (swapIndex != null) { - final int swapAction = swapIndex >= 0 - ? 1 + swapIndex - : -1; - - if (swapAction == -1) + if (swapIndex == -1) { swap(menuEntries, "use", target, index, true); } - else if (swapAction == menuEntry.getItemOp()) + else if (swapIndex + 1 == menuEntry.getItemOp() + || (menuEntry.getParent() != null && menuEntry.getOption().hashCode() == swapIndex)) { swap(optionIndexes, menuEntries, index, menuEntries.length - 1); + menuEntry.setParent(null); } return; } } // Worn items swap - final Widget w = menuEntry.getWidget(); if (w != null && WidgetUtil.componentToInterface(w.getId()) == InterfaceID.EQUIPMENT) { Widget child = w.getChild(1); @@ -1473,9 +1536,11 @@ else if (swapAction == menuEntry.getItemOp()) final Integer wornItemSwapConfig = getWornItemSwapConfig(shiftModifier(), child.getItemId()); if (wornItemSwapConfig != null) { - if (wornItemSwapConfig == menuEntry.getIdentifier()) + if (wornItemSwapConfig == menuEntry.getIdentifier() + || (menuEntry.getParent() != null && menuEntry.getOption().hashCode() == wornItemSwapConfig)) { swap(optionIndexes, menuEntries, index, menuEntries.length - 1); + menuEntry.setParent(null); // Move from a submenu to a top level menu } return; } @@ -1535,7 +1600,8 @@ else if (swapAction == menuEntry.getItemOp()) if ((menuAction == MenuAction.CC_OP || menuAction == MenuAction.CC_OP_LOW_PRIORITY || menuAction == MenuAction.WIDGET_TARGET) && w != null && (w.getIndex() == -1 || w.getItemId() != -1) && w.getActions() != null - && !itemOp && WidgetUtil.componentToInterface(w.getId()) != InterfaceID.EQUIPMENT) + && WidgetUtil.componentToInterface(w.getId()) != InterfaceID.INVENTORY + && WidgetUtil.componentToInterface(w.getId()) != InterfaceID.EQUIPMENT) { // fast check to avoid hitting config on components with single ops if ((index > 0 && menuEntries[index - 1].getWidget() == w) || @@ -1712,7 +1778,7 @@ public void onPostItemComposition(PostItemComposition event) ItemComposition itemComposition = event.getItemComposition(); Integer option = getItemSwapConfig(true, itemComposition.getId()); - if (option != null && option < itemComposition.getInventoryActions().length) + if (option != null && option >= 0 && option < itemComposition.getInventoryActions().length) { itemComposition.setShiftClickActionIndex(option); } @@ -1945,4 +2011,229 @@ private void unsetUiSwapConfig(boolean shift, int componentId, int itemId) configManager.unsetConfiguration(MenuEntrySwapperConfig.GROUP, (shift ? UI_SHIFT_KEY_PREFIX : UI_KEY_PREFIX) + componentId + (itemId != -1 ? "_" + itemId : "")); } + + private void setupTeleportSwaps() + { + // region Max cape opworn + teleportSwap("Fishing Teleports", ItemID.MAX_CAPE_13342) + .worn() + .addSub("Fishing Guild", () -> pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1)) + .addSub("Otto's Grotto", () -> pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 2)); + teleportSwap("POH Portals", ItemID.MAX_CAPE_13342) + .worn() + .addSub("Home", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 0)) + .addSub("Rimmington", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 1)) + .addSub("Taverley", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 2)) + .addSub("Pollnivneach", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 3)) + .addSub("Hosidius", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 4)) + .addSub("Rellekka", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5)) + .addSub("Brimhaven", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6)) + .addSub("Yanille", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 7)) + .addSub("Prifddinas", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 8)); + teleportSwap("Other Teleports", ItemID.MAX_CAPE_13342) + .worn() + .addSub("Feldip hills", () -> { + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Chinchompa Teleports + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Carnivorous chinchompas (Feldip Hills) + }) + .addSub("Black chinchompas", () -> { + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Chinchompa Teleports + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 2); // Black chinchompas (Wilderness) + }) + .addSub("Hunter Guild", () -> { + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Chinchompa Teleports + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 3); // Hunter Guild + }) + .addSub("Farming Guild", () -> pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 2)); + // endregion + + // region Max cape opheld + teleportSwap("Teleports", ItemID.MAX_CAPE) + .held() + .addSub("Warriors' Guild", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 0)) + .addSub("Fishing Guild", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 1)) + .addSub("Crafting Guild", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 2)) + .addSub("Farming Guild", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 3)) + .addSub("Otto's Grotto", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 4)) + .addSub("Feldip hills", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5); // Chinchompas + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Carnivorous chinchompas (Feldip Hills) + }) + .addSub("Black chinchompas", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5); // Chinchompas + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 2); // Black chinchompas (Wilderness) + }) + .addSub("Hunter Guild", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5); // Chinchompas + pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 3); // Hunter Guild + }) + .addSub("Home", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 0); + }) + .addSub("Rimmington", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 1); + }) + .addSub("Taverley", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 2); + }) + .addSub("Pollnivneach", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 3); + }) + .addSub("Hosidius", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 4); + }) + .addSub("Rellekka", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5); + }) + .addSub("Yanille", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); + }) + .addSub("Prifddinas", () -> { + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals + pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 7); + }); + // endregion + + // region Con cape + teleportSwap("Teleport", ItemID.CONSTRUCT_CAPE, ItemID.CONSTRUCT_CAPET) + .worn() + .held() + .addSub("Home", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 0)) + .addSub("Rimmington", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 1)) + .addSub("Taverley", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 2)) + .addSub("Pollnivneach", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 3)) + .addSub("Hosidius", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 4)) + .addSub("Rellekka", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5)) + .addSub("Brimhaven", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6)) + .addSub("Yanille", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 7)) + .addSub("Prifddinas", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 8)); + // endregion + + // region Achievement diary cape + teleportSwap("Teleport", ItemID.ACHIEVEMENT_DIARY_CAPE, ItemID.ACHIEVEMENT_DIARY_CAPE_T) + .worn() + .held() + .addSub("Two-pints", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 0)) + .addSub("Jarr", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 1)) + .addSub("Sir Rebral", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 2)) + .addSub("Thorodin", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 3)) + .addSub("Flax keeper", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 4)) + .addSub("Pirate Jackie the Fruit", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5)) + .addSub("Kaleb Paramaya", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6)) + .addSub("Jungle forester", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 7)) + .addSub("TzHaar-Mej", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 8)) + .addSub("Elise", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 9)) + .addSub("Hatius Cosaintus", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 10)) + .addSub("Le-sabrè", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 11)) + .addSub("Toby", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 12)) + .addSub("Lesser Fanatic", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 13)) + .addSub("Elder Gnome child", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 14)) + .addSub("Twiggy O'Korn", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 15)); + // endregion + } + + private TeleportSwap teleportSwap(String option, int... items) + { + Preconditions.checkArgument(items.length > 0, "no items"); + var ts = new TeleportSwap(); + ts.option = option; + for (int item : items) + { + teleportSwaps.put(item, ts); + } + return ts; + } + + @Subscribe + public void onMenuEntryAdded(MenuEntryAdded menuEntryAdded) + { + var me = menuEntryAdded.getMenuEntry(); + if (me.getWidget() != null && me.getWidget().getId() == ComponentID.EQUIPMENT_CAPE) + { + var item = me.getWidget().getChild(1); + var swap = teleportSwaps.get(item.getItemId()) + .stream() + .filter(ts -> ts.worn) + .filter(ts -> ts.option.equals(me.getOption())) + .findAny() + .orElse(null); + if (swap != null && config.teleportSubmenus()) + { + me.setType(MenuAction.RUNELITE_SUBMENU_WIDGET); + me.onClick(e -> client.menuAction(e.getParam0(), e.getParam1(), MenuAction.CC_OP, + e.getIdentifier(), e.getItemId(), e.getOption(), e.getTarget())); + + int off = 0; + final int p0 = me.getParam0(); + final int p1 = me.getParam1(); + final int id = me.getIdentifier(); + final int itemId = me.getItemId(); + final String option = me.getOption(); + final String target = me.getTarget(); + for (TeleportSub sub : swap.subs) + { + client.createMenuEntry(-1 - off++) + .setParam0(p0) + .setParam1(p1) + .setOption(sub.option) + .setTarget(target) + .setType(MenuAction.RUNELITE) + .setParent(me) + .onClick(e -> clientThread.invokeLater(() -> { + client.menuAction(p0, p1, MenuAction.CC_OP, id, itemId, option, target); + sub.execute.run(); + })); + } + } + } + else if (me.getWidget() != null && me.getWidget().getId() == ComponentID.INVENTORY_CONTAINER) + { + var swap = teleportSwaps.get(me.getItemId()) + .stream() + .filter(ts -> ts.held) + .filter(ts -> ts.option.equals(me.getOption())) + .findAny() + .orElse(null); + if (swap != null && config.teleportSubmenus()) + { + me.setType(MenuAction.RUNELITE_SUBMENU_WIDGET); + me.onClick(e -> client.menuAction(e.getParam0(), e.getParam1(), MenuAction.CC_OP, + e.getIdentifier(), e.getItemId(), e.getOption(), e.getTarget())); + + int off = 0; + final int p0 = me.getParam0(); + final int p1 = me.getParam1(); + final int id = me.getIdentifier(); + final int itemId = me.getItemId(); + final String option = me.getOption(); + final String target = me.getTarget(); + for (TeleportSub sub : swap.subs) + { + client.createMenuEntry(-1 - off++) + .setParam0(p0) + .setParam1(p1) + .setOption(sub.option) + .setTarget(target) + .setType(MenuAction.RUNELITE) + .setParent(me) + .onClick(e -> clientThread.invokeLater(() -> { + client.menuAction(p0, p1, MenuAction.CC_OP, id, itemId, option, target); + sub.execute.run(); + })); + } + } + } + } + + private void pauseresume(@Component int comp, int op) + { + client.menuAction(op, comp, MenuAction.WIDGET_CONTINUE, -1, -1, "", ""); + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/TeleportSwap.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/TeleportSwap.java new file mode 100644 index 0000000000..b88ad961eb --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/TeleportSwap.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2024, Adam + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.menuentryswapper; + +import java.util.ArrayList; +import java.util.List; + +class TeleportSwap +{ + boolean worn; + boolean held; + String option; + List subs = new ArrayList<>(); + + TeleportSwap addSub(String option, Runnable r) + { + var sub = new TeleportSub(); + sub.option = option; + sub.execute = r; + subs.add(sub); + return this; + } + + TeleportSwap worn() + { + worn = true; + return this; + } + + TeleportSwap held() + { + held = true; + return this; + } +} + +class TeleportSub +{ + String option; + Runnable execute; +} diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPluginTest.java index 97f75e8e31..6d2cc692c6 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPluginTest.java @@ -41,6 +41,7 @@ import net.runelite.client.config.ConfigManager; import net.runelite.client.game.ItemManager; import net.runelite.client.menus.TestMenuEntry; +import org.junit.After; import static org.junit.Assert.assertArrayEquals; import org.junit.Before; import org.junit.Test; @@ -111,7 +112,13 @@ public void before() return null; }).when(client).setMenuEntries(any(MenuEntry[].class)); - menuEntrySwapperPlugin.setupSwaps(); + menuEntrySwapperPlugin.startUp(); + } + + @After + public void after() + { + menuEntrySwapperPlugin.shutDown(); } private MenuEntry menu(String option, String target, MenuAction menuAction) From a3945d49a71103aa4723a3e8f77b7532260fe998 Mon Sep 17 00:00:00 2001 From: Adam Date: Tue, 16 Apr 2024 20:31:45 -0400 Subject: [PATCH 64/67] menuentryswapper: remove construction build/remove block --- .../plugins/menuentryswapper/MenuEntrySwapperPlugin.java | 7 ------- 1 file changed, 7 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java index 57499bc180..46138fbcb6 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 @@ -535,13 +535,6 @@ private void configureObjectClick(MenuOpened event) continue; } - if ("Build".equals(actions[actionIdx]) - || "Remove".equals(actions[actionIdx])) - { - // https://secure.runescape.com/m=news/third-party-client-guidelines?oldschool=1 - continue; - } - final MenuAction menuAction = OBJECT_MENU_TYPES.get(actionIdx); if (menuAction != currentAction) { From 874e778c1286b681713be1d905c300a3aa9c46d3 Mon Sep 17 00:00:00 2001 From: Adam Date: Wed, 17 Apr 2024 08:39:43 -0400 Subject: [PATCH 65/67] menu swapper: fix checkstyle violations --- .../MenuEntrySwapperPlugin.java | 48 ++++++++++++------- 1 file changed, 32 insertions(+), 16 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/menuentryswapper/MenuEntrySwapperPlugin.java index 46138fbcb6..0cb4bc19c8 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 @@ -2025,15 +2025,18 @@ private void setupTeleportSwaps() .addSub("Prifddinas", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 8)); teleportSwap("Other Teleports", ItemID.MAX_CAPE_13342) .worn() - .addSub("Feldip hills", () -> { + .addSub("Feldip hills", () -> + { pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Chinchompa Teleports pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Carnivorous chinchompas (Feldip Hills) }) - .addSub("Black chinchompas", () -> { + .addSub("Black chinchompas", () -> + { pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Chinchompa Teleports pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 2); // Black chinchompas (Wilderness) }) - .addSub("Hunter Guild", () -> { + .addSub("Hunter Guild", () -> + { pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Chinchompa Teleports pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 3); // Hunter Guild }) @@ -2048,47 +2051,58 @@ private void setupTeleportSwaps() .addSub("Crafting Guild", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 2)) .addSub("Farming Guild", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 3)) .addSub("Otto's Grotto", () -> pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 4)) - .addSub("Feldip hills", () -> { + .addSub("Feldip hills", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5); // Chinchompas pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 1); // Carnivorous chinchompas (Feldip Hills) }) - .addSub("Black chinchompas", () -> { + .addSub("Black chinchompas", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5); // Chinchompas pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 2); // Black chinchompas (Wilderness) }) - .addSub("Hunter Guild", () -> { + .addSub("Hunter Guild", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5); // Chinchompas pauseresume(ComponentID.DIALOG_OPTION_OPTIONS, 3); // Hunter Guild }) - .addSub("Home", () -> { + .addSub("Home", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 0); }) - .addSub("Rimmington", () -> { + .addSub("Rimmington", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 1); }) - .addSub("Taverley", () -> { + .addSub("Taverley", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 2); }) - .addSub("Pollnivneach", () -> { + .addSub("Pollnivneach", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 3); }) - .addSub("Hosidius", () -> { + .addSub("Hosidius", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 4); }) - .addSub("Rellekka", () -> { + .addSub("Rellekka", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 5); }) - .addSub("Yanille", () -> { + .addSub("Yanille", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); }) - .addSub("Prifddinas", () -> { + .addSub("Prifddinas", () -> + { pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 6); // POH Portals pauseresume(ComponentID.ADVENTURE_LOG_OPTIONS, 7); }); @@ -2179,7 +2193,8 @@ public void onMenuEntryAdded(MenuEntryAdded menuEntryAdded) .setTarget(target) .setType(MenuAction.RUNELITE) .setParent(me) - .onClick(e -> clientThread.invokeLater(() -> { + .onClick(e -> clientThread.invokeLater(() -> + { client.menuAction(p0, p1, MenuAction.CC_OP, id, itemId, option, target); sub.execute.run(); })); @@ -2216,7 +2231,8 @@ else if (me.getWidget() != null && me.getWidget().getId() == ComponentID.INVENTO .setTarget(target) .setType(MenuAction.RUNELITE) .setParent(me) - .onClick(e -> clientThread.invokeLater(() -> { + .onClick(e -> clientThread.invokeLater(() -> + { client.menuAction(p0, p1, MenuAction.CC_OP, id, itemId, option, target); sub.execute.run(); })); From e3067daa0980785e4e64fbf8296f8f9d4a8b4d4a Mon Sep 17 00:00:00 2001 From: Felanbird <41973452+Felanbird@users.noreply.github.com> Date: Wed, 17 Apr 2024 07:40:47 -0400 Subject: [PATCH 66/67] clues: update wilderness agility arena hard&master clues --- .../client/plugins/cluescrolls/clues/CoordinateClue.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java index ac49013e2b..d50e114cca 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/cluescrolls/clues/CoordinateClue.java @@ -179,7 +179,7 @@ private CoordinateClueInfo(@Nonnull String directions, Enemy enemy, boolean ligh .put(new WorldPoint(3189, 3963, 0), new CoordinateClueInfo("Wilderness. North of Resource Area, near magic axe hut.", ZAMORAK_WIZARD)) .put(new WorldPoint(2341, 3697, 0), new CoordinateClueInfo("North-east of the Piscatoris Fishing Colony bank.", SARADOMIN_WIZARD)) .put(new WorldPoint(3143, 3774, 0), new CoordinateClueInfo("In level 32 Wilderness, by the black chinchompa hunting area.", ZAMORAK_WIZARD)) - .put(new WorldPoint(2992, 3941, 0), new CoordinateClueInfo("Wilderness Agility Course, past the log balance.", ZAMORAK_WIZARD)) + .put(new WorldPoint(2970, 3913, 0), new CoordinateClueInfo("Frozen Waste Plateau, south-west of Wilderness Agility Course.", ZAMORAK_WIZARD)) .put(new WorldPoint(1410, 3611, 0), new CoordinateClueInfo("Lake Molch dock west of Shayzien Encampment.", SARADOMIN_WIZARD)) .put(new WorldPoint(1409, 3483, 0), new CoordinateClueInfo("South of Shayziens' Wall.", SARADOMIN_WIZARD)) // Elite @@ -248,7 +248,7 @@ private CoordinateClueInfo(@Nonnull String directions, Enemy enemy, boolean ligh .put(new WorldPoint(3380, 3929, 0), new CoordinateClueInfo("Wilderness. Near Volcano.", ANCIENT_WIZARDS)) .put(new WorldPoint(3188, 3939, 0), new CoordinateClueInfo("Wilderness. Resource Area.", BRASSICAN_MAGE)) .put(new WorldPoint(3304, 3941, 0), new CoordinateClueInfo("Wilderness. East of Rogues' Castle.", ANCIENT_WIZARDS)) - .put(new WorldPoint(2994, 3961, 0), new CoordinateClueInfo("Wilderness. Inside Agility Training Area.", BRASSICAN_MAGE)) + .put(new WorldPoint(3028, 3928, 0), new CoordinateClueInfo("Wilderness. South-east of Agility Training Area.", BRASSICAN_MAGE)) .put(new WorldPoint(1769, 3418, 0), new CoordinateClueInfo("Crabclaw Isle", ANCIENT_WIZARDS)) .build(); From 8ca32feac98e6670ec43162699a775dc14122e42 Mon Sep 17 00:00:00 2001 From: RuneLite updater Date: Wed, 17 Apr 2024 13:00:33 +0000 Subject: [PATCH 67/67] Release 1.10.27 --- cache/pom.xml | 2 +- pom.xml | 4 ++-- runelite-api/pom.xml | 2 +- runelite-client/pom.xml | 2 +- runelite-jshell/pom.xml | 2 +- runelite-maven-plugin/pom.xml | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cache/pom.xml b/cache/pom.xml index 11baf04ef8..077d2174cf 100644 --- a/cache/pom.xml +++ b/cache/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.10.27-SNAPSHOT + 1.10.27 cache diff --git a/pom.xml b/pom.xml index 87a216b2e6..2684af1899 100644 --- a/pom.xml +++ b/pom.xml @@ -28,7 +28,7 @@ net.runelite runelite-parent - 1.10.27-SNAPSHOT + 1.10.27 pom RuneLite @@ -63,7 +63,7 @@ https://github.com/runelite/runelite scm:git:git://github.com/runelite/runelite scm:git:git@github.com:runelite/runelite - HEAD + runelite-parent-1.10.27 diff --git a/runelite-api/pom.xml b/runelite-api/pom.xml index 68aa736838..55a56a9b23 100644 --- a/runelite-api/pom.xml +++ b/runelite-api/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.10.27-SNAPSHOT + 1.10.27 runelite-api diff --git a/runelite-client/pom.xml b/runelite-client/pom.xml index 6f353d4044..2239cd2d6e 100644 --- a/runelite-client/pom.xml +++ b/runelite-client/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.10.27-SNAPSHOT + 1.10.27 client diff --git a/runelite-jshell/pom.xml b/runelite-jshell/pom.xml index 8c536caa26..a5d8640445 100644 --- a/runelite-jshell/pom.xml +++ b/runelite-jshell/pom.xml @@ -30,7 +30,7 @@ net.runelite runelite-parent - 1.10.27-SNAPSHOT + 1.10.27 jshell diff --git a/runelite-maven-plugin/pom.xml b/runelite-maven-plugin/pom.xml index e443faed1e..868f013e4b 100644 --- a/runelite-maven-plugin/pom.xml +++ b/runelite-maven-plugin/pom.xml @@ -29,7 +29,7 @@ net.runelite runelite-parent - 1.10.27-SNAPSHOT + 1.10.27 runelite-maven-plugin