From db9866a13e17a14e9bf4ee93ab83c174efc5cfd2 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 17 Feb 2018 01:37:20 -0700 Subject: [PATCH 1/4] Change perspective to convert world to local points centered on the tile --- runelite-api/src/main/java/net/runelite/api/Perspective.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/Perspective.java b/runelite-api/src/main/java/net/runelite/api/Perspective.java index 851a313c53..e9362efd97 100644 --- a/runelite-api/src/main/java/net/runelite/api/Perspective.java +++ b/runelite-api/src/main/java/net/runelite/api/Perspective.java @@ -279,8 +279,8 @@ public class Perspective int baseX = client.getBaseX(); int baseY = client.getBaseY(); - int x = (point.getX() - baseX) << LOCAL_COORD_BITS; - int y = (point.getY() - baseY) << LOCAL_COORD_BITS; + int x = ((point.getX() - baseX) << LOCAL_COORD_BITS) + (1 << LOCAL_COORD_BITS - 1) - 1; + int y = ((point.getY() - baseY) << LOCAL_COORD_BITS) + (1 << LOCAL_COORD_BITS - 1) - 1; return new Point(x, y); } From fe16a0537e77c5139dd9836ee0b0f7fa031f5f0a Mon Sep 17 00:00:00 2001 From: Max Weber Date: Sat, 17 Feb 2018 01:38:12 -0700 Subject: [PATCH 2/4] Add Kourend Library plugin --- .../java/net/runelite/api/AnimationID.java | 1 + .../main/java/net/runelite/api/Point3D.java | 39 + .../net/runelite/api/widgets/WidgetID.java | 7 + .../net/runelite/api/widgets/WidgetInfo.java | 3 + .../client/plugins/kourendlibrary/Book.java | 147 ++++ .../plugins/kourendlibrary/BookPanel.java | 77 ++ .../plugins/kourendlibrary/Bookcase.java | 121 +++ .../kourendlibrary/KourendLibraryOverlay.java | 227 +++++ .../kourendlibrary/KourendLibraryPanel.java | 137 +++ .../kourendlibrary/KourendLibraryPlugin.java | 188 ++++ .../plugins/kourendlibrary/Library.java | 800 ++++++++++++++++++ .../kourendlibrary/LibraryCustomer.java | 67 ++ .../plugins/kourendlibrary/SolvedState.java | 32 + .../plugins/kourendlibrary/items/13514.png | Bin 0 -> 679 bytes .../plugins/kourendlibrary/items/13515.png | Bin 0 -> 658 bytes .../plugins/kourendlibrary/items/13516.png | Bin 0 -> 661 bytes .../plugins/kourendlibrary/items/13517.png | Bin 0 -> 676 bytes .../plugins/kourendlibrary/items/13518.png | Bin 0 -> 635 bytes .../plugins/kourendlibrary/items/13519.png | Bin 0 -> 655 bytes .../plugins/kourendlibrary/items/13520.png | Bin 0 -> 621 bytes .../plugins/kourendlibrary/items/13521.png | Bin 0 -> 607 bytes .../plugins/kourendlibrary/items/13522.png | Bin 0 -> 602 bytes .../plugins/kourendlibrary/items/13523.png | Bin 0 -> 631 bytes .../plugins/kourendlibrary/items/13524.png | Bin 0 -> 783 bytes .../plugins/kourendlibrary/items/13525.png | Bin 0 -> 811 bytes .../plugins/kourendlibrary/items/13526.png | Bin 0 -> 872 bytes .../plugins/kourendlibrary/items/13527.png | Bin 0 -> 854 bytes .../plugins/kourendlibrary/items/13528.png | Bin 0 -> 868 bytes .../plugins/kourendlibrary/items/13529.png | Bin 0 -> 802 bytes .../plugins/kourendlibrary/items/13530.png | Bin 0 -> 686 bytes .../plugins/kourendlibrary/items/13531.png | Bin 0 -> 749 bytes .../plugins/kourendlibrary/items/13532.png | Bin 0 -> 735 bytes .../plugins/kourendlibrary/items/13533.png | Bin 0 -> 854 bytes .../plugins/kourendlibrary/items/13534.png | Bin 0 -> 1134 bytes .../plugins/kourendlibrary/items/13535.png | Bin 0 -> 816 bytes .../plugins/kourendlibrary/items/13536.png | Bin 0 -> 1203 bytes .../plugins/kourendlibrary/items/13537.png | Bin 0 -> 1171 bytes .../plugins/kourendlibrary/items/19637.png | Bin 0 -> 947 bytes .../plugins/kourendlibrary/items/21756.png | Bin 0 -> 738 bytes .../plugins/kourendlibrary/panel_icon.png | Bin 0 -> 2320 bytes 40 files changed, 1846 insertions(+) create mode 100644 runelite-api/src/main/java/net/runelite/api/Point3D.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Book.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/BookPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Bookcase.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/LibraryCustomer.java create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/SolvedState.java create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13514.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13515.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13516.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13517.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13518.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13519.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13520.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13521.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13522.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13523.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13524.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13525.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13526.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13527.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13528.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13529.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13530.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13531.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13532.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13533.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13534.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13535.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13536.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13537.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/19637.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/21756.png create mode 100644 runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/panel_icon.png 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 7b6a24e35c..b4f64915b7 100644 --- a/runelite-api/src/main/java/net/runelite/api/AnimationID.java +++ b/runelite-api/src/main/java/net/runelite/api/AnimationID.java @@ -103,6 +103,7 @@ public final class AnimationID public static final int HERBLORE_POTIONMAKING = 363; //used for both herb and secondary public static final int MAGIC_CHARGING_ORBS = 726; public static final int BURYING_BONES = 827; + public static final int LOOKING_INTO = 832; // NPC animations public static final int TZTOK_JAD_MAGIC_ATTACK = 2656; diff --git a/runelite-api/src/main/java/net/runelite/api/Point3D.java b/runelite-api/src/main/java/net/runelite/api/Point3D.java new file mode 100644 index 0000000000..2aef205009 --- /dev/null +++ b/runelite-api/src/main/java/net/runelite/api/Point3D.java @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.api; + +import lombok.Value; +import net.runelite.api.Point; + +@Value +public class Point3D +{ + private final int X, Y, Z; + + public Point toPoint() + { + return new Point(X, Y); + } +} diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index ad56114ddd..e559311608 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -66,6 +66,7 @@ public class WidgetID public static final int PUZZLE_BOX_GROUP_ID = 306; public static final int NIGHTMARE_ZONE_GROUP_ID = 202; public static final int BLAST_FURNACE_GROUP_ID = 474; + public static final int LIBRARY_FIND_GROUP_ID = 193; static class WorldMap { @@ -287,4 +288,10 @@ public class WidgetID { static final int VISIBLE_BOX = 4; } + + static class DialogLibrary + { + static final int ITEM = 0; + static final int TEXT = 1; + } } diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 261031a4d7..4c1d6a3caa 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -120,6 +120,9 @@ public enum WidgetInfo COMBAT_SPELL_ICON(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.SPELL_ICON), COMBAT_SPELL_TEXT(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.SPELL_TEXT), + DIALOG_LIBRARY_FIND(WidgetID.LIBRARY_FIND_GROUP_ID, WidgetID.DialogLibrary.ITEM), + DIALOG_LIBRARY_FIND_TEXT(WidgetID.LIBRARY_FIND_GROUP_ID, WidgetID.DialogLibrary.TEXT), + DIALOG_NPC_NAME(WidgetID.DIALOG_NPC_GROUP_ID, WidgetID.DialogNPC.NAME), DIALOG_NPC_TEXT(WidgetID.DIALOG_NPC_GROUP_ID, WidgetID.DialogNPC.TEXT), DIALOG_NPC_HEAD_MODEL(WidgetID.DIALOG_NPC_GROUP_ID, WidgetID.DialogNPC.HEAD_MODEL), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Book.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Book.java new file mode 100644 index 0000000000..db18505b07 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Book.java @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import javax.imageio.ImageIO; +import lombok.Getter; +import net.runelite.api.ItemID; + +public enum Book +{ + DARK_MANUSCRIPT_13514(ItemID.DARK_MANUSCRIPT), + DARK_MANUSCRIPT_13515(ItemID.DARK_MANUSCRIPT_13515), + DARK_MANUSCRIPT_13516(ItemID.DARK_MANUSCRIPT_13516), + DARK_MANUSCRIPT_13517(ItemID.DARK_MANUSCRIPT_13517), + DARK_MANUSCRIPT_13518(ItemID.DARK_MANUSCRIPT_13518), + DARK_MANUSCRIPT_13519(ItemID.DARK_MANUSCRIPT_13519), + DARK_MANUSCRIPT_13520(ItemID.DARK_MANUSCRIPT_13520), + DARK_MANUSCRIPT_13521(ItemID.DARK_MANUSCRIPT_13521), + DARK_MANUSCRIPT_13522(ItemID.DARK_MANUSCRIPT_13522), + DARK_MANUSCRIPT_13523(ItemID.DARK_MANUSCRIPT_13523), + + RADAS_CENSUS(ItemID.RADAS_CENSUS, "Rada's Census", "Census of King Rada III, by Matthias Vorseth."), + RICKTORS_DIARY_7(ItemID.RICKTORS_DIARY_7, "Ricktor's Diary 7", "Diary of Steklan Ricktor, volume 7."), + EATHRAM_RADA_EXTRACT(ItemID.EATHRAM__RADA_EXTRACT, "Eathram & Rada extract", "An extract from Eathram & Rada, by Anonymous."), + KILLING_OF_A_KING(ItemID.KILLING_OF_A_KING, "Killing of a King", "Killing of a King, by Griselle."), + HOSIDIUS_LETTER(ItemID.HOSIDIUS_LETTER, "Hosidius Letter", "A letter from Lord Hosidius to the Council of Elders."), + WINTERTODT_PARABLE(ItemID.WINTERTODT_PARABLE, "Wintertodt Parable", "The Parable of the Wintertodt, by Anonymous."), + TWILL_ACCORD(ItemID.TWILL_ACCORD, "Twill Accord", "The Royal Accord of Twill."), + BYRNES_CORONATION_SPEECH(ItemID.BYRNES_CORONATION_SPEECH, "Byrnes Coronation Speech", "Speech of King Byrne I, on the occasion of his coronation."), + IDEOLOGY_OF_DARKNESS(ItemID.IDEOLOGY_OF_DARKNESS, "The Ideology of Darkness", "The Ideology of Darkness, by Philophaire."), + RADAS_JOURNEY(ItemID.RADAS_JOURNEY, "Rada's Journey", "The Journey of Rada, by Griselle."), + TRANSVERGENCE_THEORY(ItemID.TRANSVERGENCE_THEORY, "Transvergence Theory", "The Theory of Transvergence, by Amon Ducot."), + TRISTESSAS_TRAGEDY(ItemID.TRISTESSAS_TRAGEDY, "Tristessa's Tradgedy", "The Tragedy of Tristessa."), + TREACHERY_OF_ROYALTY(ItemID.TREACHERY_OF_ROYALTY, "The Treachery of Royalty", "The Treachery of Royalty, by Professor Answith."), + TRANSPORTATION_INCANTATIONS(ItemID.TRANSPORTATION_INCANTATIONS, "Transportation Incantations", "Transportation Incantations, by Amon Ducot."), + SOUL_JORUNEY(ItemID.SOUL_JOURNEY, "Soul Journey", "The Journey of Souls, by Aretha."), + VARLAMORE_ENVOY(ItemID.VARLAMORE_ENVOY, "Varlamore Envoy", "The Envoy to Varlamore, by Deryk Paulson."); + + private static final Map BY_ID = buildById(); + + private static final Map BY_NAME = buildByName(); + + private static Map buildById() + { + HashMap byId = new HashMap<>(); + for (Book b : Book.values()) + { + byId.put(b.item, b); + } + return Collections.unmodifiableMap(byId); + } + + private static final Map buildByName() + { + HashMap byName = new HashMap<>(); + for (Book b : Book.values()) + { + if (!b.isDarkManuscript) + { + byName.put(b.name, b); + } + } + return Collections.unmodifiableMap(byName); + } + + public static Book byId(int id) + { + return BY_ID.get(id); + } + + public static Book byName(String name) + { + return BY_NAME.get(name); + } + + @Getter + private final int item; + + @Getter + private final String name; + + @Getter + private final String shortName; + + @Getter + private final BufferedImage icon; + + @Getter + private final boolean isDarkManuscript; + + Book(int id, String shortName, String name) + { + this.item = id; + this.isDarkManuscript = false; + this.shortName = shortName; + this.name = name; + this.icon = getImage(Integer.toString(id)); + } + + Book(int id) + { + this.item = id; + this.isDarkManuscript = true; + this.name = "Dark Manuscript"; + this.shortName = "Dark Manuscript"; + this.icon = getImage(Integer.toString(id)); + } + + private static BufferedImage getImage(String name) + { + try + { + return ImageIO.read(Book.class.getResourceAsStream("items/" + name + ".png")); + } + catch (IOException | IllegalArgumentException e) + { + throw new RuntimeException("Cannot load book " + name, e); + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/BookPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/BookPanel.java new file mode 100644 index 0000000000..a5d1c0db53 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/BookPanel.java @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +import java.awt.Color; +import javax.swing.GroupLayout; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JPanel; +import net.runelite.client.ui.FontManager; + +class BookPanel extends JPanel +{ + private JLabel location = new JLabel(); + + BookPanel(Book b) + { + GroupLayout layout = new GroupLayout(this); + this.setLayout(layout); + + JLabel image = new JLabel(new ImageIcon(b.getIcon())); + JLabel name = new JLabel(b.getShortName()); + location.setFont(FontManager.getRunescapeSmallFont()); + + layout.setVerticalGroup(layout.createParallelGroup() + .addComponent(image) + .addGroup(layout.createSequentialGroup() + .addComponent(name) + .addComponent(location) + ) + ); + + layout.setHorizontalGroup(layout.createSequentialGroup() + .addComponent(image) + .addGap(8) + .addGroup(layout.createParallelGroup() + .addComponent(name) + .addComponent(location) + ) + ); + + // AWT's Z order is weird. This put image at the back of the stack + setComponentZOrder(image, getComponentCount() - 1); + } + + public void setLocation(String location) + { + this.location.setText(location); + } + + public void setIsTarget(boolean target) + { + location.setForeground(target ? Color.GREEN : Color.WHITE); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Bookcase.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Bookcase.java new file mode 100644 index 0000000000..3aed5b383c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Bookcase.java @@ -0,0 +1,121 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; +import lombok.Getter; +import net.runelite.api.Point3D; + +class Bookcase +{ + public Bookcase(Point3D location) + { + this.location = location; + this.index = new ArrayList<>(); + } + + @Getter + private final Point3D location; + + @Getter + private final List index; + + @Getter + private boolean isBookSet; + + /** + * Book in this bookcase as found by the player. + * Will be correct as long as isBookSet is true, unless the library has reset; + */ + @Getter + private Book book; + + /** + * Books that can be in this slot. Will only be populated if library.state != SolvedState.NO_DATA + */ + @Getter + private Set possibleBooks = new HashSet<>(); + + public void clearBook() + { + book = null; + isBookSet = false; + } + + public void setBook(Book book) + { + this.book = book; + this.isBookSet = true; + } + + public String getLocationString() + { + StringBuilder b = new StringBuilder(); + + boolean north = location.getY() > 3815; + boolean west = location.getX() < 1625; + if (north && west) + { + b.append("Northwest"); + } + else if (north) + { + b.append("Northeast"); + } + else if (west) + { + b.append("Southwest"); + } + else + { + b.append("Center"); + } + + b.append(" "); + + switch (location.getZ()) + { + case 0: + b.append("ground floor"); + break; + case 1: + b.append("middle floor"); + break; + case 2: + b.append("top floor"); + break; + } + + if (KourendLibraryPlugin.debug) + { + b.append(" ").append(index.stream().map(Object::toString).collect(Collectors.joining(", "))); + } + return b.toString(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java new file mode 100644 index 0000000000..d3beb6c9fc --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryOverlay.java @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +import com.google.inject.Inject; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FontMetrics; +import java.awt.Graphics2D; + +import java.awt.Polygon; +import java.awt.geom.Rectangle2D; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; +import net.runelite.api.Client; +import net.runelite.api.Perspective; +import net.runelite.api.Player; +import net.runelite.api.Point; +import net.runelite.api.Point3D; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayLayer; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayUtil; + +import static net.runelite.api.Perspective.getCanvasTilePoly; + +public class KourendLibraryOverlay extends Overlay +{ + private final static Point LIBRARY_CENTER = new Point(1632, 3807); + private final static int MAXIMUM_DISTANCE = 24; + private final static int ROUGH_ENABLE_DISTANCE = 45; + + private final Library library; + private final Client client; + + @Inject + KourendLibraryOverlay(Library library, Client client) + { + this.library = library; + this.client = client; + + setPosition(OverlayPosition.DYNAMIC); + setLayer(OverlayLayer.ABOVE_SCENE); + } + + @Override + public Dimension render(Graphics2D g, java.awt.Point parent) + { + Player player = client.getLocalPlayer(); + if (player == null) + { + return null; + } + + Point playerLoc = player.getWorldLocation(); + + if (playerLoc.distanceTo(LIBRARY_CENTER) > ROUGH_ENABLE_DISTANCE) + { + return null; + } + + List allBookcases = library.getBookcasesOnLevel(client.getPlane()); + + if (allBookcases == null) + { + return null; + } + + for (Bookcase bookcase : allBookcases) + { + // AABB + Point3D caseLoc = bookcase.getLocation(); + if (Math.abs(playerLoc.getX() - caseLoc.getX()) > MAXIMUM_DISTANCE + || Math.abs(playerLoc.getY() - caseLoc.getY()) > MAXIMUM_DISTANCE) + { + continue; + } + + Point localBookcase = Perspective.worldToLocal(client, caseLoc.toPoint()); + Point screenBookcase = Perspective.worldToCanvas(client, localBookcase.getX(), localBookcase.getY(), caseLoc.getZ(), 25); + + if (screenBookcase != null) + { + boolean bookIsKnown = bookcase.isBookSet(); + Book book = bookcase.getBook(); + Set possible = bookcase.getPossibleBooks(); + if (bookIsKnown && book == null) + { + for (Book b : possible) + { + if (b != null && b.isDarkManuscript()) + { + book = b; + break; + } + } + } + + if (!bookIsKnown && possible.size() == 1) + { + book = possible.iterator().next(); + bookIsKnown = true; + } + Color color = bookIsKnown ? Color.ORANGE : Color.WHITE; + + // Render the poly on the floor + if (!(bookIsKnown && book == null) && (library.getState() == SolvedState.NO_DATA || book != null || possible.size() > 0)) + { + Polygon poly = getCanvasTilePoly(client, localBookcase); + if (poly != null) + { + OverlayUtil.renderPolygon(g, poly, color); + } + } + + int height = 0; + // If the book is singled out, render the text and the book's icon + if (bookIsKnown) + { + if (book != null) + { + FontMetrics fm = g.getFontMetrics(); + Rectangle2D bounds = fm.getStringBounds(book.getShortName(), g); + height = (int) bounds.getHeight() + book.getIcon().getHeight() + 6; + Point textLoc = new Point( + (int) (screenBookcase.getX() - (bounds.getWidth() / 2)), + screenBookcase.getY() - (height / 2) + (int) bounds.getHeight() + ); + OverlayUtil.renderTextLocation(g, textLoc, book.getShortName(), color); + g.drawImage( + book.getIcon(), + screenBookcase.getX() - (book.getIcon().getWidth() / 2), + screenBookcase.getY() + (height / 2) - book.getIcon().getHeight(), + null + ); + } + } + else + { + // otherwise render up to 9 icons of the possible books in the bookcase in a square + final int BOOK_ICON_SIZE = 32; + Book[] books = possible.stream() + .filter(Objects::nonNull) + .limit(9) + .toArray(Book[]::new); + if (books.length > 1 && books.length <= 9) + { + int cols = (int) Math.ceil(Math.sqrt(books.length)); + int rows = (int) Math.ceil((double) books.length / cols); + height = rows * BOOK_ICON_SIZE; + int xbase = screenBookcase.getX() - ((cols * BOOK_ICON_SIZE) / 2); + int ybase = screenBookcase.getY() - rows * BOOK_ICON_SIZE / 2; + + for (int i = 0; i < books.length; i++) + { + int col = i % cols; + int row = i / cols; + int x = col * BOOK_ICON_SIZE; + int y = row * BOOK_ICON_SIZE; + if (row == rows - 1) + { + x += (BOOK_ICON_SIZE * (books.length % rows)) / 2; + } + g.drawImage(books[i].getIcon(), xbase + x, ybase + y, null); + } + } + } + + // Draw the bookcase's ID on top + if (KourendLibraryPlugin.debug) + { + FontMetrics fm = g.getFontMetrics(); + String str = bookcase.getIndex().stream().map(Object::toString).collect(Collectors.joining(", ")); + Rectangle2D bounds = fm.getStringBounds(str, g); + Point textLoc = new Point((int) (screenBookcase.getX() - (bounds.getWidth() / 2)), screenBookcase.getY() - (height / 2)); + OverlayUtil.renderTextLocation(g, textLoc, str, Color.WHITE); + } + } + } + + // Render the customer's wanted book on their head and a poly under their feet + LibraryCustomer customer = library.getCustomer(); + if (customer != null) + { + client.getNpcs().stream() + .filter(n -> n.getId() == customer.getId()) + .forEach(n -> + { + Book b = library.getCustomerBook(); + Point local = n.getLocalLocation(); + Polygon poly = getCanvasTilePoly(client, local); + OverlayUtil.renderPolygon(g, poly, Color.WHITE); + Point screen = Perspective.worldToCanvas(client, local.getX(), local.getY(), client.getPlane(), n.getLogicalHeight()); + if (screen != null) + { + g.drawImage(b.getIcon(), screen.getX() - (b.getIcon().getWidth() / 2), screen.getY() - b.getIcon().getHeight(), null); + } + }); + } + + return null; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java new file mode 100644 index 0000000000..fb894ee2a8 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPanel.java @@ -0,0 +1,137 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +import com.google.inject.Inject; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.util.Comparator; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.stream.Collectors; +import java.util.stream.Stream; +import javax.inject.Singleton; +import javax.swing.GroupLayout; +import javax.swing.JButton; +import javax.swing.JPanel; +import javax.swing.SwingUtilities; +import net.runelite.client.ui.PluginPanel; + +@Singleton +public class KourendLibraryPanel extends PluginPanel +{ + @Inject + private Library library; + + private final HashMap bookPanels = new HashMap<>(); + + void init() + { + GroupLayout layout = new GroupLayout(this); + setLayout(layout); + + JPanel books = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.gridx = 0; + c.gridy = 0; + Stream.of(Book.values()) + .filter(b -> !b.isDarkManuscript()) + .sorted(Comparator.comparing(Book::getShortName)) + .forEach(b -> + { + BookPanel p = new BookPanel(b); + bookPanels.put(b, p); + books.add(p, c); + c.gridy++; + }); + + JButton reset = new JButton("Reset"); + reset.addActionListener(e -> + { + library.reset(); + update(); + }); + + layout.setHorizontalGroup(layout.createParallelGroup() + .addComponent(books) + .addComponent(reset) + ); + layout.setVerticalGroup(layout.createSequentialGroup() + .addComponent(books) + .addGap(4) + .addComponent(reset) + ); + + update(); + } + + void update() + { + SwingUtilities.invokeLater(() -> + { + Book customerBook = library.getCustomerBook(); + for (Map.Entry b : bookPanels.entrySet()) + { + b.getValue().setIsTarget(customerBook == b.getKey()); + } + + HashMap> bookLocations = new HashMap<>(); + + for (Bookcase bookcase : library.getBookcases()) + { + if (bookcase.getBook() != null) + { + bookLocations.computeIfAbsent(bookcase.getBook(), a -> new HashSet<>()).add(bookcase.getLocationString()); + } + else + { + for (Book book : bookcase.getPossibleBooks()) + { + if (book != null) + { + bookLocations.computeIfAbsent(book, a -> new HashSet<>()).add(bookcase.getLocationString()); + } + } + } + } + + for (Map.Entry e : bookPanels.entrySet()) + { + HashSet locs = bookLocations.get(e.getKey()); + if (locs == null || locs.size() > 3) + { + e.getValue().setLocation("Unknown"); + } + else + { + e.getValue().setLocation("" + locs.stream().collect(Collectors.joining("
")) + ""); + } + } + }); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java new file mode 100644 index 0000000000..de7bb5265c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +import com.google.common.eventbus.Subscribe; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.imageio.ImageIO; +import javax.inject.Inject; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.AnimationID; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.MenuAction; +import net.runelite.api.Perspective; +import net.runelite.api.Point; +import net.runelite.api.Point3D; +import net.runelite.api.events.AnimationChanged; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.GameTick; +import net.runelite.api.events.MenuOptionClicked; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.ui.ClientUI; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.overlay.Overlay; + +@PluginDescriptor( + name = "Kourend Library" +) +@Slf4j +public class KourendLibraryPlugin extends Plugin +{ + final static boolean debug = false; + + @Inject + private ClientUI ui; + + @Inject + private Client client; + + @Inject + private Library library; + + @Inject + private KourendLibraryOverlay overlay; + + private KourendLibraryPanel panel; + private NavigationButton navButton; + + private Point3D lastBookcaseClick = null; + private Point3D lastBookcaseAnimatedOn = null; + + @Override + protected void startUp() throws Exception + { + panel = injector.getInstance(KourendLibraryPanel.class); + panel.init(); + + navButton = new NavigationButton( + "Kourend Library", + ImageIO.read(Book.class.getResourceAsStream("panel_icon.png")), + () -> panel + ); + + ui.getPluginToolbar().addNavigation(navButton); + } + + @Override + protected void shutDown() + { + ui.getPluginToolbar().removeNavigation(navButton); + } + + @Override + public Overlay getOverlay() + { + return overlay; + } + + @Subscribe + private void onMenuOptionClicked(MenuOptionClicked menuOpt) + { + if (MenuAction.GAME_OBJECT_FIRST_OPTION == menuOpt.getMenuAction() && menuOpt.getMenuTarget().contains("Bookshelf")) + { + Point worldPoint = Perspective.regionToWorld(client, new Point(menuOpt.getId() & 127, menuOpt.getId() >> 7 & 127)); + lastBookcaseClick = new Point3D(worldPoint.getX(), worldPoint.getY(), client.getPlane()); + } + } + + @Subscribe + private void onAnimationChanged(AnimationChanged anim) + { + if (anim.getActor() == client.getLocalPlayer() && anim.getActor().getAnimation() == AnimationID.LOOKING_INTO) + { + lastBookcaseAnimatedOn = lastBookcaseClick; + } + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { + if (lastBookcaseAnimatedOn != null && event.getType() == ChatMessageType.SERVER) + { + if (event.getMessage().equals("You don't find anything useful here.")) + { + library.mark(lastBookcaseAnimatedOn, null); + panel.update(); + lastBookcaseAnimatedOn = null; + } + } + } + + private static final Pattern BOOK_EXTRACTOR = Pattern.compile("'(.*)'"); + private static final Pattern TAG_MATCHER = Pattern.compile("(<[^>]*>)"); + + @Subscribe + void onTick(GameTick tick) + { + if (lastBookcaseAnimatedOn != null) + { + Widget find = client.getWidget(WidgetInfo.DIALOG_LIBRARY_FIND); + if (find != null) + { + Book book = Book.byId(find.getItemId()); + if (book != null) + { + library.mark(lastBookcaseAnimatedOn, book); + panel.update(); + lastBookcaseAnimatedOn = null; + } + } + } + + Widget npcHead = client.getWidget(WidgetInfo.DIALOG_NPC_HEAD_MODEL); + if (npcHead != null) + { + LibraryCustomer cust = LibraryCustomer.getById(npcHead.getModelId()); + if (cust != null) + { + Widget textw = client.getWidget(WidgetInfo.DIALOG_NPC_TEXT); + String text = textw.getText(); + Matcher m = BOOK_EXTRACTOR.matcher(text); + if (m.find()) + { + String bookName = TAG_MATCHER.matcher(m.group(1).replace("
", " ")).replaceAll(""); + Book book = Book.byName(bookName); + if (book == null) + { + log.warn("Book '{}' is not recognised", bookName); + return; + } + library.setCustomer(cust, book); + panel.update(); + } + else if (text.contains("You can have this other book") || text.contains("please accept a token of my thanks.") || text.contains("Thanks, I'll get on with reading it.")) + { + library.setCustomer(null, null); + panel.update(); + } + } + } + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java new file mode 100644 index 0000000000..3dc1905dd9 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/Library.java @@ -0,0 +1,800 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.stream.IntStream; +import javax.inject.Singleton; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.Point3D; + +import static net.runelite.client.plugins.kourendlibrary.Book.*; + +/** + * Library represents a instance of the Kourend/Arceuus House library. + *

