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 0000000000..fd432311a5 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13514.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13515.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13515.png new file mode 100644 index 0000000000..65279b24b8 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13515.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13516.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13516.png new file mode 100644 index 0000000000..272c806e73 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13516.png differ 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 0000000000..79ea1d2ba6 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13517.png differ 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 0000000000..e955d7ae82 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13518.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13519.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13519.png new file mode 100644 index 0000000000..e2afb3a60e Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13519.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13520.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13520.png new file mode 100644 index 0000000000..5f60351a10 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13520.png differ 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 0000000000..f21f84d0d3 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13521.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13522.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13522.png new file mode 100644 index 0000000000..aecc61a062 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13522.png differ 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 0000000000..8afc530245 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13523.png differ 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 0000000000..a0c2a1085a Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13524.png differ 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 0000000000..583c4d1200 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13525.png differ 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 0000000000..98389e8964 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13526.png differ 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 0000000000..cf652a98b5 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13527.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13528.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13528.png new file mode 100644 index 0000000000..f5625c5ea8 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13528.png differ 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 0000000000..738b4c3a3d Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13529.png differ 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 0000000000..058ccef5e1 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13530.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13531.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13531.png new file mode 100644 index 0000000000..9d61342efa Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13531.png differ 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 0000000000..516b12fb2a Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13532.png differ 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 0000000000..cf652a98b5 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13533.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13534.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13534.png new file mode 100644 index 0000000000..40479929f3 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13534.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13535.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13535.png new file mode 100644 index 0000000000..e646c21655 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13535.png differ diff --git a/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13536.png b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13536.png new file mode 100644 index 0000000000..a72d1236f1 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13536.png differ 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 0000000000..b0a0f6d890 Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/13537.png differ 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 0000000000..d60bcbdcab Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/19637.png differ 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 0000000000..5021db2a7d Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/items/21756.png differ 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 0000000000..cb569cd1be Binary files /dev/null and b/runelite-client/src/main/resources/net/runelite/client/plugins/kourendlibrary/panel_icon.png differ