+ * The library changes the locations of it's books every 60-90 minutes. + * Of the 554 bookcases in the library, only 346 of them can ever have books. + * 6 of the bookcases in the south-west corner of the top floor are duplicated. + * These 6 bookcases are not handled 100% correctly due to their low chance + * of being used to train the predictor. + *

+ * Each of the 352 bookcase slots "Bookcase"s has an index which is used to + * place the book inside of them. The game chooses one of the 5 sequences and a + * bookcase starting index, then places a book from the sequence into every 13th + * bookcase, by index. Each sequence contains 26 Books, consisting of 16 books + * and 10 dark manuscripts. You can only get one dark manuscript at a time, though + * they are all placed into shelves. + */ +@Singleton +@Slf4j +public class Library +{ + private final Map byPoint = new HashMap<>(); + private final Map> byLevel = new HashMap<>(); + private final List byIndex = new ArrayList<>(); + + private final List> sequences = populateSequences(); + + private final int step; + + @Getter + private SolvedState state; + + @Getter + private Book customerBook; + + @Getter + private LibraryCustomer customer; + + Library() + { + populateBooks(); + step = byIndex.size() / Book.values().length; + reset(); + } + + public synchronized List getBookcasesOnLevel(int z) + { + return Collections.unmodifiableList(byLevel.get(z)); + } + + public synchronized List getBookcases() + { + return Collections.unmodifiableList(byIndex); + } + + public void setCustomer(LibraryCustomer customer, Book book) + { + this.customer = customer; + this.customerBook = book; + } + + public synchronized void reset() + { + state = SolvedState.NO_DATA; + for (Bookcase b : byIndex) + { + b.clearBook(); + b.getPossibleBooks().clear(); + } + log.info("Library is now reset"); + } + + public synchronized void mark(Point3D loc, Book book) + { + Bookcase bookcase = byPoint.get(loc); + if (bookcase == null) + { + log.debug("Requested non-existent bookcase at {}", loc); + return; + } + + if (bookcase.isBookSet()) + { + // Bookcase is set from a previous mark + // Check for a mismatch, unless it is now null and had a dark manuscript + if (book != bookcase.getBook() && !(book == null && bookcase.getBook().isDarkManuscript())) + { + reset(); + } + } + else if (state != SolvedState.NO_DATA) + { + // We know all of the possible things in this shelf. + if (book != null) + { + // Check to see if our guess is wrong + if (!bookcase.getPossibleBooks().contains(book)) + { + reset(); + } + } + } + + // Everything is known, nothing to do + if (state == SolvedState.COMPLETE) + { + return; + } + + log.info("Setting bookcase {} to {}", bookcase.getIndex(), book); + for (; ; ) + { + bookcase.setBook(book); + + // Basing the sequences on null is not supported, though possible + if (book == null) + { + return; + } + + // This is one of the 6 bookcases with 2 ids. Not fully supported. + if (bookcase.getIndex().size() != 1) + { + return; + } + + int bookcaseIndex = bookcase.getIndex().get(0); + + state = SolvedState.INCOMPLETE; + + // Map each sequence to the number of bookcases that match the sequence + // return 0 if it is a mismatch. + // Keep in mind that Bookcases with dark manuscripts may be set to null. + int[] certainty = sequences.stream().mapToInt(sequence -> + { + int zero = getBookcaseZeroIndexForSequenceWithBook(sequence, bookcaseIndex, book); + + int found = 0; + for (int i = 0; i < byIndex.size(); i++) + { + int ai = (i + zero) % byIndex.size(); + Bookcase iBookcase = byIndex.get(ai); + if (i % step == 0) + { + int seqI = i / step; + if (iBookcase.isBookSet() && seqI < sequence.size()) + { + Book seqBook = sequence.get(seqI); + boolean isSeqManuscript = seqBook == null || seqBook.isDarkManuscript(); + if (!((isSeqManuscript && iBookcase.getBook() == null) || (iBookcase.getBook() == seqBook))) + { + log.debug("Bailing @ i={} ai={} {}; {} != {}", i, ai, iBookcase.getIndex(), iBookcase.getBook(), seqBook); + found = 0; + break; + } + found++; + } + } + else + { + // Only bail if this isn't a double bookcase + if (iBookcase.isBookSet() && iBookcase.getBook() != null && iBookcase.getIndex().size() == 1) + { + log.debug("Bailing @ i={} ai={} {}; {} is set", i, ai, iBookcase.getIndex(), iBookcase.getBook()); + found = 0; + break; + } + } + } + return found; + }).toArray(); + log.info("Certainty is now {}", certainty); + + for (Bookcase b : byIndex) + { + b.getPossibleBooks().clear(); + } + + // Write the most likely sequences onto the bookcases + int max = IntStream.of(certainty).max().getAsInt(); + + // We have books set, but 0 sequences match, Something is wrong, reset. + if (max == 0) + { + reset(); + continue; + } + + IntStream.range(0, sequences.size()) + .filter(i -> certainty[i] == max) + .forEach(isequence -> + { + List sequence = sequences.get(isequence); + int zero = getBookcaseZeroIndexForSequenceWithBook(sequence, bookcaseIndex, book); + + for (int i = 0; i < byIndex.size(); i++) + { + int ai = (i + zero) % byIndex.size(); + Bookcase iBookcase = byIndex.get(ai); + if (iBookcase.getBook() == null) + { + int iseq = i / step; + if (i % step == 0 && iseq < sequence.size()) + { + Book seqBook = sequence.get(iseq); + iBookcase.getPossibleBooks().add(seqBook); + } + } + } + }); + if (IntStream.range(0, certainty.length).filter(i -> certainty[i] == max).count() == 1) + { + state = SolvedState.COMPLETE; + } + return; + } + } + + /** + * Find the bookcase index that is index zero in the sequence, identifying by the book in bookcase + */ + private int getBookcaseZeroIndexForSequenceWithBook(List sequences, int bookcaseIndex, Book book) + { + int bookSequence = sequences.indexOf(book); + assert bookSequence >= 0; + + bookcaseIndex -= step * bookSequence; + for (; bookcaseIndex < 0; ) + { + bookcaseIndex += byIndex.size(); + } + return bookcaseIndex; + } + + private List> populateSequences() + { + List> books = Arrays.asList( + Arrays.asList( + DARK_MANUSCRIPT_13516, + KILLING_OF_A_KING, + DARK_MANUSCRIPT_13520, + IDEOLOGY_OF_DARKNESS, + RADAS_JOURNEY, + TRANSVERGENCE_THEORY, + TRISTESSAS_TRAGEDY, + DARK_MANUSCRIPT_13523, + DARK_MANUSCRIPT_13521, + RADAS_CENSUS, + TREACHERY_OF_ROYALTY, + HOSIDIUS_LETTER, + DARK_MANUSCRIPT_13519, + RICKTORS_DIARY_7, + DARK_MANUSCRIPT_13514, + EATHRAM_RADA_EXTRACT, + DARK_MANUSCRIPT_13522, + VARLAMORE_ENVOY, + WINTERTODT_PARABLE, + TWILL_ACCORD, + DARK_MANUSCRIPT_13515, + BYRNES_CORONATION_SPEECH, + DARK_MANUSCRIPT_13517, + SOUL_JORUNEY, + DARK_MANUSCRIPT_13518, + TRANSPORTATION_INCANTATIONS + ), + Arrays.asList( + DARK_MANUSCRIPT_13516, + KILLING_OF_A_KING, + DARK_MANUSCRIPT_13520, + IDEOLOGY_OF_DARKNESS, + RADAS_JOURNEY, + TRANSVERGENCE_THEORY, + TRISTESSAS_TRAGEDY, + DARK_MANUSCRIPT_13523, + DARK_MANUSCRIPT_13521, + RADAS_CENSUS, + TREACHERY_OF_ROYALTY, + HOSIDIUS_LETTER, + VARLAMORE_ENVOY, + DARK_MANUSCRIPT_13519, + RICKTORS_DIARY_7, + DARK_MANUSCRIPT_13514, + EATHRAM_RADA_EXTRACT, + DARK_MANUSCRIPT_13522, + SOUL_JORUNEY, + WINTERTODT_PARABLE, + TWILL_ACCORD, + DARK_MANUSCRIPT_13515, + BYRNES_CORONATION_SPEECH, + DARK_MANUSCRIPT_13517, + DARK_MANUSCRIPT_13518, + TRANSPORTATION_INCANTATIONS + ), + Arrays.asList( + RICKTORS_DIARY_7, + VARLAMORE_ENVOY, + DARK_MANUSCRIPT_13514, + EATHRAM_RADA_EXTRACT, + IDEOLOGY_OF_DARKNESS, + DARK_MANUSCRIPT_13516, + DARK_MANUSCRIPT_13521, + RADAS_CENSUS, + DARK_MANUSCRIPT_13515, + KILLING_OF_A_KING, + DARK_MANUSCRIPT_13520, + TREACHERY_OF_ROYALTY, + HOSIDIUS_LETTER, + DARK_MANUSCRIPT_13519, + BYRNES_CORONATION_SPEECH, + DARK_MANUSCRIPT_13517, + SOUL_JORUNEY, + DARK_MANUSCRIPT_13522, + WINTERTODT_PARABLE, + TWILL_ACCORD, + RADAS_JOURNEY, + TRANSVERGENCE_THEORY, + TRISTESSAS_TRAGEDY, + DARK_MANUSCRIPT_13523, + DARK_MANUSCRIPT_13518, + TRANSPORTATION_INCANTATIONS + ), + Arrays.asList( + RADAS_CENSUS, + DARK_MANUSCRIPT_13522, + RICKTORS_DIARY_7, + DARK_MANUSCRIPT_13514, + EATHRAM_RADA_EXTRACT, + DARK_MANUSCRIPT_13516, + KILLING_OF_A_KING, + DARK_MANUSCRIPT_13520, + HOSIDIUS_LETTER, + DARK_MANUSCRIPT_13519, + DARK_MANUSCRIPT_13521, + WINTERTODT_PARABLE, + TWILL_ACCORD, + DARK_MANUSCRIPT_13515, + BYRNES_CORONATION_SPEECH, + DARK_MANUSCRIPT_13517, + IDEOLOGY_OF_DARKNESS, + RADAS_JOURNEY, + TRANSVERGENCE_THEORY, + TRISTESSAS_TRAGEDY, + DARK_MANUSCRIPT_13523, + TREACHERY_OF_ROYALTY, + DARK_MANUSCRIPT_13518, + TRANSPORTATION_INCANTATIONS, + SOUL_JORUNEY, + VARLAMORE_ENVOY + ), + Arrays.asList( + RADAS_CENSUS, + TRANSVERGENCE_THEORY, + TREACHERY_OF_ROYALTY, + RADAS_JOURNEY, + KILLING_OF_A_KING, + DARK_MANUSCRIPT_13520, + VARLAMORE_ENVOY, + DARK_MANUSCRIPT_13522, + BYRNES_CORONATION_SPEECH, + DARK_MANUSCRIPT_13517, + HOSIDIUS_LETTER, + DARK_MANUSCRIPT_13516, + DARK_MANUSCRIPT_13519, + TRISTESSAS_TRAGEDY, + DARK_MANUSCRIPT_13523, + DARK_MANUSCRIPT_13521, + RICKTORS_DIARY_7, + DARK_MANUSCRIPT_13514, + IDEOLOGY_OF_DARKNESS, + WINTERTODT_PARABLE, + TWILL_ACCORD, + SOUL_JORUNEY, + DARK_MANUSCRIPT_13515, + EATHRAM_RADA_EXTRACT, + DARK_MANUSCRIPT_13518, + TRANSPORTATION_INCANTATIONS + ) + ); + + for (int i = 0; i < books.size(); i++) + { + assert new HashSet<>(books.get(i)).size() == books.get(i).size(); + books.set(i, Collections.unmodifiableList(books.get(i))); + } + return Collections.unmodifiableList(books); + } + + private void add(int x, int y, int z, int i) + { + // 'i' is added as a parameter for readability + Point3D p = new Point3D(x, y, z); + Bookcase b = byPoint.get(p); + if (b == null) + { + b = new Bookcase(p); + byPoint.put(p, b); + byLevel.computeIfAbsent(z, a -> new ArrayList<>()).add(b); + } + b.getIndex().add(i); + assert i == byIndex.size(); + byIndex.add(b); + } + + private void populateBooks() + { + add(1626, 3795, 0, 0); + add(1625, 3793, 0, 1); + add(1623, 3793, 0, 2); + add(1620, 3792, 0, 3); + add(1624, 3792, 0, 4); + add(1626, 3788, 0, 5); + add(1626, 3787, 0, 6); + add(1624, 3784, 0, 7); + add(1623, 3784, 0, 8); + add(1621, 3784, 0, 9); + add(1615, 3785, 0, 10); + add(1615, 3788, 0, 11); + add(1615, 3790, 0, 12); + add(1614, 3790, 0, 13); + add(1614, 3788, 0, 14); + add(1614, 3786, 0, 15); + add(1612, 3784, 0, 16); + add(1610, 3784, 0, 17); + add(1609, 3784, 0, 18); + add(1607, 3786, 0, 19); + add(1607, 3789, 0, 20); + add(1607, 3795, 0, 21); + add(1607, 3796, 0, 22); + add(1607, 3799, 0, 23); + add(1610, 3801, 0, 24); + add(1612, 3801, 0, 25); + add(1618, 3801, 0, 26); + add(1620, 3801, 0, 27); + add(1620, 3814, 0, 28); + add(1618, 3814, 0, 29); + add(1617, 3814, 0, 30); + add(1615, 3816, 0, 31); + add(1615, 3817, 0, 32); + add(1615, 3820, 0, 33); + add(1614, 3820, 0, 34); + add(1614, 3817, 0, 35); + add(1614, 3816, 0, 36); + add(1612, 3814, 0, 37); + add(1610, 3814, 0, 38); + add(1607, 3816, 0, 39); + add(1607, 3817, 0, 40); + add(1607, 3820, 0, 41); + add(1607, 3826, 0, 42); + add(1607, 3828, 0, 43); + add(1609, 3831, 0, 44); + add(1612, 3831, 0, 45); + add(1614, 3831, 0, 46); + add(1619, 3831, 0, 47); + add(1621, 3831, 0, 48); + add(1624, 3831, 0, 49); + add(1626, 3829, 0, 50); + add(1626, 3827, 0, 51); + add(1624, 3823, 0, 52); + add(1622, 3823, 0, 53); + add(1620, 3823, 0, 54); + add(1621, 3822, 0, 55); + add(1624, 3822, 0, 56); + add(1626, 3820, 0, 57); + add(1639, 3821, 0, 58); + add(1639, 3822, 0, 59); + add(1639, 3827, 0, 60); + add(1639, 3829, 0, 61); + add(1642, 3831, 0, 62); + add(1645, 3831, 0, 63); + add(1646, 3829, 0, 64); + add(1646, 3827, 0, 65); + add(1646, 3826, 0, 66); + add(1647, 3827, 0, 67); + add(1647, 3829, 0, 68); + add(1647, 3830, 0, 69); + add(1652, 3831, 0, 70); + add(1653, 3831, 0, 71); + add(1656, 3831, 0, 72); + add(1658, 3829, 0, 73); + add(1658, 3826, 0, 74); + add(1658, 3825, 0, 75); + add(1658, 3820, 0, 76); + add(1658, 3819, 0, 77); + add(1658, 3816, 0, 78); + add(1655, 3814, 0, 79); + add(1654, 3814, 0, 80); + add(1651, 3817, 0, 81); + add(1651, 3819, 0, 82); + add(1651, 3820, 0, 83); + add(1650, 3821, 0, 84); + add(1650, 3819, 0, 85); + add(1650, 3816, 0, 86); + add(1648, 3814, 0, 87); + add(1646, 3814, 0, 88); + add(1645, 3814, 0, 89); + add(1607, 3820, 1, 90); + add(1607, 3821, 1, 91); + add(1609, 3822, 1, 92); + add(1612, 3823, 1, 93); + add(1611, 3823, 1, 94); + add(1607, 3824, 1, 95); + add(1607, 3825, 1, 96); + add(1607, 3827, 1, 97); + add(1611, 3831, 1, 98); + add(1612, 3831, 1, 99); + add(1613, 3831, 1, 100); + add(1617, 3831, 1, 101); + add(1618, 3831, 1, 102); + add(1620, 3831, 1, 103); + add(1624, 3831, 1, 104); + add(1624, 3829, 1, 105); + add(1624, 3825, 1, 106); + add(1624, 3824, 1, 107); + add(1624, 3819, 1, 108); + add(1624, 3817, 1, 109); + add(1623, 3816, 1, 110); + add(1621, 3816, 1, 111); + add(1617, 3816, 1, 112); + add(1616, 3816, 1, 113); + add(1611, 3816, 1, 114); + add(1609, 3816, 1, 115); + add(1620, 3820, 1, 116); + add(1620, 3822, 1, 117); + add(1620, 3824, 1, 118); + add(1620, 3825, 1, 119); + add(1620, 3827, 1, 120); + add(1621, 3826, 1, 121); + add(1621, 3822, 1, 122); + add(1621, 3820, 1, 123); + add(1607, 3788, 1, 124); + add(1607, 3789, 1, 125); + add(1609, 3790, 1, 126); + add(1611, 3790, 1, 127); + add(1613, 3790, 1, 128); + add(1614, 3789, 1, 129); + add(1615, 3788, 1, 130); + add(1615, 3790, 1, 131); + add(1614, 3791, 1, 132); + add(1613, 3791, 1, 133); + add(1610, 3791, 1, 134); + add(1609, 3791, 1, 135); + add(1608, 3791, 1, 136); + add(1607, 3793, 1, 137); + add(1607, 3794, 1, 138); + add(1608, 3799, 1, 139); + add(1610, 3799, 1, 140); + add(1615, 3799, 1, 141); + add(1616, 3799, 1, 142); + add(1621, 3799, 1, 143); + add(1623, 3799, 1, 144); + add(1624, 3798, 1, 145); + add(1624, 3796, 1, 146); + add(1624, 3792, 1, 147); + add(1624, 3791, 1, 148); + add(1623, 3789, 1, 149); + add(1621, 3789, 1, 150); + add(1620, 3788, 1, 151); + add(1621, 3788, 1, 152); + add(1624, 3787, 1, 153); + add(1624, 3786, 1, 154); + add(1619, 3784, 1, 155); + add(1618, 3784, 1, 156); + add(1616, 3784, 1, 157); + add(1612, 3784, 1, 158); + add(1611, 3784, 1, 159); + add(1625, 3801, 1, 160); + add(1625, 3802, 1, 161); + add(1625, 3803, 1, 162); + add(1625, 3804, 1, 163); + add(1625, 3806, 1, 164); + add(1625, 3807, 1, 165); + add(1625, 3808, 1, 166); + add(1625, 3809, 1, 167); + add(1625, 3811, 1, 168); + add(1625, 3812, 1, 169); + add(1625, 3813, 1, 170); + add(1625, 3814, 1, 171); + add(1626, 3815, 1, 172); + add(1627, 3815, 1, 173); + add(1631, 3815, 1, 174); + add(1632, 3815, 1, 175); + add(1633, 3815, 1, 176); + add(1634, 3815, 1, 177); + add(1638, 3815, 1, 178); + add(1639, 3815, 1, 179); + add(1640, 3814, 1, 180); + add(1640, 3813, 1, 181); + add(1640, 3803, 1, 182); + add(1640, 3802, 1, 183); + add(1640, 3801, 1, 184); + add(1639, 3800, 1, 185); + add(1638, 3800, 1, 186); + add(1634, 3800, 1, 187); + add(1633, 3800, 1, 188); + add(1632, 3800, 1, 189); + add(1631, 3800, 1, 190); + add(1627, 3800, 1, 191); + add(1626, 3800, 1, 192); + add(1641, 3817, 1, 193); + add(1641, 3818, 1, 194); + add(1641, 3819, 1, 195); + add(1641, 3824, 1, 196); + add(1641, 3825, 1, 197); + add(1641, 3829, 1, 198); + add(1645, 3831, 1, 199); + add(1646, 3831, 1, 200); + add(1647, 3831, 1, 201); + add(1648, 3831, 1, 202); + add(1649, 3830, 1, 203); + add(1649, 3828, 1, 204); + add(1650, 3829, 1, 205); + add(1652, 3831, 1, 206); + add(1653, 3831, 1, 207); + add(1658, 3827, 1, 208); + add(1658, 3826, 1, 209); + add(1658, 3823, 1, 210); + add(1658, 3822, 1, 211); + add(1658, 3821, 1, 212); + add(1658, 3820, 1, 213); + add(1656, 3816, 1, 214); + add(1655, 3816, 1, 215); + add(1651, 3816, 1, 216); + add(1649, 3816, 1, 217); + add(1648, 3816, 1, 218); + add(1644, 3816, 1, 219); + add(1643, 3816, 1, 220); + add(1607, 3785, 2, 221); + add(1607, 3786, 2, 222); + add(1607, 3796, 2, 223); + add(1607, 3797, 2, 224); + add(1608, 3799, 2, 225); + add(1610, 3799, 2, 226); + add(1611, 3799, 2, 227); + add(1618, 3799, 2, 228); + add(1621, 3799, 2, 229); + add(1624, 3797, 2, 230); + add(1624, 3795, 2, 231); + add(1624, 3794, 2, 232); + add(1624, 3792, 2, 233); + add(1623, 3791, 2, 234); + add(1622, 3791, 2, 235); + add(1618, 3792, 2, 236); + add(1618, 3793, 2, 237); + add(1618, 3794, 2, 238); + add(1617, 3793, 2, 239); + add(1617, 3792, 2, 240); + add(1618, 3790, 2, 241); + add(1620, 3790, 2, 242); + add(1622, 3790, 2, 243); + add(1624, 3789, 2, 244); + add(1624, 3788, 2, 245); + add(1624, 3786, 2, 246); + add(1624, 3785, 2, 247); + add(1623, 3784, 2, 248); + add(1621, 3784, 2, 249); + add(1611, 3784, 2, 250); + add(1609, 3784, 2, 251); + add(1612, 3789, 2, 252); + add(1612, 3791, 2, 253); + add(1612, 3794, 2, 254); + add(1613, 3793, 2, 255); + add(1613, 3792, 2, 256); + add(1613, 3791, 2, 257); + add(1617, 3791, 2, 258); + add(1617, 3793, 2, 259); + add(1618, 3794, 2, 260); + add(1618, 3792, 2, 261); + add(1619, 3791, 2, 262); + add(1623, 3791, 2, 263); + add(1623, 3790, 2, 264); + add(1622, 3790, 2, 265); + add(1619, 3790, 2, 266); + add(1611, 3816, 2, 267); + add(1610, 3816, 2, 268); + add(1609, 3816, 2, 269); + add(1607, 3817, 2, 270); + add(1607, 3819, 2, 271); + add(1607, 3829, 2, 272); + add(1608, 3831, 2, 273); + add(1610, 3831, 2, 274); + add(1611, 3831, 2, 275); + add(1622, 3831, 2, 276); + add(1623, 3831, 2, 277); + add(1624, 3829, 2, 278); + add(1624, 3828, 2, 279); + add(1624, 3821, 2, 280); + add(1624, 3819, 2, 281); + add(1622, 3816, 2, 282); + add(1620, 3816, 2, 283); + add(1618, 3816, 2, 284); + add(1615, 3821, 2, 285); + add(1617, 3821, 2, 286); + add(1619, 3822, 2, 287); + add(1619, 3824, 2, 288); + add(1618, 3826, 2, 289); + add(1617, 3826, 2, 290); + add(1615, 3827, 2, 291); + add(1616, 3827, 2, 292); + add(1618, 3827, 2, 293); + add(1620, 3826, 2, 294); + add(1620, 3824, 2, 295); + add(1620, 3822, 2, 296); + add(1620, 3821, 2, 297); + add(1619, 3820, 2, 298); + add(1617, 3820, 2, 299); + add(1615, 3820, 2, 300); + add(1641, 3818, 2, 301); + add(1641, 3820, 2, 302); + add(1641, 3821, 2, 303); + add(1641, 3829, 2, 304); + add(1643, 3831, 2, 305); + add(1644, 3831, 2, 306); + add(1654, 3831, 2, 307); + add(1656, 3831, 2, 308); + add(1658, 3830, 2, 309); + add(1658, 3828, 2, 310); + add(1658, 3818, 2, 311); + add(1658, 3817, 2, 312); + add(1656, 3816, 2, 313); + add(1655, 3816, 2, 314); + add(1652, 3816, 2, 315); + add(1648, 3817, 2, 316); + add(1648, 3819, 2, 317); + add(1648, 3821, 2, 318); + add(1649, 3823, 2, 319); + add(1650, 3823, 2, 320); + add(1652, 3823, 2, 321); + add(1654, 3822, 2, 322); + add(1654, 3820, 2, 323); + add(1655, 3820, 2, 324); + add(1655, 3821, 2, 325); + add(1655, 3823, 2, 326); + add(1653, 3824, 2, 327); + add(1652, 3824, 2, 328); + add(1649, 3824, 2, 329); + add(1648, 3824, 2, 330); + add(1647, 3822, 2, 331); + add(1647, 3820, 2, 332); + add(1647, 3818, 2, 333); + add(1645, 3816, 2, 334); + add(1644, 3816, 2, 335); + add(1625, 3802, 2, 336); + add(1625, 3804, 2, 337); + add(1625, 3811, 2, 338); + add(1625, 3812, 2, 339); + add(1627, 3815, 2, 340); + add(1628, 3815, 2, 341); + add(1635, 3815, 2, 342); + add(1637, 3815, 2, 343); + add(1638, 3815, 2, 344); + add(1640, 3813, 2, 345); + add(1640, 3811, 2, 346); + add(1640, 3810, 2, 347); + add(1638, 3800, 2, 348); + add(1632, 3800, 2, 349); + add(1630, 3800, 2, 350); + add(1629, 3800, 2, 351); + add(1627, 3800, 2, 352); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/LibraryCustomer.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/LibraryCustomer.java new file mode 100644 index 0000000000..b9d75289d3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/LibraryCustomer.java @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; + +import net.runelite.api.NpcID; + +public enum LibraryCustomer +{ + VILLIA(NpcID.VILLIA, "Villia"), + PROFESSOR_GRACKLEBONE(NpcID.PROFESSOR_GRACKLEBONE, "Prof. Gracklebone"), + Sam(NpcID.SAM_7049, "Sam"); + + @Getter + private final int id; + + @Getter + private final String name; + + private static final Map byId = buildIdMap(); + + LibraryCustomer(int id, String name) + { + this.id = id; + this.name = name; + } + + public static LibraryCustomer getById(int id) + { + return byId.get(id); + } + + private static Map buildIdMap() + { + Map byId = new HashMap<>(); + for (LibraryCustomer c : values()) + { + byId.put(c.id, c); + } + return byId; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/SolvedState.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/SolvedState.java new file mode 100644 index 0000000000..80747aafea --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/SolvedState.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2018 Abex + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR + * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package net.runelite.client.plugins.kourendlibrary; + +public enum SolvedState +{ + NO_DATA, + INCOMPLETE, + COMPLETE; +} diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13514.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13514.png new file mode 100644 index 0000000000000000000000000000000000000000..fd432311a54eeab7c20ec2756fb09f13cdfccb80 GIT binary patch literal 679 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10A}YWq#1%+0FfcY2$JG_a z*5pT5TzrWKn8n>r>`sfBPKyHR*@>}9#4?EvI56+Agu$$stmpUD;5I{ z683a)46!)9c2Z*aAp;)Q>1vYMD;F%<>i7Tudz1CD?%Td7zZY&VFLyRt_VT5oRNc?{ z2j(eU{JF(N)K8Pe&ble$(VR()bAlg>ks$fOY`8?4mUHhXv5w0#aAG%ALUvWe{rAtv#eY#$O zLvDYhYQRdP`( zkYX@0FtpG$Fw!+N4>2^gGBCC>G}kpSw=ytz;_7e!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!aKkx#1%+0Ffg{2Ck1+X zHWtT~XGhc(#x|EG7#kaxW`?y_C3n`O_BCYG~bp9zYIfzMESykY)m6R)$wo zA2$FQ>?NMQuI!JP1jSfIs;qlFfkK>R1&--JS`&;5&C6tf2Jw2jIEGl9K0E1PRf_^o zi#SJT$HBGhKK;wT#rZ|4?EAI%uVwa}nXlMcY4dO4oR^bd)^a?#C*sC8LA>ztSs%3> z#c?0@GU+*m&AQTUc<=O6-teSgZ-q$X4)2!Bmy{<7txU;Iu5gwXn!Ks>r^_)0f#!y# z0W+Nq9x-(YsxAw1(%5A)-IQO@=?FuTPSlkB+j6BFZgOiLW7zP1L-D!msZ9(YypC^F zZtVDTBW>PO8~+~_OoulK_BUoY9F_6VP-yEgl@8F}%y_Fqy5J212Sawqv}kd$fR)TE z1(RLYTCnP@eed#9jIr`h*0!Vjg`Sm`GkCC1ioIy!Vsci_!FYM5#U4J3?~nKGydC$m zJNws@zf1QqE}qV}H~7MfeZUY>Epd$~Nl7e8wMs5Z1yT$~28I^821dGu<{^frRtCma qhUU5k=2ivPsvQH#I0ef#1Bv;GI+ZBxvX!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!al$!#1%+0Ffdl-MwVuV z6{H5|B?lI#hX#6jPH4)i$&W72j_7O1s4I+ZuS)KzOEWe$ZY+*#E=}mHNo^}n0_was z>B?J>0VP3x!3+-1Zlr-YN#5=*3>~bp9zYIfzMESykY)j5CWhkPX;DB1dx@v7EBhlR zK`~a5D(fCkkh-!0$8;dA4aBMp6TD_f0}Yb#ba4!^IK6h#-Lk_9Jk1+#DoA+zsQh0( z*XQPmgoMF5oGq!O&$hjfPl+ZBsDeH<``>s#F z50`DzB0Fy`SkdV2F4*2)?(kMOn=#$>O5$EFt}jjA-QJcRmA(7F&wasWaNXObd4_P> zlnjPvaSs0sD_CdozhcjOAfkMsZk_@tz)D;rN>UO_QmvAUQh^kMk%6Iwu7Q!Rp?Qd* usg;4Tm7%$=fw`4|!4p@9TPPZG^HVa@DsgL=D)9pprwpF1elF{r5}E)-ZswE# literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13517.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13517.png new file mode 100644 index 0000000000000000000000000000000000000000..79ea1d2ba648f01f0f683b937392838599b02645 GIT binary patch literal 676 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10A~wJ$#1%+0FfcY2$JG_a zmS%>PXGc`!M%LsdRC=-lqS38$2re!jhWsMRi5f@Y;0Vd9-0v5?(c3H z9ps!D=a&-Y)mENVkQ&@xmE2jA+EbU-*N_1;H%?W3HpuvrAirP+hi5m^K%69RcNc~Z zR#^`qhcn;Jtr#Q-#B2=iI)~l@8SEvVzOL+#m;}XGMXIcOJVENp3LMjcv?>s*Gt4NP z{Sv5$*VDx@#NzbcNjHm{4R~B9w&;e(I`6X3`}zO=o2i~gH(KnT@i7~}D44)<_`?f< zx8Ye48OzPuRvhYlnyIYVn`qL#N{L}!e4I5?lz^TA`@MViFE%KeNFCzydHCa|_9WpK z4B|45{qf&BvKSt$dOv^Kf;TKI7Bl@EA5~r$ z>uHACJ>T@?*S&5k`uIaFF<|Y9C`SoneeHnV%Yqe7A6n!0YLeSTn}7@ly%SLz8C02P zyqI=4>YyA$-S24&*>_C)|NC?K=@V|h7W`p+ohsm`Z+LMoD3nTEBT7;dOH!?pi&B9U zgOP!ug|2~-uAzB|p{bRDv6Z2@u7SCgfx#13hg&Eba`RI%(<*Umm@4rD6p0L;u6{1- HoD!Mjx literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13518.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13518.png new file mode 100644 index 0000000000000000000000000000000000000000..e955d7ae82e803ada23ef360ee862c7247e7e88b GIT binary patch literal 635 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!Y{xl#1%+0Ffg{2C$(24 zch;o#)TK2R$JOLVmuE*5r-v4#23O@q))mG!mnM{Eh8Y_hx0IJRloV$tCX{AnO>S!o z^z`g&$e7TSHMu1lXpr23r@KMsl?3?(GdMiEkp|)cE&v(qC7!;n?2niP#aKnEtb05`>dFcn(}A=O5Nk3h{(8P1$oTB(;uvCa`s}6C zVTTQPST6j~6!+$1k^1ppKUL)NHLsvBof)D2&)n*kT>c{ec!GL=bxp_0|3bULsQsl>Ly~sa1SY-r_x9whVt*dT|j&++1&l6_754_yUcbP0l+XkKvn$!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10A~wJ$#1%+0Ffi5?##ZG< zmS;xO`t6l8o!kY6x^!?PP8F~%fscNc~Z zR#^`qhcn;Jtr$py{K&!}e>Y$akilN!>Fdh=h)Gb4Riw(g#}lNktiUlHNSgq$Izzx8 zuj@d@4^J1z5R21$CtZv>tiaFu;q&uCEfpT>N{h#_QTIb|BuJ=wX@7vwwpE2 zcvbR(3QpsL93CaJoSg*SnvOEB=`vO5wt8_;+rej|KxjjM`g#UKRwJY2s;as|t`j|l z=U>kbb}(ywcWvvXKkphR>4*Ao&9S>9$rbTGuyI4sk3X+O1G+m*8kstudD+i38HN=t9=1BQWWiEBhjN@7W>RdP`(kYX@0FtpG$Fw!+N4>2^gGBCC>G}kpS lw=ytz;_7e!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!YRNf#1%+0Fff*9N0erU z)#OJv7RR+$CHFOCw3R0{mnKx@Mh1F%8XFsT)};2-rPUS2PHxH0PE07x%4#SnZYeJZ zYW^W-(FHQ2B*-tA!Qt7BG!Q4r+uensgH_f8$l=U)b1MeY>_E)Quq&d-7{n>@^mS!_ z#3U%jDpFX@Bn?Xz98OVblJzX3_EKZ-D6gaKffWuXowIw5E)y@A= z>`DIqs(K5)f4^&|rLt6BiNB@BD2;uBn3&@p$(<4V>Y6KW?_<>ncV&FIYjfz6x7QX3 zNVy%`_v)Yud=-K*q3hcL_T4?ib= zZ<^Bi@h-!rKX0cl3i$a{=J?AkVeAi2o!{JWYuPu2Cwt>6Hw$*1*kaG=p3N|C_1fQ# z8%{2WTaYChxF9_H?4&q`ZSNF9;@8bR$hy_=P;)|ut?>Eo*e8J>!Y{O*OmjI{xcGVS z#q`FAU%qc+6Yo4Z@OOr0F7wOsAMEEm1%wx<7^VU}uUg_7QIe8al4_M)lnSI6j0_Ad zbPbGj4b4LgO|1-!tqjd|4a}_!44$|;+(OZio1c=IR*74~REZy;h+^<`^>bP0l+XkK D((>3W literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13521.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13521.png new file mode 100644 index 0000000000000000000000000000000000000000..f21f84d0d33c055cabad0042a5ed8076e3810142 GIT binary patch literal 607 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!Y05c#1%+0FfdMN%Ia&# z=&4I9P7f{33^O)1F3*k#^z`hkNv+C_Y_CeL$&YR>O{go3Z7hy!D^JQ!3;=2qF*^>j zfUzXVFPOpM*^M+1C&}C0g`tC0)&t1l%y)Au2GKyw&af$^@F0j$;_2(k{)kCXj8&w{ zy2leFUsm9l4x~+hSexOGC+lq>zzdfjBIMP@k_cr`z@D8i} zzZQQG{=)0}miNBK*?JbBr&UW_BT7;dOH!?pi&B9UgOP!ug|2~-uAzB|p{bRDv6Z2@ nu7SCgfx#13hg&Eba`RI%(<*Umm@4rD6hRE0u6{1-oD!M!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10A|${k#1%+0Ffaysde#-j zR^>*PXGavLhZdv;*W^bx7RNQ0CKM&Obe06xW_snvIcG%J85e!&b5&u)Ok7?Zr+T^Kr8Wj%l#&U`nw zVj#^1Vl&*`_-+=E!CvC&>&pI!Nl=Vcq{_O-6C_tw;Fu1ijeyvYVV13oERb==)5S5w z;`H5#C&dma@VH7(O6Uj{b^Z9?{>lMMK8MovqcIb-yW($zzsye8_o5IRn-tcSPij0k%TRwD&|1r1~d(MB}vpfISADmm) zX6>+)Yx(N5yHkMvQY~?fC`m~yNwrEYN(E93Mh1o!x&}tNhUOuLrd9^VRv@mqm4Sf} huYoa&hTQy=%(P0}8j=G#`hgl4JYD@<);T3K0RVda#SQ=f literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13523.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13523.png new file mode 100644 index 0000000000000000000000000000000000000000..8afc530245ebe8d80f5569ab264c8b6799f1b4f0 GIT binary patch literal 631 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10!Xv;Z#1%+0FfjJirL|Wj zch;mD8ymNkCzWSMROLq2%|88VodoyCwVs$fS}WzhDN3XE)M7oFs2|7lsa2Sq~tGGvCdv7)Ub#F$Y7u_1v>S z278I8uPgf_CP6V)kt*vRPmsE@0>^Y9tpUWk4ENX*_5vASJzX3_EKXmYbh_-Y0*?!` zQiJNl>6iZfUtZ?Ycb>Csa;Y`9*u2v(8uPdRU%^njMOozD?I|%2|5X>Am}ynmSFrv{ zr>pUmsM*Im46be}X$TelXVow0DB;2Ga`f*{CyPUMT-)Dr?h)f$b;y9xp!~nkL~Eto z+uKT0&rW&t&x_@hLrCC>Ha?DtvT_RUlY!f82#vN1R*iMO>9O&kK-|R4)3qdYWXi%n5;o z|E3u-{@`nWx4f0PbLQXQ3olP@T#&c_f2{qD3#MQDU$7rM%&VDi@b)J#08~p{BT7;d zOH!?pi&B9UgOP!ug|2~-uAzB|p{bRDu@#7GZe?H~#A{%Tq9HdwB{QuOw}#|^j((s9 N22WQ%mvv4FO#mvw+fo1k literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13524.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13524.png new file mode 100644 index 0000000000000000000000000000000000000000..a0c2a1085ad34ff4bbdaed820e7e3e2e648b87d6 GIT binary patch literal 783 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10Vsd~_h%1n0U|@7Gle09C za<-JWHIa6+QS`J^3UE>Naa5L-8&Lq$Qc)?6YZxR zl%lV$BpjY)C@I3FuP(;V$F8j^!o|s&8*7vvVUQB4pBSt+V^?@ql%cAe;FPVQ1@Wd` zEBsp)c-PK!pRgf#@}`iU)d5X&z53P#mQ8f2p5_L0je9^+706K~L4Lsu4$p3+fjCLt z?k)@+tg;?J4rjibTQQJk17a424EYJyfeiK%PhVH|M@)iZtRhv`J)R(SWd)AuKw2M& z)fp_`?&t>^RP|gGy!JwbyUT=B#t%Lx z_f;uObr#zf@I!gRCbr8)EDWsHFN41C+_=J#pS)E`FDnz+m8BdVDaN$Ri&HX&A54I z+GXv9ch3DjbpP&pv)OMCKc24rbJ43X(bFq#-@W|KvQu5b>W_Tv*-bsuQ-1H7)1>j5 zJ4ODC*!_Fzx;#7wPOn+<@b@1FnaMvT-*8@%xiL9M?2E2@{Yw6AM}#fY13CPF!Kzx~ z8c~vxSdwa$T$Bo=7>o=IEp!cxbPdfz3{9;JjIBUib1MS_AzlMx6b-rgDVb@NxHTjP SbO7Uvfx*+&&t;ucLK6TkhY>me literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13525.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13525.png new file mode 100644 index 0000000000000000000000000000000000000000..583c4d120082e30b4d28c7f376abf1dd8461de6d GIT binary patch literal 811 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10Vo`ukh%1n0U|_VcvoNtV zv39UFvoW)EwzYS&H#9eN^>KCfau$~qclUR9@Nj5b>|<>cc_Z>sMX>gO5gDIz8! zC@dHl6%-H=5R{^?tgalMWvFMQr>(E8siVoq&!?uP#>K-G91|R$86TYz9i9+AV^?@& zQly-c+?1`M3E7FEaiLu+{96`y*UogGupxNzrjVZ10Zntg`ql-OO?0WA<_2`!uX^pd zAUBo-`2{mLJiCzw;v{*yyD)UH%6b4focV5U#Xy=3h*=miK8C{J0a+Sqj=R?sOM=E2yY*9KRqs ztCeZW1K5 zhKQ(>f<;Wb)i`ud<$O?@FTL=jxr6xARI}Ip1v~dQ)NpzgM23F;C;G3tobffw($rA# z!#3t&JK9T|rj<_6k6pLq9qYRF>)3;t#JLr#9oL5)zH>f;tGwHKiKt2RU6x?Bm7$yO z<;-?{XYE)h`z-IQ;l-@V-S^&X3)3tM@7eCMBjnhVZ*}|YGWh!auU2@Q<(K_FCZ)`G z{P|;Vp?-&wU-o6(*&_A6eEF*lJA&dLd|zr(`Cnq|1!IQCYUecm%$eV@rRhIkl+zKB z3{NZ0n$s8KUkU%W7E5fgiSY)8v1*BHL`h0wNvc(HQ7VvPFfuT-&^0j9H8c+~G_^7? pwgPd@tqcr=cnyqEH00)|WTsW(){q>~0gOBb22WQ%mvv4FO#put4Rini literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13526.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13526.png new file mode 100644 index 0000000000000000000000000000000000000000..98389e896484a410322da4782925eeca61aa87f9 GIT binary patch literal 872 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10;&6aZh%1n0U|@_jw~a8d zOtN;!uyYADGS78zPqlGQu&|3Vvo3Y@$#?e5vUk%Im#XpzSiHz5*wD<_*tp(1I6&W| z#Xqdv-A_qGOioa^J2<*6AfnbQC?#3H$tToKRWmEourn~yOWVLl&q!5F++9oGMN>zL zU$Dr<%TY~BoQF@Ci)UV3`t+#e$>H&RAu+plgwKshn;Ma5DzCJ4OX#wsoQ3h3E0*~$ znD0Gvy8DK8!J9UQtX>%~cdpmEHGvZ+xJ;Yk26X+|%3C0>GnNGT1v5B2yO9RsBze2L zFm$lWdH^|``EG8-K$;DRSr{_pCtL?I*h@TpUD+Qo35v0b^4jdV4^mfF;Fu1i^?_KO z!Sd~nexO0wo-U3d7N^%<$_#Ec5MWC%<5hV$L0Q$7iEHcBEbXYRSF8U0uTNcYO~~z~ z#pAl#`JQIG4SW9UG$>oXD&;=l&N4$HGM0%Wwaj@-G}G~~SsV4s7IA-&HRoWm-Xi%m z#$D89BHNDlETxJ$LXRqIJH7}f={3~~PHttEWjL}gTj}DYH=Lh8qhFTE&cE0!-5lfgG(8Y&QJ_tv_7f+ygl4OZ22puVtoNGwVw+n z_J`_SIqb7bSmWe!XWi#PJKjxSacjmoJ*i+Oz1oK&_n8*o{Iki@HDKlBm4>2Ob*uPA z&aK~~+3bBJW8)sZ%@KE>7;0ZH{>rc8<5IJEN9?*9#TR!M>{Wdw+VuI#{`!dNPoI6v zymW6;kNfIUO-p^V+t1&6Cv0ul_*;IpPtn7@Y_qplN=pQWE_kL{+jQSPN>@gr!6(%* z_lM>EiB4~ylm`fF?Q-y4k^?$`5zD~f>FVdQ&MBb@ E08`s8Bme*a literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13527.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13527.png new file mode 100644 index 0000000000000000000000000000000000000000..cf652a98b504bae9ec995b9926c9df2210d261be GIT binary patch literal 854 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10;#h!Bh%1n0U|?)%wyh|) zXl_nuY=~=YurW3^Zf{F&Yfb9x2<+@kZEFjtuk)Tc-My*NXWA6Ei4$BVPYRznA*`jv zfAVD4zFz0}V&={*o;jmn@}%s( z-i(^+=qZzP>*`{sPs^V$A*-q~vZLLhr#r2*#Js#bqOH{)=mB4+%PJs$lmz(&GdMiE zkp|)G1mNK=omsE{-79QN4sUW0Xq$OtNAf1FkPh*n-4n_krf>cAfB&k; z4Z=BH%b&mgZD4JjK4D2cV}a`aOj9X^?fH(GCLwRT7G%36OD~u`E$eV9d&%D9$XBuA zejPiVzr?j`*Is}9`#XPs6Jw*ZkFe;GZMpZqw@ynGXgOD4p(B%R@~SaNIKifK-m=Y% zip6)|L@s_VZZX+^`TG3A7Xoo^It`U4f9v##H%~Y)zvk!RELn@sRgFshUTU-7^4%BU zaygw5wBcsLycKV^s4@KSGj`h+x#-mI`{F;m7>ngZQ#ut-PmRfYuNaqDp>cABV)j%% zM`z`;*I&P3F*Q+MX~NKBc>ePx6P_}eDifh7SwXv~wYP&jWxZY+E}Xf6iH}R}^hHGz zYYyvaA}j|=10SAKva6mE^KzHo>A;-@J6BZ1POp9Th}(4O`%UYYT@JsnBW8ib$#*9I ztu3}(((!Qm$J@I{Z{dE!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10;(UNlh%1n0U|?)%wyh|) z=urW3^?&}Td?h3A|jGQq!*JAce z^U0H4`+A+byBz1t@?5;gXZ6Z}O&dcN%(q;&)MoJ_>$%3H`lrt z?NMQuI!JP1jSfI zd2ROG2dOJ7a7+i%nn0|?FzN96>p=C(Oq_V|Z znuHGCef9tU^0e0CDZU@S)I8q9zuEY(632h02R?NdGff@dd~?V$O?le0Kr1R+dcimL zO?=B`1KQgRC+NEe9Dcsc^o_KS7?zKf3sUmI{xu-@TWhU5>kIKosN`QFR}i!O6BYsVt-DC zua0CreSbEq!EuvS9GB1h`tgM8LG{H=YG+Lf=bE$JIGT7=?edh;jrNUeMU;HZO_we! zxz4_`!~dk&oh7EKfx8|#wl7!^_+(%FHG#+@>-Xg|Xe{k}EYkWsl}AZi%2GyfpVi#B zr6OvIF4sIxu5D;+axN~>30iiikCl^gLvF~7&2#1V&hU}EX?8ZyvamR?(9P`cxu3#P zr*15>&kc`vdR)?Y*JIbkfA1e$2->Ys@Kc|?ME_V>>S7CEc&nDUMwFx^mZVxG7o`Fz z1|tJQ3ta;vT|@H_LsKgQV=EBX+{(Z}h}XavMMG|WN@iLmZVkx+9sNKJ44$rjF6*2U FngH0MQ`-Ol literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13529.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13529.png new file mode 100644 index 0000000000000000000000000000000000000000..738b4c3a3dd7cc05e80673b8267c6aa9290baddf GIT binary patch literal 802 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10Vsd~_h%1n0U||v}aR}klvEKutpdfhir0l-ljN+ou_O|4z%E;#C1fXk{{`hnT2*<)fq+}2Q$vG_KySd-EeqDJz4q&0{`Ir0kCqx;p84#3 z`LmkYzD*C}`5AnkD|RlESRm4AQQ6{f%A~>N+io_-G||Xu=ift8X0S> zAm}mE;JComnvEY=G*)t^$^4Q?;yW$4Vycdhrw7NgFvio24a=^z=zT0cH{XqM%{AY* zUKtskHQOrt?($qvx*)+?&ZV--_tPVh3)|cl-kIYTxG!z$Y{p%3ji-g27NyKqb>8>- z*S~d3?o4{f_o3}bD5qW2Ke=A-hHbZh3hnO6kWYUs&myUx?qDE$z~GTsd{mgp1)- zQ`rv+?RVR~YU!ip!kjO^=dKe>3AwCLWH&#OXZz0u4_AfF*;L(lo#FATKszJhovF({ z@1HMk`a=F!ZZOL$U`VT$xJHzuB$lLFB^RXvDF!10LknF4BV9xD5JOWd17j-?*WAj$ iK#14C7)3*FeoAIqC2kGL0UiB74Gf;HelF{r5}E+rnG(VP literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13530.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13530.png new file mode 100644 index 0000000000000000000000000000000000000000..058ccef5e1da8eff496b7e0258a5958cb71e0576 GIT binary patch literal 686 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10A}zot#1%+0Ffi&Uv8&6m z8mO|HYO)LSF~QYLH3jc`2{mL zJiCzw;v{*yyD)UH%6b4focV5U#Xy=3h*=mi%(GT>o*pysQido-|Z_F>EK|LdP#3Tq8h z+rIlLbH#VNB@-81sTVuo)+c3lppD^{fI~}_;;V@bmfJ2AXy2L2#1nkkBfo6&vGT4D(vfQ@=mrFawt_;)y`YCd0oS)`^P07aUB2r zO$UWZPXn;tro@;Fm!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10qA|cH#1%+0Fff+JndgQZ zbr(2xC-zPb_iiD{@W=)Sq1Dn&__==dIJ0ZQqh7kM{TB8eYST~u1`z8e|3^&R;Xd5yH-VlMQO5Yew=erf=h0ULq>#MQiydz zphbDAduo_XR+K%^Zf&V|IUpyL1o;IsI6S+N2I3@nySp%Su*!M>Ih^@!ZpA>F2_(VL z_frk%F9!A!PhVH|M@)iZtfIU&d+vkOl@&Oq18GelR$`cRc>Q&tdLvI4#}JFtQzt(x zYck+*X~tujy=?q@_Oe@UKFxIE_O_Xu z-dClqUcm71@07d`$1i_(YOuBo?wDT}zhJ79@y5?P7r)n^F8TbXRF87e=?BM87hY53 z3|eU-yX?#3!Y`ZI4oEKEI<08)%k`4(_s_etWariet9-tvEY~C&5OBe5Ve;>V++}k_ zBp8+kD5{s%%c*JJSRJ^+W@k*umXN5GA6i?Rj;AFpk6-X{&wF} zVCX65{%iiBb--X$Epd$~Nl7e8wMs5Z1yT$~28I^821dGu<{^frRtCmaAg;NUfq@XO gfia4P-29Zxv`X9>k^?&Wff^V*UHx3vIVCg!0AX?mL;wH) literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13532.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13532.png new file mode 100644 index 0000000000000000000000000000000000000000..516b12fb2a6f0c319d41a7e395f56da78cb9174e GIT binary patch literal 735 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10qAtKE#1%+0FfghrDk{mz zsVghXOH0d2NolF6Ns5b$iHIy%Fj-hoP)AdfpO24+i%VZy+t}FHNKen--`&(eKRDPs zFwoQ7&@eRAFDf$7(!?Y#F4V@%EFmF0B_%pDBR)SrW!}7rrKMT3W_C}V+CF)5TUBLV zUtdE*Lvee1HPGUNvpK$j>@NxO3ubV5b|VeMN%D4gVd!9$^#F1>^WEHvfix2kvoRdp zvB4S0U@!6Xb!C6VBq+uz%4@UdK1f|zfnz$5)&ycLhA1gjpdh2Vr;B5V#p$(|UI#TB z2(TuYU1xO)c3E7JnZ0)E_j)ID-$Q%PSR~gK?X%o0Jt_5n6+?n``kZ`8fu2ImtF}ys znDwtz@xIWQ;&f&@+ZMGzkyf5XI?LI5Ive`fRaV_qIB-iLLZNm23auOZjJ^+x;~guR z0vS*d3UlOo98zx zV9kU1GVS{vqRqZsop;_zSV?ipl*LT{L!!flf9{HzapBmBe8ZEQbNu}e2ligQuATlS zeO|Rwo14O=QrXD{=YxBf37SM}N@O`?RO`P#>n3-m?BDm#+(!e%9=+0S2c{ba)e_f; zl9a@fRIB8oR3OD*WMF8aYha{nXdYr{YGq(-1>%}p85juh8W^K!$jwj5OsmALAvvI< QAE<%B)78&qol`;+0Hk2?c>n+a literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13533.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13533.png new file mode 100644 index 0000000000000000000000000000000000000000..cf652a98b504bae9ec995b9926c9df2210d261be GIT binary patch literal 854 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10;#h!Bh%1n0U|?)%wyh|) zXl_nuY=~=YurW3^Zf{F&Yfb9x2<+@kZEFjtuk)Tc-My*NXWA6Ei4$BVPYRznA*`jv zfAVD4zFz0}V&={*o;jmn@}%s( z-i(^+=qZzP>*`{sPs^V$A*-q~vZLLhr#r2*#Js#bqOH{)=mB4+%PJs$lmz(&GdMiE zkp|)G1mNK=omsE{-79QN4sUW0Xq$OtNAf1FkPh*n-4n_krf>cAfB&k; z4Z=BH%b&mgZD4JjK4D2cV}a`aOj9X^?fH(GCLwRT7G%36OD~u`E$eV9d&%D9$XBuA zejPiVzr?j`*Is}9`#XPs6Jw*ZkFe;GZMpZqw@ynGXgOD4p(B%R@~SaNIKifK-m=Y% zip6)|L@s_VZZX+^`TG3A7Xoo^It`U4f9v##H%~Y)zvk!RELn@sRgFshUTU-7^4%BU zaygw5wBcsLycKV^s4@KSGj`h+x#-mI`{F;m7>ngZQ#ut-PmRfYuNaqDp>cABV)j%% zM`z`;*I&P3F*Q+MX~NKBc>ePx6P_}eDifh7SwXv~wYP&jWxZY+E}Xf6iH}R}^hHGz zYYyvaA}j|=10SAKva6mE^KzHo>A;-@J6BZ1POp9Th}(4O`%UYYT@JsnBW8ib$#*9I ztu3}(((!Qm$J@I{Z{dE!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+0817l2pPlzi}0g!2=r>CQ- zX=Bj zXJKJ!V&YYs<588hYrDR@w6w9Yv5lFTe@n4XQ=xZ#o>8b5Cp&v!XL&$dsY!&7VX%jo zh)76ZO>lRmtdVxa)TZ#s4Pg`O^aEU3xi}rHt)pkPMNMyUFV83~k#n}SjbGRkH?J#Z zZb#CxiHS@4-R$jCS4~M?F0+%#3pDHYt*YL;qHOolh=2ekIXQV#{XM%4wr$nlvPo~_I=%M88=4QUZ9K51 za`&>jqQc^gjI`L;n2=z9clZ5!4G$eKoN{tU|M9K8M>n^WmzU?{eKor)9>+r$k2sgEfWua5X4&OM?7@862M7NCR<_ zyxm>3vuavq28SEvVzOL+#m;}XGMR{%Z+y|*ED{xE)(!f|{ zP+>4VzY7?(j5j=8978NlPrdXud{%+TfkOQUUWO{JCpu>(c^|zX!?To ze@c1C)e-0tc zi$r%deb3z*_r7GC?c)iX1nvf26RB~G4t-y`ds%wGGA+Y9wb%H+RK5LoVABS}s984B zH{O12E4y!gdzYHwQiTRC=kg7PUvF{kUA*QI!!O<=&F%ee)y}uee77sw-m5)i$NTSD zS&n_J!8{+n{&~H9?cYv*zAT>Js?ZRpD7EXN$NTyFZR)@Ndi3G!?U&Y5>)x|FeR%lz z-T@Qe*vZG)J8G?F&O7Gqc;M%)ho6sLEWE$TD*EcD>FXyOS{ZRHIGJ+5`P%^#Dbtmb znlYReY>OwRp18Sr<^lN^x4+BVSI*eXf8opZI}QyGp6$KO`S)7vA`9yMd9d zTH+c}l9E`GYL#4+3Zxi}3=A!F4UBXR%|i@LtqhE!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10Vq<_$h%1n0U|{UbG^|U| zSx{{`x6-05-N4w`cv-#8;#%vhDEpO7HW?9iNg>v$VKy^L%<59DtCK8emzhs3GM!vt zvZlp$OP9l*2`-K4HZ7U9xiJn2ffh|gJ}o8wZRG)-m4V&W!QBOp`Ekxg2`;hz<`YX? z`ih)8ave&OT`LkSYV*9xGu=z$%!^{os~bp9zYIfzMESykY)l&F!cRY z1BL?wdx@v7EBhlRK`~ZQUYkAlLF&p19MgfcCJ-wzOgg;&I#9i|C;DLr9ZXoR>(~(FrU5|35yf&{F+|i2wWR&lA(i&MnCN&vk&eZaKeH!VKjN zY`nL3D(*P<_kqNNKJ{&k*JTT0&(FT0)@-IV|JS=4%Bl|%AK2K~X|kQUV4*!PeOBAU z70S*nZZgL=ovE>7TdjT|X{OEkYn#uOIiE}U$0YtE>+3G>@4t05vlyQk?Jk`aWd7sy zWU~{0o%b_*REa#Lv*}KKbvTzwi?~sw;;F~({tkMXu8MVk-a2Tvrk-A04j$FwFNAZkx05rHTFWb*}enm1!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+0817l}^Plzi}0g&nK9qj7r z6A%#L=^5zk>}6}~Y++%StrnM|8k4FV85|tr=NIbk?(g8>QKFSrsF{+do|vQ*7Oxai zp`9767#Jb%=V{LDYQpSb$ZVs>93Gw!7!=j0S6HW$H*-dTsx*VKv2k>CN@!?Yn?YHN zeo3gDw~-n%9~VQ9QB9{|Wst0gln6t7d}d^1(qxmy2_|($f;uzI+NPSe_)EL;FmvfE zGbbhG#>S@2wdkI0-jS5(VXnoTmR6XMn7!C~!UD_QYVDjgw$oSIOj%|#IV-C)B_)5I z-K>m^;%$x#w>Zq(Xg^1vUo$VSDkrCWkIS;1&WrO4Y7V-t+V8TWvop25E%~I!#^dhm zkGQQ(^AuvbL01LNTK_x~)Qs3-~Y3ubV5b|VeMN%D4gVd!9$ z^#F1>^WEHvfix2sFTGOg4rH*Gc>21sKVlLTV-@AK*>fMHuB^Z@9Y_O{0D}sH>G@s2 zbinw*)5S5w;`G!@zvC|jNU(j74eD+@CefoI%aV62%pze@ualber5OfILAx6&RCU+B zse5n#e$K}3ABwA=U;qBQ@?7<{=EVPk4F8P2$5>2n*!Z!td-{R-y_|b2-Z3v0{NV2w z%O~LIazbE35t~oT;f@Nyg-YRz!q@L)@;P6z>_vo3s)Gi{y5<&J|8sqn64QFE4>}iL zlwPT5aPlM1;Y-bx0_w`<`Hd~C4psN;l~aslOmupE4tNJec5NzeNHCi&?3$vG_%cG_ zMf8cu+t(@|cr^EC=E{HuHvu1~_U4dp?`*T#17f_?y;URK*sd1X?CsHiGFR52&@eVk zq%=X`VMNoZNs-lYn(K5zS3ljfNU51a+?lnqy7|;!(Y0ZhbCta<=Pd|x_H*1DXxOtX zO|oK&&anrg&*n6-e|1}Z*KF2_7gj1l{w`s2c?~XP%q;9t;t@Sy-D6*~W!pZ+*32xS z&p%EEf13GwruIKS^L;mO-@13xH79@n|33<%CwKo}IWI(X>xH*6`z&fIOa8q2I(zc= z_cib5GV#>>xRLcLG}1zVUzKT-~E5ef=jYGL zy}AEcT7OT0;K6{<0$?IgEpd$~Nl7e8wMs5Z1yT$~28I^821dGu<{^frRtCmaAg;NU kfq@XOfia4P-29Zxv`X9>k^?&Wff^V*UHx3vIVCg!0P0G~V*mgE literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13537.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13537.png new file mode 100644 index 0000000000000000000000000000000000000000..b0a0f6d8905f4f9c63ab8b3465b7f2043f2ef9a5 GIT binary patch literal 1171 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+0817l%;Plzi}0g!2=r>CQ- zX=ZVPR=vQXY|A8kV_hyMCCxpS-lRv9Ym@nOR+IVNG;?pp7RdJ9~3NX=7Y*Rb;Ne zrMsAjNM~wIdvaA>xv4CGcwX*V`D;s{oUR7?=?Jh!0^zr?FScc-9LZR-nkoF%FD}h za&nWC62imn^(+ECJ$+nVC$+X5S-x}iO6_%PbdIguePH31-80s8)z((!1^057 z7|1X;GIlo_^fEk`;7y6?Vl0m6oa(e}W>{Fs{`h~t&)wjyKJn@I?Rnon*PO5ZCe#01 zi1Ej9nZx=j4hcKHbN#u?u(6?9ytYp0nKFj}x3`F>35P6`_H%|d{no>$%{pC@c^Y>z zT@aV^SLvP6;`%^q$s5ad2dl}RR*K5nJoDxs<4{fReXTOLQ0vSw3Co zZk^-!h$FymmhPrCdzp{SaL9~3p`1MF$S0+pifwzY2?ji~a9cHnCwWqZg@oq418Za* z^p5Yy+Nu`GHu0+O|F#!z5*l_bGSO-F&~x7Cx+~-S?4q4_vbN_2YjU>k_`ht2+AV35 z)SqF?7jKJNw$k&pjTVO`>)pJ~*RPu#H_38pnxlK;RoPd^^M-GC9SOdeyPU!F#pN4r zYo7~0$t^sp=TLs&-JQFV?_M<@yi&5{%iguWzc7C{H)pOs`S)VQpAQc%eh844v9+r! znpDbtK`_QSGh|Zra)1B%w)WK@KV5yY)n|Vpm(7Q7ck|+NSC)E8yjA}8FQZ7NXBX?S z>~{Wi_I6v}>~Qba{`tq0OT4!;s7^JScmKJ5e0x=Vs8(g&<>_-ZH}v$DnYen)pQHVQ zx%T(J$Lh?*&-7;fnHBv&X-9?K+mCzH@1NSI)|ds8zo@>NS*BT7;dOH!?pi&B9U zgOP!ug|2~-uAzB|p{bRDu@#7GZe?H~#A{%Tq9HdwB{QuOw}#|^j((s922WQ%mvv4F FO#o@;%Rm4C literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/19637.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/19637.png new file mode 100644 index 0000000000000000000000000000000000000000..d60bcbdcabf41ec51d54be3ec5303dc9c29ead72 GIT binary patch literal 947 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+081EXw!Plzi}0g&nA>gwU> z;B0GaXJMhQt*@i0G8?Y-}W}s4XR{DK2HCXQ-v7Wh$?vFD+-PZ>p}WZfpx_8#5a_RZTk!I|pkAM>Q>HTW1$d9XESd4+jr-EqyO%FE4EaA6FlL zcmF`oKqWaPA3dXB@8E>+@K8U$P`}XfoSgjBl$7Y`0DY5)fba>;O>LExH3j*_85zNb zW>JBWOQuho-P=34t*xuJwyCT%CO9T8G%g`LAt^E`B|2sO(k0V7J421k8ybpRTFSdR zYbQ)-nmoB}*39m?bNd%9nzDS^>?KR4Em$yl`t;78?z#vQ%c)b_SIwKZXzG+%-Q7`U z)@iY63nou)C@GFLw@t9H1BMIdJ||vK@RS7k1v5B2yO9RsBze2LFm$lWdVmz3IIs~9WS{BXg@CyyS? zbD3Dd8<=Vzk>kk4b^i5)Wn7sByh$39Qk-_F8EI%}Olp0hB;4d)(b_x1$UAr9Q&ZW+ z0WLZ-@7`mKwrC4<(kU&^muFe`c#7ceptZ6M3*K4rE~&hcC4X?iyIseP=lX5G{d(?~ z*~{Nbah$vJx%*&&$vhwZ_3P9h&%XGfj3HipXR+JkJ6Go4&EAzaYg%fS5jXSUO>dJn zhGqCBR^uMF(Z6|%S z8Yk_s1O~WjiEBhjN@7W>RdP`(kYX@0FtpG$Fw!+N4>2^gGBCCRam}p^41{gTe~DWM4f^ixKm literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/21756.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/21756.png new file mode 100644 index 0000000000000000000000000000000000000000..5021db2a7df60215c3996dbc3750da28ac12735f GIT binary patch literal 738 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE3?yBabR7dyEa{HEjtmSN`?>!lvVtU&J%W50 z7^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10qA|cH#1%+0FfiI!Gn<<; zJJ>TD88PeYF}u1jYiTm5Dl^QU-77E8;OW6ECB+~j!r77%jwojVWGGRhf zd3jDxcU@gwVOv`z(C*X6>XblEC<*clW^j0RBMrn!@^*J&=wOxg0CG6<-Q0?SG!qcB zF&x~n!5PS4FY)wsWq-sZD8?$vYqRG*NL^WhV>*!51Y#|QC@EE-Afvjci(`nz>9v<` z`W-e9V7<_Dz4P%kUPkALQ(+oE|I4Su+`cKz$!Y&gF7~X2URS*>!-l!W=eF~39NRJF zl@z0(gFx0^wh|#P#cAHmQIidhozPg7m3812%L^8Vr0w!77UHbkM_Rh>^6_~)1h?3P z1k7W+^3~&sX5KHRj8}0_LO1T&8=T4Z!Pj`@jx|wtAKQgC%*gI|*OmwQECPm| zYKdz^NlIc#s#S7PDv)9@GBC8zH89dOG!HQ}wK6ca0&&f)3=D*L4UADVFVdQ&MBb@0ID(fZU6uP literal 0 HcmV?d00001 diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/panel_icon.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/panel_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..cb569cd1be3e06850edfc205dcc4fb9eb4329963 GIT binary patch literal 2320 zcmbVO2~ZPP7>*PX@F?Er=yqv6I^FD%K*&l|OpwGOh!Bo?!De?uwkFxIyD6rzW2TF|Ni6svrA)R zqPhkS2$acWT@89&ocQi8-Tr>!bEG}0Nqhx3^GF zagw#m35qq-a*y39qGhu1(Hfy+qXAoZad9k7!ywQC5vFUv2+1IjY>Wl8EJp(& z@=%CGl^CE>%h6CojVZqXFa%Y>hzdp(5Q5@JD6UWgZ5Jq7<0uOrryJL1OZ?J+RzYy$ zFzj}_O*RnR26Ag>%6tB)3wAxsX zwJZ%8at78$Py-KUizp(Z9s!ZlAKg3F?`@m!c1HFPzNDD{8Qs znDL0hq-p+sr&}H62k()?)26VANx7?UMIRosVA|YcjaS!S$Y0rGO-U!fLr>njZuZM; zV)ziPx8bz!kGKGU^)_wrvSC2vn6 zWxdNs#+=_c50APwsCv#&gU_xfl|K|d+PvvvT}lT}aV?bl?0H`GqeC&na{_9QBZtk@ zV%b(t^d$N14HbQl1M9DbR}YCX3rIh*1PB;-JW6|NdC)fvu@gHgV|NTr-~V7ruTFoJ zz6{!w#hEv5&m7vR=cwj~10L)z8`iD2(|`1fX**90oToVV{DkfC=U0Y&_Trk#oHAN5 zvAA+Y&Fvl9%E+etJBL0g3ZLRxbEAJW*7OWMGv{eR5Vp0;pILze59s6FiGAuXuyeB3 zmOWp2b@HiR^*#LNXD_RmHhk99Z%Z~_9#>ShVE(1*{@Hgj-SFl6Wi`Ri9jht!R8dW- z;l!bp=-_-;{@(D~l(@xfhxjaV6V^J`=UG=>@3FPfJ#H3C|15^c7~S@ Date: Sun, 18 Feb 2018 23:47:26 -0700 Subject: [PATCH 3/4] Fix menu action clicked hook log --- .../src/main/java/net/runelite/client/callback/Hooks.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) 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 351a5377f7..9703887387 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 @@ -178,9 +178,6 @@ public class Hooks menuAction -= 2000; } - log.debug("Menu action clicked: {} ({}) on {} ({} widget: {})", - menuOption, menuAction, menuTarget.isEmpty() ? "" : menuTarget, id, actionParam, widgetId); - MenuOptionClicked menuOptionClicked = new MenuOptionClicked(); menuOptionClicked.setActionParam(actionParam); menuOptionClicked.setMenuOption(menuOption); @@ -189,6 +186,8 @@ public class Hooks menuOptionClicked.setId(id); menuOptionClicked.setWidgetId(widgetId); + log.debug("Menu action clicked: {}", menuOptionClicked); + eventBus.post(menuOptionClicked); } From 87e0d7a68fda2978f4aec35f629ef29caaae2f57 Mon Sep 17 00:00:00 2001 From: Max Weber Date: Mon, 19 Feb 2018 03:11:44 -0700 Subject: [PATCH 4/4] Merge HUNTER_LEVEL_UP* and DIALOG_LIBRARY_FIND* into DIALOG_SPRITE* --- .../java/net/runelite/api/widgets/WidgetID.java | 7 +++---- .../java/net/runelite/api/widgets/WidgetInfo.java | 9 +++------ .../kourendlibrary/KourendLibraryPlugin.java | 2 +- .../plugins/screenshot/ScreenshotPlugin.java | 6 +++--- .../plugins/screenshot/ScreenshotPluginTest.java | 14 +++++++------- 5 files changed, 17 insertions(+), 21 deletions(-) diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java index e559311608..a5d65ccdfb 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetID.java @@ -57,7 +57,7 @@ public class WidgetID public static final int BA_DEFENDER_GROUP_ID = 487; public static final int BA_HEALER_GROUP_ID = 488; public static final int LEVEL_UP_GROUP_ID = 233; - public static final int HUNTER_LEVEL_UP_GROUP_ID = 193; + public static final int DIALOG_SPRITE_GROUP_ID = 193; public static final int QUEST_COMPLETED_GROUP_ID = 277; public static final int CLUE_SCROLL_REWARD_GROUP_ID = 73; public static final int BARROWS_REWARD_GROUP_ID = 155; @@ -66,7 +66,6 @@ public class WidgetID public static final int PUZZLE_BOX_GROUP_ID = 306; public static final int NIGHTMARE_ZONE_GROUP_ID = 202; public static final int BLAST_FURNACE_GROUP_ID = 474; - public static final int LIBRARY_FIND_GROUP_ID = 193; static class WorldMap { @@ -289,9 +288,9 @@ public class WidgetID static final int VISIBLE_BOX = 4; } - static class DialogLibrary + static class DialogSprite { - static final int ITEM = 0; + static final int SPRITE = 0; static final int TEXT = 1; } } diff --git a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java index 4c1d6a3caa..96138f4c04 100644 --- a/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java +++ b/runelite-api/src/main/java/net/runelite/api/widgets/WidgetInfo.java @@ -120,8 +120,9 @@ public enum WidgetInfo COMBAT_SPELL_ICON(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.SPELL_ICON), COMBAT_SPELL_TEXT(WidgetID.COMBAT_GROUP_ID, WidgetID.Combat.SPELL_TEXT), - DIALOG_LIBRARY_FIND(WidgetID.LIBRARY_FIND_GROUP_ID, WidgetID.DialogLibrary.ITEM), - DIALOG_LIBRARY_FIND_TEXT(WidgetID.LIBRARY_FIND_GROUP_ID, WidgetID.DialogLibrary.TEXT), + DIALOG_SPRITE(WidgetID.DIALOG_SPRITE_GROUP_ID, 0), + DIALOG_SPRITE_SPRITE(WidgetID.DIALOG_SPRITE_GROUP_ID, WidgetID.DialogSprite.SPRITE), + DIALOG_SPRITE_TEXT(WidgetID.DIALOG_SPRITE_GROUP_ID, WidgetID.DialogSprite.TEXT), DIALOG_NPC_NAME(WidgetID.DIALOG_NPC_GROUP_ID, WidgetID.DialogNPC.NAME), DIALOG_NPC_TEXT(WidgetID.DIALOG_NPC_GROUP_ID, WidgetID.DialogNPC.TEXT), @@ -165,10 +166,6 @@ public enum WidgetInfo LEVEL_UP_SKILL(WidgetID.LEVEL_UP_GROUP_ID, WidgetID.LevelUp.SKILL), LEVEL_UP_LEVEL(WidgetID.LEVEL_UP_GROUP_ID, WidgetID.LevelUp.LEVEL), - HUNTER_LEVEL_UP(WidgetID.HUNTER_LEVEL_UP_GROUP_ID, 0), - HUNTER_LEVEL_UP_SKILL(WidgetID.HUNTER_LEVEL_UP_GROUP_ID, WidgetID.LevelUp.SKILL), - HUNTER_LEVEL_UP_LEVEL(WidgetID.HUNTER_LEVEL_UP_GROUP_ID, WidgetID.LevelUp.LEVEL), - QUEST_COMPLETED(WidgetID.QUEST_COMPLETED_GROUP_ID, 0), QUEST_COMPLETED_NAME_TEXT(WidgetID.QUEST_COMPLETED_GROUP_ID, WidgetID.QuestCompleted.NAME_TEXT), diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java index de7bb5265c..567c196f80 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/kourendlibrary/KourendLibraryPlugin.java @@ -143,7 +143,7 @@ public class KourendLibraryPlugin extends Plugin { if (lastBookcaseAnimatedOn != null) { - Widget find = client.getWidget(WidgetInfo.DIALOG_LIBRARY_FIND); + Widget find = client.getWidget(WidgetInfo.DIALOG_SPRITE_SPRITE); if (find != null) { Book book = Book.byId(find.getItemId()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java index 8f5bdac0fa..f7aeb6b8b2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/screenshot/ScreenshotPlugin.java @@ -61,7 +61,7 @@ import net.runelite.api.events.WidgetHiddenChanged; import net.runelite.api.widgets.Widget; import static net.runelite.api.widgets.WidgetID.BARROWS_REWARD_GROUP_ID; import static net.runelite.api.widgets.WidgetID.CLUE_SCROLL_REWARD_GROUP_ID; -import static net.runelite.api.widgets.WidgetID.HUNTER_LEVEL_UP_GROUP_ID; +import static net.runelite.api.widgets.WidgetID.DIALOG_SPRITE_GROUP_ID; import static net.runelite.api.widgets.WidgetID.LEVEL_UP_GROUP_ID; import static net.runelite.api.widgets.WidgetID.QUEST_COMPLETED_GROUP_ID; import net.runelite.api.widgets.WidgetInfo; @@ -253,9 +253,9 @@ public class ScreenshotPlugin extends Plugin fileName = parseLevelUpWidget(WidgetInfo.LEVEL_UP_SKILL, WidgetInfo.LEVEL_UP_LEVEL); break; } - case HUNTER_LEVEL_UP_GROUP_ID: + case DIALOG_SPRITE_GROUP_ID: { - fileName = parseLevelUpWidget(WidgetInfo.HUNTER_LEVEL_UP_SKILL, WidgetInfo.HUNTER_LEVEL_UP_LEVEL); + fileName = parseLevelUpWidget(WidgetInfo.DIALOG_SPRITE_SPRITE, WidgetInfo.DIALOG_SPRITE_TEXT); break; } case QUEST_COMPLETED_GROUP_ID: diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java index 8d4efc0e76..cac1a8f7af 100644 --- a/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java +++ b/runelite-client/src/test/java/net/runelite/client/plugins/screenshot/ScreenshotPluginTest.java @@ -35,10 +35,10 @@ import net.runelite.api.Client; import net.runelite.api.events.ChatMessage; import net.runelite.api.events.WidgetHiddenChanged; import net.runelite.api.widgets.Widget; -import static net.runelite.api.widgets.WidgetID.HUNTER_LEVEL_UP_GROUP_ID; +import static net.runelite.api.widgets.WidgetID.DIALOG_SPRITE_GROUP_ID; import static net.runelite.api.widgets.WidgetID.LEVEL_UP_GROUP_ID; -import static net.runelite.api.widgets.WidgetInfo.HUNTER_LEVEL_UP_LEVEL; -import static net.runelite.api.widgets.WidgetInfo.HUNTER_LEVEL_UP_SKILL; +import static net.runelite.api.widgets.WidgetInfo.DIALOG_SPRITE_SPRITE; +import static net.runelite.api.widgets.WidgetInfo.DIALOG_SPRITE_TEXT; import static net.runelite.api.widgets.WidgetInfo.LEVEL_UP_LEVEL; import static net.runelite.api.widgets.WidgetInfo.LEVEL_UP_SKILL; import static net.runelite.api.widgets.WidgetInfo.PACK; @@ -197,18 +197,18 @@ public class ScreenshotPluginTest public void testHunter() { Widget widget = mock(Widget.class); - when(widget.getId()).thenReturn(PACK(HUNTER_LEVEL_UP_GROUP_ID, 0)); + when(widget.getId()).thenReturn(PACK(DIALOG_SPRITE_GROUP_ID, 0)); Widget skillChild = mock(Widget.class); - when(client.getWidget(Matchers.eq(HUNTER_LEVEL_UP_SKILL))).thenReturn(skillChild); + when(client.getWidget(Matchers.eq(DIALOG_SPRITE_SPRITE))).thenReturn(skillChild); Widget levelChild = mock(Widget.class); - when(client.getWidget(Matchers.eq(HUNTER_LEVEL_UP_LEVEL))).thenReturn(levelChild); + when(client.getWidget(Matchers.eq(DIALOG_SPRITE_TEXT))).thenReturn(levelChild); when(skillChild.getText()).thenReturn("Congratulations, you just advanced a Hunter level."); when(levelChild.getText()).thenReturn("Your Hunter level is now 2."); - assertEquals("Hunter(2)", screenshotPlugin.parseLevelUpWidget(HUNTER_LEVEL_UP_SKILL, HUNTER_LEVEL_UP_LEVEL)); + assertEquals("Hunter(2)", screenshotPlugin.parseLevelUpWidget(DIALOG_SPRITE_SPRITE, DIALOG_SPRITE_TEXT)); WidgetHiddenChanged event = new WidgetHiddenChanged(); event.setWidget(widget);