diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/PointsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/PointsPanel.java new file mode 100644 index 0000000000..0adacc78f2 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/PointsPanel.java @@ -0,0 +1,502 @@ +package net.runelite.client.plugins.newraids; + +import com.google.inject.Provides; +import java.awt.BorderLayout; +import java.awt.GridLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.TimeUnit; +import javax.inject.Inject; +import javax.swing.BoxLayout; +import javax.swing.ImageIcon; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.SwingUtilities; +import javax.swing.border.EmptyBorder; +import net.runelite.api.Client; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.game.SkillIconManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.FontManager; +import net.runelite.client.ui.PluginPanel; +import net.runelite.client.util.ImageUtil; + +public class PointsPanel extends PluginPanel +{ + + static final JPanel infoBoxPanel = new JPanel(); + public static ArrayList raids = new ArrayList(); + @Inject + private static RaidsPlugin raidsPlugin; + @Inject + private static Client client; + @Inject + private static SkillIconManager iconManager; + final JMenuItem pausePopup = new JMenuItem("Pause Timer"); + final JButton pauseButton = new JButton("Pause Timer"); + final JPanel overallInfo = new JPanel(); + private final Map infoBoxes = new HashMap<>(); + private final JLabel overallPointsGained = new JLabel(XpInfoBox.htmlLabel("Personal Points: ", 0)); + private final JLabel overallPointsPerHour = new JLabel(XpInfoBox.htmlLabel("Per Hour: ", 0)); + private final JLabel teamPointsGained = new JLabel(XpInfoBox.htmlLabel("Team Points: ", 0)); + private final JLabel teamPointsPerHour = new JLabel(XpInfoBox.htmlLabel("Per Hour: ", 0)); + private final JLabel raidsCompleted = new JLabel(XpInfoBox.htmlLabel("Raids Done: ", 0)); + private final JLabel averageTime = new JLabel(XpInfoBox.htmlLabel("Avg. Raid Time: ", "00:00:00")); + private final JLabel averagePoints = new JLabel(XpInfoBox.htmlLabel("Avg. Points: ", 0)); + private final JLabel averagePointsTeam = new JLabel(XpInfoBox.htmlLabel("Avg. Points: ", 0)); + private final JLabel elapsedTime = new JLabel(XpInfoBox.htmlLabel("Elapsed Time: ", "00:00:00")); + private final JLabel blankline1 = new JLabel("
"); + private final JLabel blankline = new JLabel("
"); + private final JLabel blankline2 = new JLabel("
"); + private final JPanel overallPanel = new JPanel(); + public Date finalTime = new Date(); + public Date tempTime = new Date(); + public boolean paused = false; + public boolean soloMode; + timerSpecial timer = new timerSpecial(); + ActionListener action = new ActionListener() + { + @Override + public void actionPerformed(ActionEvent event) + { + updateTime(); + } + }; + @Inject + private RaidsConfig raidsConfig; + + PointsPanel(RaidsPlugin raidsPlugin, RaidsConfig raidsConfig, Client client, SkillIconManager iconManager) + { + + super(); + setBorder(new EmptyBorder(6, 6, 6, 6)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + setLayout(new BorderLayout()); + + final JPanel layoutPanel = new JPanel(); + BoxLayout boxLayout = new BoxLayout(layoutPanel, BoxLayout.Y_AXIS); + layoutPanel.setLayout(boxLayout); + add(layoutPanel, BorderLayout.NORTH); + + overallPanel.setBorder(new EmptyBorder(10, 10, 10, 10)); + overallPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + overallPanel.setLayout(new BorderLayout()); + overallPanel.setVisible(true); // this will only become visible when the player gets exp + + // Create reset all menu + final JMenuItem reset = new JMenuItem("Reset Tracker"); + reset.addActionListener(e -> resetAll(true)); + + pausePopup.addActionListener(e -> pauseTime()); + pauseButton.addActionListener(e -> pauseTime()); + + + // Create popup menu + final JPopupMenu popupMenu = new JPopupMenu(); + popupMenu.setBorder(new EmptyBorder(5, 5, 5, 5)); + popupMenu.add(reset); + popupMenu.add(pausePopup); + overallPanel.setComponentPopupMenu(popupMenu); + final BufferedImage image = ImageUtil.getResourceStreamFromClass(this.getClass(), "cox.png"); + ImageIcon i = new ImageIcon(image); + final JLabel overallIcon = new JLabel(i); + + + overallInfo.setBackground(ColorScheme.DARKER_GRAY_COLOR); + overallInfo.setLayout(new GridLayout(12, 1)); + overallInfo.setBorder(new EmptyBorder(0, 10, 0, 0)); + pauseButton.setFont(FontManager.getRunescapeSmallFont()); + overallInfo.add(pauseButton); + overallPointsGained.setFont(FontManager.getRunescapeSmallFont()); + overallPointsPerHour.setFont(FontManager.getRunescapeSmallFont()); + teamPointsGained.setFont(FontManager.getRunescapeSmallFont()); + raidsCompleted.setFont(FontManager.getRunescapeSmallFont()); + averagePoints.setFont(FontManager.getRunescapeSmallFont()); + averageTime.setFont(FontManager.getRunescapeSmallFont()); + teamPointsPerHour.setFont(FontManager.getRunescapeSmallFont()); + blankline1.setFont(FontManager.getRunescapeSmallFont()); + blankline.setFont(FontManager.getRunescapeSmallFont()); + blankline2.setFont(FontManager.getRunescapeSmallFont()); + averagePointsTeam.setFont(FontManager.getRunescapeSmallFont()); + elapsedTime.setFont(FontManager.getRunescapeSmallFont()); + overallInfo.add(teamPointsGained); + overallInfo.add(teamPointsPerHour); + overallInfo.add(averagePointsTeam); + + overallInfo.add(blankline1); + overallInfo.add(overallPointsGained); + + overallInfo.add(overallPointsPerHour); + + overallInfo.add(averagePoints); + overallInfo.add(blankline2); + overallInfo.add(raidsCompleted); + + overallInfo.add(averageTime); + overallInfo.add(elapsedTime); + + overallPanel.add(overallIcon, BorderLayout.WEST); + overallPanel.add(overallInfo, BorderLayout.CENTER); + + + infoBoxPanel.setLayout(new BoxLayout(infoBoxPanel, BoxLayout.Y_AXIS)); + layoutPanel.add(overallPanel); + layoutPanel.add(infoBoxPanel); + + try + { + for (data d : raids) + { + infoBoxPanel.add(new XpInfoBox(raidsPlugin, raidsConfig, client, infoBoxPanel, d, iconManager)); + } + } + catch (IOException e) + { + + } + + + } + + /* + for(data d : raids){ + try { + infoBoxPanel.add(new XpInfoBox(raidsPlugin, raidsConfig, client, infoBoxPanel, Skill.AGILITY, iconManager)); + } catch (IOException e) { + e.printStackTrace(); + } + } + */ + public static long getDateDiff(Date date1, Date date2, TimeUnit timeUnit) + { + long diffInMillies = date2.getTime() - date1.getTime(); + return timeUnit.convert(diffInMillies, TimeUnit.MILLISECONDS); + } + + public static String formatSeconds(int timeInSeconds) + { + int hours = timeInSeconds / 3600; + int secondsLeft = timeInSeconds - hours * 3600; + int minutes = secondsLeft / 60; + int seconds = secondsLeft - minutes * 60; + + String formattedTime = ""; + if (hours < 10) + { + formattedTime += "0"; + } + formattedTime += hours + ":"; + + if (minutes < 10) + { + formattedTime += "0"; + } + formattedTime += minutes + ":"; + + if (seconds < 10) + { + formattedTime += "0"; + } + formattedTime += seconds; + + return formattedTime; + } + + @Provides + RaidsConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(RaidsConfig.class); + } + + XpInfoBox test(data d) + { + try + { + XpInfoBox x; + x = new XpInfoBox(raidsPlugin, raidsConfig, client, infoBoxPanel, d, iconManager); + return x; + } + catch (IOException e) + { + e.printStackTrace(); + } + return null; + } + + SoloBox testS(data d) + { + try + { + SoloBox x; + x = new SoloBox(raidsPlugin, raidsConfig, client, infoBoxPanel, d, iconManager); + return x; + } + catch (IOException e) + { + e.printStackTrace(); + } + return null; + } + + void init() + { + + } + + void update() + { + int totalPoints = 0; + int totalTeamPoints = 0; + float teamperhour = 0; + infoBoxPanel.removeAll(); + Date startTime = null; + Date finishTime = null; + int totalTime = 0; + float difference = 0; + int raidsDone = 0; + for (data d : raids) + { + SwingUtilities.invokeLater(() -> infoBoxPanel.add(test(d))); + totalPoints = totalPoints + d.personal; + totalTeamPoints = totalTeamPoints + d.total; + raidsDone++; + totalTime = totalTime + d.timeTaken; + } + if (timer.getElapsedTime() > 0) + { + difference = timer.getElapsedTime(); + teamperhour = (totalTeamPoints / difference) * 3600; + difference = (totalPoints / difference) * 3600; + } + + overallInfo.removeAll(); + overallInfo.setLayout(new GridLayout(12, 1)); + teamPointsPerHour.setVisible(true); + teamPointsGained.setVisible(true); + overallInfo.add(pauseButton); + overallInfo.add(teamPointsGained); + overallInfo.add(teamPointsPerHour); + overallInfo.add(averagePointsTeam); + + overallInfo.add(blankline1); + overallInfo.add(overallPointsGained); + + overallInfo.add(overallPointsPerHour); + + overallInfo.add(averagePoints); + overallInfo.add(blankline2); + overallInfo.add(raidsCompleted); + + overallInfo.add(averageTime); + overallInfo.add(elapsedTime); + overallPointsGained.setText(XpInfoBox.htmlLabel("Personal Points: ", totalPoints)); + overallPointsPerHour.setText(XpInfoBox.htmlLabel("Per Hour: ", (int) difference)); + teamPointsGained.setText(XpInfoBox.htmlLabel("Team Points: ", totalTeamPoints)); + teamPointsPerHour.setText(XpInfoBox.htmlLabel("Per Hour: ", (int) teamperhour)); + + raidsCompleted.setText(XpInfoBox.htmlLabel("Raids Done: ", raidsDone)); + if (raidsDone > 0) + { + elapsedTime.setText(XpInfoBox.htmlLabel("Elapsed Time: ", formatSeconds((int) timer.getElapsedTime()))); + averageTime.setText(XpInfoBox.htmlLabel("Avg. Raid Time: ", formatSeconds(totalTime / raidsDone))); + averagePoints.setText(XpInfoBox.htmlLabel("Avg. Points: ", totalPoints / raidsDone)); + averagePointsTeam.setText(XpInfoBox.htmlLabel("Avg. Points: ", totalTeamPoints / raidsDone)); + } + else + { + elapsedTime.setText(XpInfoBox.htmlLabel("Elapsed Time: ", "00:00:00")); + averageTime.setText(XpInfoBox.htmlLabel("Avg. Raid Time: ", "00:00:00")); + averagePoints.setText(XpInfoBox.htmlLabel("Avg. Points: ", 0)); + averagePointsTeam.setText(XpInfoBox.htmlLabel("Avg. Points: ", 0)); + } + + + infoBoxPanel.revalidate(); + } + + void updateSolo() + { + int totalPoints = 0; + int totalTeamPoints = 0; + float teamperhour = 0; + infoBoxPanel.removeAll(); + Date startTime = null; + Date finishTime = null; + int totalTime = 0; + float difference = 0; + int raidsDone = 0; + if (raids.size() > 0) + { + startTime = raids.get(0).start; + finishTime = raids.get(raids.size() - 1).finish; + difference = getDateDiff(startTime, finishTime, TimeUnit.SECONDS); + } + for (data d : raids) + { + SwingUtilities.invokeLater(() -> infoBoxPanel.add(testS(d))); + totalPoints = totalPoints + d.personal; + totalTeamPoints = totalTeamPoints + d.total; + raidsDone++; + totalTime = totalTime + d.timeTaken; + } + + if (timer.getElapsedTime() > 0) + { + difference = timer.getElapsedTime(); + teamperhour = (totalTeamPoints / difference) * 3600; + difference = (totalPoints / difference) * 3600; + } + overallInfo.removeAll(); + overallInfo.setLayout(new GridLayout(7, 1)); + overallInfo.add(pauseButton); + overallInfo.add(overallPointsGained); + overallInfo.add(overallPointsPerHour); + overallInfo.add(blankline); + overallInfo.add(raidsCompleted); + overallInfo.add(averageTime); + overallInfo.add(elapsedTime); + overallPointsGained.setText(XpInfoBox.htmlLabel("Personal Points: ", totalPoints)); + overallPointsPerHour.setText(XpInfoBox.htmlLabel("Per Hour: ", (int) difference)); + teamPointsGained.setText(XpInfoBox.htmlLabel("Team Points: ", totalTeamPoints)); + teamPointsPerHour.setText(XpInfoBox.htmlLabel("Per Hour: ", (int) teamperhour)); + + raidsCompleted.setText(XpInfoBox.htmlLabel("Raids Done: ", raidsDone)); + if (raidsDone > 0) + { + elapsedTime.setText(XpInfoBox.htmlLabel("Elapsed Time: ", formatSeconds((int) timer.getElapsedTime()))); + averageTime.setText(XpInfoBox.htmlLabel("Avg. Raid Time: ", formatSeconds(totalTime / raidsDone))); + averagePoints.setText(XpInfoBox.htmlLabel("Avg. Points: ", totalPoints / raidsDone)); + averagePointsTeam.setText(XpInfoBox.htmlLabel("Avg. Points: ", totalTeamPoints / raidsDone)); + } + else + { + elapsedTime.setText(XpInfoBox.htmlLabel("Elapsed Time: ", "00:00:00")); + averageTime.setText(XpInfoBox.htmlLabel("Avg. Raid Time: ", "00:00:00")); + averagePoints.setText(XpInfoBox.htmlLabel("Avg. Points: ", 0)); + averagePointsTeam.setText(XpInfoBox.htmlLabel("Avg. Points: ", 0)); + } + teamPointsPerHour.setVisible(false); + teamPointsGained.setVisible(false); + infoBoxPanel.revalidate(); + } + + void unpauseTime() + { + if (!timer.started) + { + return; + } + timer.unpause(); + pausePopup.setText("Pause Timer"); + pauseButton.setText("Pause Timer"); + for (ActionListener d : pausePopup.getActionListeners()) + { + pausePopup.removeActionListener(d); + } + for (ActionListener d : pauseButton.getActionListeners()) + { + pauseButton.removeActionListener(d); + } + pausePopup.addActionListener(e -> pauseTime()); + pauseButton.addActionListener(e -> pauseTime()); + } + + public void updateTime() + { + finalTime = new Date(); + int totalPoints = 0; + int totalTeamPoints = 0; + int raidsDone = 0; + int totalTime = 0; + if (raids.size() > 0) + { + int difference; + int teamperhour; + for (data d : raids) + { + totalPoints = totalPoints + d.personal; + totalTeamPoints = totalTeamPoints + d.total; + raidsDone++; + totalTime = totalTime + d.timeTaken; + } + if (timer.getElapsedTime() > 0) + { + difference = (int) timer.getElapsedTime(); + teamperhour = (int) ((totalTeamPoints / (float) difference) * 3600); + difference = (int) ((totalPoints / (float) difference) * 3600); + overallPointsGained.setText(XpInfoBox.htmlLabel("Personal Points: ", totalPoints)); + overallPointsPerHour.setText(XpInfoBox.htmlLabel("Per Hour: ", difference)); + teamPointsGained.setText(XpInfoBox.htmlLabel("Team Points: ", totalTeamPoints)); + teamPointsPerHour.setText(XpInfoBox.htmlLabel("Per Hour: ", teamperhour)); + } + + elapsedTime.setText(XpInfoBox.htmlLabel("Elapsed Time: ", formatSeconds((int) timer.getElapsedTime()))); + revalidate(); + } + + } + + void pauseTime() + { + + if (!timer.started) + { + return; + } + timer.pause(); + pausePopup.setText("Resume Timer"); + pauseButton.setText("Resume Timer"); + for (ActionListener d : pausePopup.getActionListeners()) + { + pausePopup.removeActionListener(d); + } + for (ActionListener d : pauseButton.getActionListeners()) + { + pauseButton.removeActionListener(d); + } + pausePopup.addActionListener(e -> unpauseTime()); + pauseButton.addActionListener(e -> unpauseTime()); + } + + void resetAll(boolean solo) + { + timer = new timerSpecial(); + pausePopup.setText("Pause Timer"); + pauseButton.setText("Pause Timer"); + for (ActionListener d : pausePopup.getActionListeners()) + { + pausePopup.removeActionListener(d); + } + for (ActionListener d : pauseButton.getActionListeners()) + { + pauseButton.removeActionListener(d); + } + pausePopup.addActionListener(e -> pauseTime()); + pauseButton.addActionListener(e -> pauseTime()); + raids.clear(); + if (soloMode) + { + updateSolo(); + } + else + { + update(); + } + + } + + void setSolo(boolean solo) + { + soloMode = solo; + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/Raid.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/Raid.java new file mode 100644 index 0000000000..cf644f0bc1 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/Raid.java @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids; + +import com.google.common.base.Joiner; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import lombok.Getter; +import net.runelite.client.plugins.newraids.solver.Layout; +import net.runelite.client.plugins.newraids.solver.Room; + +public class Raid +{ + @Getter + private final RaidRoom[] rooms = new RaidRoom[16]; + + @Getter + private Layout layout; + + public void updateLayout(Layout layout) + { + if (layout == null) + { + return; + } + + this.layout = layout; + + for (int i = 0; i < rooms.length; i++) + { + if (layout.getRoomAt(i) == null) + { + continue; + } + + RaidRoom room = rooms[i]; + + if (room == null) + { + RaidRoom.Type type = RaidRoom.Type.fromCode(layout.getRoomAt(i).getSymbol()); + room = new RaidRoom(null, type); + + if (type == RaidRoom.Type.COMBAT) + { + room.setBoss(RaidRoom.Boss.UNKNOWN); + } + + if (type == RaidRoom.Type.PUZZLE) + { + room.setPuzzle(RaidRoom.Puzzle.UNKNOWN); + } + + setRoom(room, i); + } + } + } + + public RaidRoom getRoom(int position) + { + return rooms[position]; + } + + public void setRoom(RaidRoom room, int position) + { + if (position < rooms.length) + { + rooms[position] = room; + } + } + + public RaidRoom[] getCombatRooms() + { + List combatRooms = new ArrayList<>(); + + for (Room room : layout.getRooms()) + { + if (room == null) + { + continue; + } + + if (rooms[room.getPosition()].getType() == RaidRoom.Type.COMBAT) + { + combatRooms.add(rooms[room.getPosition()]); + } + } + + return combatRooms.toArray(new RaidRoom[combatRooms.size()]); + } + + public String getRotationString() + { + return Joiner.on(",").join(Arrays.stream(getCombatRooms()).map(r -> r.getBoss().getName()).toArray()); + } + + public String toCode() + { + StringBuilder builder = new StringBuilder(); + + for (RaidRoom room : rooms) + { + if (room != null) + { + builder.append(room.getType().getCode()); + } + else + { + builder.append(" "); + } + } + + return builder.toString(); + } + + public String toRoomString() + { + final StringBuilder sb = new StringBuilder(); + + for (Room r : getLayout().getRooms()) + { + final int position = r.getPosition(); + final RaidRoom room = getRoom(position); + + if (room == null || !(room.getType() == RaidRoom.Type.COMBAT || room.getType() == RaidRoom.Type.PUZZLE)) + { + continue; + } + + switch (room.getType()) + { + case PUZZLE: + final RaidRoom.Puzzle puzzle = room.getPuzzle(); + sb.append(puzzle.getName()); + + if (puzzle == RaidRoom.Puzzle.UNKNOWN) + { + sb.append(" (puzzle)"); + } + + sb.append(", "); + break; + case COMBAT: + final RaidRoom.Boss boss = room.getBoss(); + sb.append(boss.getName()); + + if (boss == RaidRoom.Boss.UNKNOWN) + { + sb.append(" (combat)"); + } + + sb.append(", "); + break; + } + } + + final String roomsString = sb.toString(); + return roomsString.substring(0, roomsString.length() - 2); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidRoom.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidRoom.java new file mode 100644 index 0000000000..37367f7e8f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidRoom.java @@ -0,0 +1,161 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; +import net.runelite.api.Tile; + +public class RaidRoom +{ + public static final int ROOM_MAX_SIZE = 32; + @Getter + private final Tile base; + @Getter + @Setter + private Type type; + @Getter + @Setter + private Boss boss; + @Getter + @Setter + private Puzzle puzzle; + @Getter + @Setter + private RaidRoom previousRoom; + @Getter + @Setter + private RaidRoom nextRoom; + + public RaidRoom(Tile base, Type type) + { + this.base = base; + this.type = type; + } + + @Override + public String toString() + { + switch (type) + { + case COMBAT: + return "RaidRoom (type: " + type.getName() + ", " + boss.getName() + ")"; + + case PUZZLE: + return "RaidRoom (type: " + type.getName() + ", " + puzzle.getName() + ")"; + + default: + return "RaidRoom (type: " + type.getName() + ")"; + } + } + + @AllArgsConstructor + public enum Type + { + START("Start", "#"), + END("End", "¤"), + SCAVENGERS("Scavengers", "S"), + FARMING("Farming", "F"), + COMBAT("Combat", "C"), + PUZZLE("Puzzle", "P"), + EMPTY("Empty", " "); + + @Getter + private final String name; + + @Getter + private final String code; + + public static Type fromCode(char code) + { + for (Type type : Type.values()) + { + if (type.getCode().equalsIgnoreCase(String.valueOf(code))) + { + return type; + } + } + + return Type.EMPTY; + } + } + + @AllArgsConstructor + public enum Boss + { + TEKTON("Tekton"), + MUTTADILES("Muttadiles"), + GUARDIANS("Guardians"), + VESPULA("Vespula"), + SHAMANS("Shamans"), + VASA("Vasa"), + VANGUARDS("Vanguards"), + MYSTICS("Mystics"), + UNKNOWN("Unknown"); + + @Getter + private final String name; + + public static Boss fromString(String name) + { + for (Boss boss : Boss.values()) + { + if (boss.getName().equalsIgnoreCase(name)) + { + return boss; + } + } + + return null; + } + } + + @AllArgsConstructor + public enum Puzzle + { + CRABS("Crabs"), + ICE_DEMON("Ice Demon"), + TIGHTROPE("Tightrope"), + THIEVING("Thieving"), + UNKNOWN("Unknown"); + + @Getter + private final String name; + + public static Puzzle fromString(String name) + { + for (Puzzle puzzle : Puzzle.values()) + { + if (puzzle.getName().equalsIgnoreCase(name)) + { + return puzzle; + } + } + + return null; + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsConfig.java new file mode 100644 index 0000000000..57d1e648f3 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsConfig.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids; + +import net.runelite.client.config.Config; +import net.runelite.client.config.ConfigGroup; +import net.runelite.client.config.ConfigItem; + +@ConfigGroup("raids") +public interface RaidsConfig extends Config +{ + @ConfigItem( + position = 0, + keyName = "raidsTimer", + name = "Display elapsed raid time", + description = "Display elapsed raid time" + ) + default boolean raidsTimer() + { + return true; + } + + @ConfigItem( + position = 1, + keyName = "pointsMessage", + name = "Display points in chatbox after raid", + description = "Display a message with total points, individual points and percentage at the end of a raid" + ) + default boolean pointsMessage() + { + return true; + } + + @ConfigItem( + position = 2, + keyName = "scoutOverlay", + name = "Show scout overlay", + description = "Display an overlay that shows the current raid layout (when entering lobby)" + ) + default boolean scoutOverlay() + { + return true; + } + + @ConfigItem( + position = 3, + keyName = "scoutOverlayAtBank", + name = "Show scout overlay outside lobby", + description = "Keep the overlay active while at the raids area" + ) + default boolean scoutOverlayAtBank() + { + return true; + } + + @ConfigItem( + position = 4, + keyName = "scoutOverlayInRaid", + name = "Show scout overlay inside raid", + description = "Keep the overlay active while inside raid" + ) + default boolean scoutOverlayInRaid() + { + return false; + } + + @ConfigItem( + position = 5, + keyName = "whitelistedRooms", + name = "Whitelisted rooms", + description = "Display whitelisted rooms in green on the overlay. Separate with comma (full name)" + ) + default String whitelistedRooms() + { + return ""; + } + + @ConfigItem( + position = 6, + keyName = "blacklistedRooms", + name = "Blacklisted rooms", + description = "Display blacklisted rooms in red on the overlay. Separate with comma (full name)" + ) + default String blacklistedRooms() + { + return ""; + } + + @ConfigItem( + position = 7, + keyName = "enableRotationWhitelist", + name = "Enable rotation whitelist", + description = "Enable the rotation whitelist" + ) + default boolean enableRotationWhitelist() + { + return false; + } + + @ConfigItem( + position = 8, + keyName = "whitelistedRotations", + name = "Whitelisted rotations", + description = "Warn when boss rotation doesn't match a whitelisted one. Add rotations like [tekton, muttadile, guardians]" + ) + default String whitelistedRotations() + { + return ""; + } + + @ConfigItem( + position = 9, + keyName = "enableLayoutWhitelist", + name = "Enable layout whitelist", + description = "Enable the layout whitelist" + ) + default boolean enableLayoutWhitelist() + { + return false; + } + + @ConfigItem( + position = 10, + keyName = "whitelistedLayouts", + name = "Whitelisted layouts", + description = "Warn when layout doesn't match a whitelisted one. Add layouts like CFSCPPCSCF separated with comma" + ) + default String whitelistedLayouts() + { + return ""; + } + + @ConfigItem( + position = 12, + keyName = "ptsHr", + name = "Enable points per hour message", + description = "Enable the message" + ) + default boolean ptsHr() + { + return true; + } + + @ConfigItem( + position = 11, + keyName = "layoutMessage", + name = "Send raid layout message when entering raid", + description = "Sends game message with raid layout on entering new raid" + ) + default boolean layoutMessage() + { + return true; + } + + @ConfigItem( + position = 13, + keyName = "ptsPanelUpdate", + name = "Enable points panel", + description = "Enable the panel" + ) + default boolean ptsPanel() + { + return true; + } + + @ConfigItem( + position = 14, + keyName = "soloPanel", + name = "Solo Points Only", + description = "Display Only Solo Points Info" + ) + default boolean soloPanel() + { + return false; + } + + @ConfigItem( + position = 15, + keyName = "oldOverlay", + name = "Old Points Overlay", + description = "Use the old points overlay" + ) + default boolean oldOverlay() + { + return false; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsOverlay.java new file mode 100644 index 0000000000..9a02e5723c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsOverlay.java @@ -0,0 +1,162 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import lombok.Setter; +import net.runelite.api.Client; +import static net.runelite.api.MenuAction.RUNELITE_OVERLAY_CONFIG; +import net.runelite.client.plugins.newraids.solver.Room; +import net.runelite.client.ui.overlay.Overlay; +import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE; +import net.runelite.client.ui.overlay.OverlayMenuEntry; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; +import net.runelite.client.ui.overlay.components.TitleComponent; + +public class RaidsOverlay extends Overlay +{ + private static final int OLM_PLANE = 0; + private final PanelComponent panelComponent = new PanelComponent(); + private Client client; + private RaidsPlugin plugin; + private RaidsConfig config; + @Setter + private boolean scoutOverlayShown = false; + + @Inject + private RaidsOverlay(Client client, RaidsPlugin plugin, RaidsConfig config) + { + super(plugin); + setPosition(OverlayPosition.TOP_LEFT); + setPriority(OverlayPriority.LOW); + this.client = client; + this.plugin = plugin; + this.config = config; + getMenuEntries().add(new OverlayMenuEntry(RUNELITE_OVERLAY_CONFIG, OPTION_CONFIGURE, "Raids overlay")); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!config.scoutOverlay() || !scoutOverlayShown || plugin.isInRaidChambers() && client.getPlane() == OLM_PLANE) + { + return null; + } + + panelComponent.getChildren().clear(); + + if (plugin.getRaid() == null || plugin.getRaid().getLayout() == null) + { + panelComponent.getChildren().add(TitleComponent.builder() + .text("Unable to scout this raid!") + .color(Color.RED) + .build()); + + return panelComponent.render(graphics); + } + + Color color = Color.WHITE; + String layout = plugin.getRaid().getLayout().toCodeString(); + + if (config.enableLayoutWhitelist() && !plugin.getLayoutWhitelist().contains(layout.toLowerCase())) + { + color = Color.RED; + } + + panelComponent.getChildren().add(TitleComponent.builder() + .text(layout) + .color(color) + .build()); + + int bossMatches = 0; + int bossCount = 0; + + if (config.enableRotationWhitelist()) + { + bossMatches = plugin.getRotationMatches(); + } + + for (Room layoutRoom : plugin.getRaid().getLayout().getRooms()) + { + int position = layoutRoom.getPosition(); + RaidRoom room = plugin.getRaid().getRoom(position); + + if (room == null) + { + continue; + } + + color = Color.WHITE; + + switch (room.getType()) + { + case COMBAT: + bossCount++; + if (plugin.getRoomWhitelist().contains(room.getBoss().getName().toLowerCase())) + { + color = Color.GREEN; + } + else if (plugin.getRoomBlacklist().contains(room.getBoss().getName().toLowerCase()) + || config.enableRotationWhitelist() && bossCount > bossMatches) + { + color = Color.RED; + } + + panelComponent.getChildren().add(LineComponent.builder() + .left(room.getType().getName()) + .right(room.getBoss().getName()) + .rightColor(color) + .build()); + + break; + + case PUZZLE: + if (plugin.getRoomWhitelist().contains(room.getPuzzle().getName().toLowerCase())) + { + color = Color.GREEN; + } + else if (plugin.getRoomBlacklist().contains(room.getPuzzle().getName().toLowerCase())) + { + color = Color.RED; + } + + panelComponent.getChildren().add(LineComponent.builder() + .left(room.getType().getName()) + .right(room.getPuzzle().getName()) + .rightColor(color) + .build()); + break; + } + } + + return panelComponent.render(graphics); + } +} \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPanel.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPanel.java new file mode 100644 index 0000000000..0671a1f1d0 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPanel.java @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018, Lyzrds + * 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.newraids; + +import java.awt.BorderLayout; +import java.awt.FlowLayout; +import java.awt.event.ActionEvent; +import java.lang.reflect.Method; +import javax.inject.Inject; +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JLabel; +import javax.swing.JPanel; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.PluginPanel; + +public class RaidsPanel extends PluginPanel +{ + @Inject + private Client client; + @Inject + private RaidsPlugin raidsPlugin; + + @Inject + private ClientThread clientThread; + private JButton reloadButton = new JButton("Reload Instance"); + private JButton reloadScouter = new JButton("Reload Raid Overlay"); + private JLabel reloadMessage = new JLabel("

Instance Reload Helper

Reloading an instance will cause your client to disconnect temporarily.
"); + + void init(RaidsConfig config) + { + + // this may or may not qualify as a hack + // but this lets the editor pane expand to fill the whole parent panel + getParent().setLayout(new FlowLayout()); + getParent().add(this, BorderLayout.CENTER); + + setLayout(new BorderLayout()); + setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10)); + setBackground(ColorScheme.DARK_GRAY_COLOR); + + JPanel reloadContainer = new JPanel(); + JPanel scouterContainer = new JPanel(); + JPanel buttons = new JPanel(); + reloadContainer.setLayout(new BorderLayout()); + buttons.setLayout(new BorderLayout()); + buttons.setBackground(ColorScheme.DARKER_GRAY_COLOR); + reloadContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); + scouterContainer.setLayout(new BorderLayout()); + scouterContainer.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + JPanel reloadFrame = new JPanel(); + JPanel scoutFrame = new JPanel(); + reloadButton.addActionListener((ActionEvent e) -> + { + + + if ((client.getGameState() == GameState.LOGGED_IN)) + { + + try + { + //look for client.gameStateChanged(-1); in src files to find + Method m = client.getClass().getClassLoader().loadClass("jr").getDeclaredMethod("fn", int.class, int.class); + m.setAccessible(true); + m.invoke(null, 40, -1893789506); + + //Method m = client.getClass().getClassLoader().loadClass("jr").getDeclaredMethod("fn", int.class, int.class); + //m.setAccessible(true); + //m.invoke(null, 40, -1893789506); + raidsPlugin.canShow = false; + raidsPlugin.timerOverride = true; + //TODO: Since this is mainly for raids i'd like to reload the raids scouting plugin after the dc is finished + + } + catch (ReflectiveOperationException f) + { + throw new RuntimeException(f); + } + } + else + { + //TODO: User is still in a dc, or not logged in. Possibly provide a meaningful message somewhere. + } + }); + reloadScouter.addActionListener((ActionEvent e) -> + { + if ((client.getGameState() == GameState.LOGGED_IN)) + { + raidsPlugin.canShow = false; + raidsPlugin.checkRaidPresence(true); + } + }); + + reloadFrame.add(reloadButton); + scoutFrame.add(reloadScouter); + reloadContainer.add(reloadFrame, BorderLayout.NORTH); + reloadContainer.add(scoutFrame, BorderLayout.SOUTH); + add(reloadMessage, BorderLayout.PAGE_START); + add(reloadContainer, BorderLayout.CENTER); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPlugin.java new file mode 100644 index 0000000000..ee2e83af94 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPlugin.java @@ -0,0 +1,958 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids; + +import com.google.inject.Binder; +import com.google.inject.Provides; +import java.awt.image.BufferedImage; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLConnection; +import java.text.DecimalFormat; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Date; +import java.util.Vector; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import javax.inject.Inject; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.InstanceTemplates; +import net.runelite.api.NullObjectID; +import static net.runelite.api.Perspective.SCENE_SIZE; +import net.runelite.api.Point; +import static net.runelite.api.SpriteID.TAB_QUESTS_BROWN_RAIDING_PARTY; +import net.runelite.api.Tile; +import net.runelite.api.VarPlayer; +import net.runelite.api.Varbits; +import net.runelite.api.events.ChatMessage; +import net.runelite.api.events.ConfigChanged; +import net.runelite.api.events.VarbitChanged; +import net.runelite.api.events.WidgetHiddenChanged; +import net.runelite.api.widgets.Widget; +import net.runelite.api.widgets.WidgetInfo; +import net.runelite.client.callback.ClientThread; +import net.runelite.client.chat.ChatColorType; +import net.runelite.client.chat.ChatMessageBuilder; +import net.runelite.client.chat.ChatMessageManager; +import net.runelite.client.chat.QueuedMessage; +import net.runelite.client.config.ConfigManager; +import net.runelite.client.eventbus.Subscribe; +import net.runelite.client.game.SkillIconManager; +import net.runelite.client.game.SpriteManager; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.plugins.PluginDescriptor; +import net.runelite.client.plugins.PluginType; +import net.runelite.client.plugins.newraids.solver.Layout; +import net.runelite.client.plugins.newraids.solver.LayoutSolver; +import net.runelite.client.plugins.newraids.solver.RotationSolver; +import net.runelite.client.ui.ClientToolbar; +import net.runelite.client.ui.NavigationButton; +import net.runelite.client.ui.overlay.OverlayManager; +import net.runelite.client.ui.overlay.infobox.InfoBoxManager; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.Text; + +@PluginDescriptor( + name = "Raids Scouter", + description = "Show helpful information for the Chambers of Xeric raid", + tags = {"combat", "raid", "overlay", "pve", "pvm", "bosses"}, + type = PluginType.PVM +) +@Slf4j +public class RaidsPlugin extends Plugin +{ + static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###"); + private static final int LOBBY_PLANE = 3; + private static final String RAID_START_MESSAGE = "The raid has begun!"; + private static final String LEVEL_COMPLETE_MESSAGE = "level complete!"; + private static final String KC_MESSAGE = "Your completed Chambers of Xeric count is:"; + private static final String KC_MESSAGECM = "Your completed Chambers of Xeric Challenge Mode count is:"; + private static final String RAID_COMPLETE_MESSAGE = "Congratulations - your raid is complete!"; + private static final String RAID_COMPLETE_MESSAGE2 = "Congratulations - your raid is complete! Duration:"; + private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###.##"); + private static final String SPLIT_REGEX = "\\s*,\\s*"; + private static final Pattern ROTATION_REGEX = Pattern.compile("\\[(.*?)]"); + @Getter + private final ArrayList roomWhitelist = new ArrayList<>(); + @Getter + private final ArrayList roomBlacklist = new ArrayList<>(); + @Getter + private final ArrayList rotationWhitelist = new ArrayList<>(); + @Getter + private final ArrayList layoutWhitelist = new ArrayList<>(); + public boolean canShow; + public boolean timerOverride = false; + data raidToAdd = new data(); + int ticks = 0; + int raidSeconds; + int backupTime; + @Inject + private ChatMessageManager chatMessageManager; + @Inject + private InfoBoxManager infoBoxManager; + @Inject + private SkillIconManager iconManager; + @Inject + private Client client; + @Inject + private RaidsConfig config; + @Inject + private OverlayManager overlayManager; + @Inject + private RaidsOverlay overlay; + @Inject + private RaidsPointsOverlay pointsOverlay; + @Inject + private LayoutSolver layoutSolver; + @Inject + private SpriteManager spriteManager; + @Inject + private ClientThread clientThread; + @Getter + private Raid raid; + private RaidsPanel panel; + private PointsPanel pointsPanel; + private NavigationButton navButton; + private NavigationButton navButton2; + @Getter + private boolean inRaidChambers; + private RaidsTimer timer; + @Inject + private ClientToolbar clientToolbar; + + @Provides + RaidsConfig provideConfig(ConfigManager configManager) + { + return configManager.getConfig(RaidsConfig.class); + } + + @Override + public void configure(Binder binder) + { + binder.bind(RaidsOverlay.class); + } + + @Override + protected void startUp() throws Exception + { + overlayManager.add(overlay); + if (config.oldOverlay()) + { + overlayManager.add(pointsOverlay); + } + updateLists(); + clientThread.invokeLater(() -> checkRaidPresence(true)); + panel = injector.getInstance(RaidsPanel.class); + pointsPanel = new PointsPanel(this, config, client, iconManager); + pointsPanel.init(); + if (config.soloPanel()) + { + pointsPanel.setSolo(true); + pointsPanel.updateSolo(); + } + else + { + pointsPanel.setSolo(false); + pointsPanel.update(); + } + panel.init(config); + + final BufferedImage icon = ImageUtil.getResourceStreamFromClass(this.getClass(), "instancereloadhelper.png"); + final BufferedImage iconPoint = ImageUtil.getResourceStreamFromClass(this.getClass(), "cox.png"); + + navButton = NavigationButton.builder() + .tooltip("Raids Reload") + .icon(icon) + .priority(8) + .panel(panel) + .build(); + navButton2 = NavigationButton.builder() + .tooltip("CoX Points") + .icon(iconPoint) + .priority(9) + .panel(pointsPanel) + .build(); + clientToolbar.addNavigation(navButton); + if (config.ptsPanel()) + { + clientToolbar.addNavigation(navButton2); + } + } + + @Override + protected void shutDown() throws Exception + { + clientToolbar.removeNavigation(navButton); + clientToolbar.removeNavigation(navButton2); + overlayManager.remove(overlay); + infoBoxManager.removeInfoBox(timer); + overlayManager.remove(pointsOverlay); + inRaidChambers = false; + raid = null; + timer = null; + + final Widget widget = client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX); + if (widget != null) + { + widget.setHidden(false); + } + } + + @Subscribe + public void onConfigChanged(ConfigChanged event) + { + if (config.ptsPanel()) + { + if (config.soloPanel()) + { + pointsPanel.updateSolo(); + pointsPanel.setSolo(true); + } + else + { + pointsPanel.update(); + pointsPanel.setSolo(false); + } + pointsPanel.revalidate(); + } + if (!config.oldOverlay()) + { + overlayManager.remove(pointsOverlay); + } + if (!event.getGroup().equals("raids")) + { + return; + } + + if (event.getKey().equals("raidsTimer")) + { + updateInfoBoxState(); + return; + } + + updateLists(); + clientThread.invokeLater(() -> checkRaidPresence(true)); + } + + @Subscribe + public void onVarbitChanged(VarbitChanged event) + { + checkRaidPresence(false); + } + + + @Subscribe + public void onWidgetHiddenChanged(WidgetHiddenChanged event) + { + if (!inRaidChambers || event.isHidden()) + { + return; + } + + Widget widget = event.getWidget(); + + if (widget == client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX)) + { + + if (config.oldOverlay()) + { + widget.setHidden(true); + overlayManager.add(pointsOverlay); + } + else + { + widget.setHidden(false); + overlayManager.remove(pointsOverlay); + } + + } + } + + @Subscribe + public void onChatMessage(ChatMessage event) + { +/* + String t = Text.removeTags(event.getMessage()); + System.out.println(t); + if(t.equals("Test")){ + long timeTake = r(2000,5000); + data test = new data(); + test.add(r(50000,75000),r(10000,20000),r(20000,40000), (int) timeTake,2000,"TEST",new Date(),new Date()); + pointsPanel.raids.add(test); + if(config.soloPanel()){ + pointsPanel.updateSolo(); + }else{ + pointsPanel.update(); + } + if(!pointsPanel.timer.started){ + pointsPanel.timer.start(timeTake); + } + + } +*/ + String test = Text.removeTags(event.getMessage()); + if (raidToAdd != null) + { + raidToAdd.cm = false; + } + if (test.startsWith(KC_MESSAGE)) + { + PointsPanel.raids.get(PointsPanel.raids.size() - 1).kc = Integer.parseInt(test.replaceAll("\\D+", "")); + PointsPanel.raids.get(PointsPanel.raids.size() - 1).cm = false; + + if (config.soloPanel()) + { + pointsPanel.updateSolo(); + } + else + { + pointsPanel.update(); + } + + //raidToAdd.kc = Integer.parseInt(test.replaceAll("\\D+","")); + } + if (test.startsWith(KC_MESSAGECM)) + { + PointsPanel.raids.get(PointsPanel.raids.size() - 1).kc = Integer.parseInt(test.replaceAll("\\D+", "")); + PointsPanel.raids.get(PointsPanel.raids.size() - 1).cm = true; + + if (config.soloPanel()) + { + pointsPanel.updateSolo(); + } + else + { + pointsPanel.update(); + } + + //raidToAdd.kc = Integer.parseInt(test.replaceAll("\\D+","")); + } + if (inRaidChambers && event.getType() == ChatMessageType.FRIENDSCHATNOTIFICATION) + { + String message = Text.removeTags(event.getMessage()); + if (message.startsWith(RAID_START_MESSAGE)) + { + raidToAdd.start = new Date(); + } + if (message.startsWith(RAID_COMPLETE_MESSAGE)) + { + raidToAdd.finish = new Date(); + } + + if (config.raidsTimer() && message.startsWith(RAID_START_MESSAGE)) + { + + timer = new RaidsTimer(spriteManager.getSprite(TAB_QUESTS_BROWN_RAIDING_PARTY, 0), this, Instant.now()); + infoBoxManager.addInfoBox(timer); + } + + if (timer != null && message.contains(LEVEL_COMPLETE_MESSAGE)) + { + timer.timeFloor(); + } + + + if (message.startsWith(RAID_COMPLETE_MESSAGE)) + { + String stripped = message.replaceAll("[^\\d]", ""); + backupTime = 0; + int sSec = 0; + int sMin = 0; + int sHour = 0; + if (stripped.length() > 1) + { + sSec = Integer.parseInt(stripped.substring(stripped.length() - 2)); + stripped = stripped.substring(0, stripped.length() - 2); + } + if (stripped.length() > 1) + { + sMin = Integer.parseInt(stripped.substring(stripped.length() - 2)); + stripped = stripped.substring(0, stripped.length() - 2); + } + else if (stripped.length() > 0) + { + sMin = Integer.parseInt(stripped.substring(stripped.length() - 1)); + stripped = stripped.substring(0, stripped.length() - 1); + } + if (stripped.length() > 1) + { + sHour = Integer.parseInt(stripped.substring(stripped.length() - 2)); + stripped = stripped.substring(0, stripped.length() - 2); + } + else if (stripped.length() > 0) + { + sHour = Integer.parseInt(stripped.substring(stripped.length() - 1)); + stripped = stripped.substring(0, stripped.length() - 1); + } + backupTime = (sSec) + (sMin * 60) + (sHour * 3600); + raidToAdd.timeTaken = backupTime; + if (timer != null) + { + + timer.timeOlm(); + raidSeconds = backupTime; + timerOverride = false; + timer.setStopped(true); + } + raidToAdd.personal = client.getVar(Varbits.PERSONAL_POINTS); + raidToAdd.total = client.getVar(Varbits.TOTAL_POINTS); + raidToAdd.hr = (int) (((float) raidToAdd.personal / (float) raidToAdd.timeTaken) * 3600); + if (config.ptsPanel()) + { + PointsPanel.raids.add(raidToAdd); + if (!pointsPanel.timer.started) + { + pointsPanel.timer.start(raidToAdd.timeTaken); + } + if (config.soloPanel()) + { + pointsPanel.updateSolo(); + } + else + { + pointsPanel.update(); + } + raidToAdd = new data(); + } + if (config.pointsMessage()) + { + int totalPoints = client.getVar(Varbits.TOTAL_POINTS); + int personalPoints = client.getVar(Varbits.PERSONAL_POINTS); + + double percentage = personalPoints / (totalPoints / 100.0); + + String chatMessage = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Total points: ") + .append(ChatColorType.HIGHLIGHT) + .append(POINTS_FORMAT.format(totalPoints)) + .append(ChatColorType.NORMAL) + .append(", Personal points: ") + .append(ChatColorType.HIGHLIGHT) + .append(POINTS_FORMAT.format(personalPoints)) + .append(ChatColorType.NORMAL) + .append(" (") + .append(ChatColorType.HIGHLIGHT) + .append(DECIMAL_FORMAT.format(percentage)) + .append(ChatColorType.NORMAL) + .append("%)") + .build(); + + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.FRIENDSCHATNOTIFICATION) + .runeLiteFormattedMessage(chatMessage) + .build()); + if (config.ptsHr()) + { + String pts; + if (timer != null) + { + pts = POINTS_FORMAT.format(((float) personalPoints / (float) raidSeconds) * 3600); + } + else + { + pts = POINTS_FORMAT.format(((float) personalPoints / (float) backupTime) * 3600); + } + + + String chatMessage2 = new ChatMessageBuilder() + .append(ChatColorType.NORMAL) + .append("Pts/Hr: ") + .append(ChatColorType.HIGHLIGHT) + .append(pts) + .build(); + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.FRIENDSCHATNOTIFICATION) + .runeLiteFormattedMessage(chatMessage2) + .build()); + } + } + } + } + } + + public void checkRaidPresence(boolean force) + { + + if (client.getGameState() != GameState.LOGGED_IN) + { + return; + } + boolean setting = true; + if (!force) + { + setting = client.getVar(Varbits.IN_RAID) == 1; + } + else + { + + } + if (force || inRaidChambers != setting) + { + inRaidChambers = setting; + updateInfoBoxState(); + + if (inRaidChambers) + { + raid = buildRaid(); + + if (raid == null) + { + System.out.println("in cm"); + log.debug("Failed to build raid"); + return; + } + + Layout layout = layoutSolver.findLayout(raid.toCode()); + + if (layout == null) + { + log.debug("Could not find layout match"); + return; + } + + raid.updateLayout(layout); + RotationSolver.solve(raid.getCombatRooms()); + raidToAdd.raid = getRaid().toRoomString(); + sendRaidLayoutMessage(); + Vector puzzles = new Vector(); + puzzles.clear(); + for (RaidRoom r : raid.getRooms()) + { + if (r != null) + { + if (r.getPuzzle() != null) + { + if (r.getPuzzle().getName() != null) + { + puzzles.add(r.getPuzzle().getName()); + } + } + } + } + //Push puzzles to api + String urlString = "http://lyzrds.com/puzzles/api.php"; + boolean add = true; + if (puzzles.size() > 3) + { + add = false; + } + if (puzzles.size() == 0) + { + add = false; + } + switch (puzzles.size()) + { + case 1: + urlString = urlString + "?p1=" + puzzles.get(0); + break; + case 2: + urlString = urlString + "?p1=" + puzzles.get(0) + "&p2=" + puzzles.get(1); + break; + case 3: + urlString = urlString + "?p1=" + puzzles.get(0) + "&p2=" + puzzles.get(1) + "&p3=" + puzzles.get(2); + break; + + } + urlString = urlString + "&r=" + getRaid().getLayout().toCodeString() + ":" + getRaid().toRoomString(); + //System.out.println(urlString +"&r=" + getRaid().getLayout().toCodeString() +":"+ getRaid().toRoomString()); + if (add && canShow) + { + URL oracle = null; + try + { + oracle = new URL(urlString.replace(" ", "")); + } + catch (MalformedURLException e) + { + e.printStackTrace(); + } + URLConnection yc = null; + try + { + yc = oracle.openConnection(); + } + catch (IOException e) + { + e.printStackTrace(); + } + BufferedReader in = null; + try + { + in = new BufferedReader(new InputStreamReader( + yc.getInputStream())); + } + catch (IOException e) + { + e.printStackTrace(); + } + try + { + in.close(); + } + catch (IOException e) + { + e.printStackTrace(); + } + + } + canShow = true; + overlay.setScoutOverlayShown(true); + } + else if (!config.scoutOverlayAtBank()) + { + overlay.setScoutOverlayShown(false); + } + } + + // If we left party raid was started or we left raid + if (client.getVar(VarPlayer.IN_RAID_PARTY) == -1 && (!inRaidChambers || !config.scoutOverlayInRaid())) + { + overlay.setScoutOverlayShown(false); + canShow = true; + } + } + + private void sendRaidLayoutMessage() + { + if (!config.layoutMessage()) + { + return; + } + + final String layout = getRaid().getLayout().toCodeString(); + final String rooms = getRaid().toRoomString(); + final String raidData = "[" + layout + "]: " + rooms; + + chatMessageManager.queue(QueuedMessage.builder() + .type(ChatMessageType.FRIENDSCHATNOTIFICATION) + .runeLiteFormattedMessage(new ChatMessageBuilder() + .append(ChatColorType.HIGHLIGHT) + .append("Layout: ") + .append(ChatColorType.NORMAL) + .append(raidData) + .build()) + .build()); + } + + private void updateInfoBoxState() + { + if (timer == null) + { + return; + } + + if (inRaidChambers && config.raidsTimer()) + { + if (!infoBoxManager.getInfoBoxes().contains(timer)) + { + infoBoxManager.addInfoBox(timer); + } + } + else + { + infoBoxManager.removeInfoBox(timer); + } + + if (!inRaidChambers) + { + timer = null; + } + } + + private void updateLists() + { + updateList(roomWhitelist, config.whitelistedRooms()); + updateList(roomBlacklist, config.blacklistedRooms()); + updateList(rotationWhitelist, config.whitelistedRotations()); + updateList(layoutWhitelist, config.whitelistedLayouts()); + } + + private void updateList(ArrayList list, String input) + { + list.clear(); + + if (list == rotationWhitelist) + { + Matcher m = ROTATION_REGEX.matcher(input); + while (m.find()) + { + String rotation = m.group(1).toLowerCase(); + + if (!list.contains(rotation)) + { + list.add(rotation); + } + } + } + else + { + list.addAll(Arrays.asList(input.toLowerCase().split(SPLIT_REGEX))); + } + } + + int getRotationMatches() + { + String rotation = raid.getRotationString().toLowerCase(); + String[] bosses = rotation.split(SPLIT_REGEX); + + if (rotationWhitelist.contains(rotation)) + { + return bosses.length; + } + + for (String whitelisted : rotationWhitelist) + { + int matches = 0; + String[] whitelistedBosses = whitelisted.split(SPLIT_REGEX); + + for (int i = 0; i < whitelistedBosses.length; i++) + { + if (i < bosses.length && whitelistedBosses[i].equals(bosses[i])) + { + matches++; + } + else + { + matches = 0; + break; + } + } + + if (matches >= 2) + { + return matches; + } + } + + return 0; + } + + private Point findLobbyBase() + { + Tile[][] tiles = client.getScene().getTiles()[LOBBY_PLANE]; + + for (int x = 0; x < SCENE_SIZE; x++) + { + for (int y = 0; y < SCENE_SIZE; y++) + { + if (tiles[x][y] == null || tiles[x][y].getWallObject() == null) + { + continue; + } + + if (tiles[x][y].getWallObject().getId() == NullObjectID.NULL_12231) + { + return tiles[x][y].getSceneLocation(); + } + } + } + + return null; + } + + private Raid buildRaid() + { + Point gridBase = findLobbyBase(); + + if (gridBase == null) + { + return null; + } + + Raid raid = new Raid(); + Tile[][] tiles; + int position, x, y, offsetX; + int startX = -2; + + for (int plane = 3; plane > 1; plane--) + { + tiles = client.getScene().getTiles()[plane]; + + if (tiles[gridBase.getX() + RaidRoom.ROOM_MAX_SIZE][gridBase.getY()] == null) + { + position = 1; + } + else + { + position = 0; + } + + for (int i = 1; i > -2; i--) + { + y = gridBase.getY() + (i * RaidRoom.ROOM_MAX_SIZE); + + for (int j = startX; j < 4; j++) + { + x = gridBase.getX() + (j * RaidRoom.ROOM_MAX_SIZE); + offsetX = 0; + + if (x > SCENE_SIZE && position > 1 && position < 4) + { + position++; + } + + if (x < 0) + { + offsetX = Math.abs(x) + 1; //add 1 because the tile at x=0 will always be null + } + + if (x < SCENE_SIZE && y >= 0 && y < SCENE_SIZE) + { + if (tiles[x + offsetX][y] == null) + { + if (position == 4) + { + position++; + break; + } + + continue; + } + + if (position == 0 && startX != j) + { + startX = j; + } + + Tile base = tiles[offsetX > 0 ? 1 : x][y]; + RaidRoom room = determineRoom(base); + raid.setRoom(room, position + Math.abs((plane - 3) * 8)); + position++; + } + } + } + } + + return raid; + } + + private RaidRoom determineRoom(Tile base) + { + RaidRoom room = new RaidRoom(base, RaidRoom.Type.EMPTY); + int chunkData = client.getInstanceTemplateChunks()[base.getPlane()][(base.getSceneLocation().getX()) / 8][base.getSceneLocation().getY() / 8]; + InstanceTemplates template = InstanceTemplates.findMatch(chunkData); + + if (template == null) + { + return room; + } + + switch (template) + { + case RAIDS_LOBBY: + case RAIDS_START: + room.setType(RaidRoom.Type.START); + break; + + case RAIDS_END: + room.setType(RaidRoom.Type.END); + break; + + case RAIDS_SCAVENGERS: + case RAIDS_SCAVENGERS2: + room.setType(RaidRoom.Type.SCAVENGERS); + break; + + case RAIDS_SHAMANS: + room.setType(RaidRoom.Type.COMBAT); + room.setBoss(RaidRoom.Boss.SHAMANS); + break; + + case RAIDS_VASA: + room.setType(RaidRoom.Type.COMBAT); + room.setBoss(RaidRoom.Boss.VASA); + break; + + case RAIDS_VANGUARDS: + room.setType(RaidRoom.Type.COMBAT); + room.setBoss(RaidRoom.Boss.VANGUARDS); + break; + + case RAIDS_ICE_DEMON: + room.setType(RaidRoom.Type.PUZZLE); + room.setPuzzle(RaidRoom.Puzzle.ICE_DEMON); + break; + + case RAIDS_THIEVING: + room.setType(RaidRoom.Type.PUZZLE); + room.setPuzzle(RaidRoom.Puzzle.THIEVING); + break; + + case RAIDS_FARMING: + case RAIDS_FARMING2: + room.setType(RaidRoom.Type.FARMING); + break; + + case RAIDS_MUTTADILES: + room.setType(RaidRoom.Type.COMBAT); + room.setBoss(RaidRoom.Boss.MUTTADILES); + break; + + case RAIDS_MYSTICS: + room.setType(RaidRoom.Type.COMBAT); + room.setBoss(RaidRoom.Boss.MYSTICS); + break; + + case RAIDS_TEKTON: + room.setType(RaidRoom.Type.COMBAT); + room.setBoss(RaidRoom.Boss.TEKTON); + break; + + case RAIDS_TIGHTROPE: + room.setType(RaidRoom.Type.PUZZLE); + room.setPuzzle(RaidRoom.Puzzle.TIGHTROPE); + break; + + case RAIDS_GUARDIANS: + room.setType(RaidRoom.Type.COMBAT); + room.setBoss(RaidRoom.Boss.GUARDIANS); + break; + + case RAIDS_CRABS: + room.setType(RaidRoom.Type.PUZZLE); + room.setPuzzle(RaidRoom.Puzzle.CRABS); + break; + + case RAIDS_VESPULA: + room.setType(RaidRoom.Type.COMBAT); + room.setBoss(RaidRoom.Boss.VESPULA); + break; + } + + return room; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPointsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPointsOverlay.java new file mode 100644 index 0000000000..b3db041b28 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsPointsOverlay.java @@ -0,0 +1,87 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids; + +import java.awt.Dimension; +import java.awt.Graphics2D; +import javax.inject.Inject; +import net.runelite.api.Client; +import net.runelite.api.Varbits; +import static net.runelite.client.plugins.newraids.RaidsPlugin.POINTS_FORMAT; +import net.runelite.client.ui.overlay.Overlay; +import net.runelite.client.ui.overlay.OverlayPosition; +import net.runelite.client.ui.overlay.OverlayPriority; +import net.runelite.client.ui.overlay.components.LineComponent; +import net.runelite.client.ui.overlay.components.PanelComponent; + +public class RaidsPointsOverlay extends Overlay +{ + private final PanelComponent panel = new PanelComponent(); + @Inject + private Client client; + @Inject + private RaidsPlugin plugin; + + @Inject + public RaidsPointsOverlay() + { + setPosition(OverlayPosition.TOP_RIGHT); + setPriority(OverlayPriority.HIGH); + } + + @Override + public Dimension render(Graphics2D graphics) + { + if (!plugin.isInRaidChambers()) + { + return null; + } + + int totalPoints = client.getVar(Varbits.TOTAL_POINTS); + int personalPoints = client.getVar(Varbits.PERSONAL_POINTS); + int partySize = client.getVar(Varbits.RAID_PARTY_SIZE); + + panel.getChildren().clear(); + panel.getChildren().add(LineComponent.builder() + .left("Total:") + .right(POINTS_FORMAT.format(totalPoints)) + .build()); + + panel.getChildren().add(LineComponent.builder() + .left(client.getLocalPlayer().getName() + ":") + .right(POINTS_FORMAT.format(personalPoints)) + .build()); + + if (partySize > 1) + { + panel.getChildren().add(LineComponent.builder() + .left("Party size:") + .right(String.valueOf(partySize)) + .build()); + } + + return panel.render(graphics); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsTimer.java new file mode 100644 index 0000000000..a6db312c02 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/RaidsTimer.java @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids; + +import java.awt.Color; +import java.awt.image.BufferedImage; +import java.time.Duration; +import java.time.Instant; +import java.time.LocalTime; +import java.time.format.DateTimeFormatter; +import lombok.Setter; +import net.runelite.client.plugins.Plugin; +import net.runelite.client.ui.overlay.infobox.InfoBox; + +public class RaidsTimer extends InfoBox +{ + private final Instant startTime; + public LocalTime time; + private Instant floorTime; + private LocalTime firstFloorTime; + private LocalTime secondFloorTime; + private LocalTime thirdFloorTime; + private LocalTime olmTime; + + @Setter + private boolean stopped; + + public RaidsTimer(BufferedImage image, Plugin plugin, Instant startTime) + { + super(image, plugin); + this.startTime = startTime; + floorTime = startTime; + stopped = false; + } + + public void timeFloor() + { + Duration elapsed = Duration.between(floorTime, Instant.now()); + + if (firstFloorTime == null) + { + firstFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds()); + } + else if (secondFloorTime == null) + { + secondFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds()); + } + else if (thirdFloorTime == null) + { + + thirdFloorTime = LocalTime.ofSecondOfDay(elapsed.getSeconds()); + } + + floorTime = Instant.now(); + } + + + public int getSeconds() + { + return time.getSecond() + (time.getMinute() * 60) + (time.getHour() * 3600); + } + + + public void timeOlm() + { + Duration elapsed = Duration.between(floorTime, Instant.now()); + olmTime = LocalTime.ofSecondOfDay(elapsed.getSeconds()); + } + + @Override + public String getTooltip() + { + StringBuilder builder = new StringBuilder(); + builder.append("Elapsed raid time: "); + builder.append(time.format(DateTimeFormatter.ofPattern("HH:mm:ss"))); + + if (firstFloorTime != null) + { + builder.append("
First floor: "); + builder.append(firstFloorTime.format(DateTimeFormatter.ofPattern("mm:ss"))); + } + + if (secondFloorTime != null) + { + builder.append("
Second floor: "); + builder.append(secondFloorTime.format(DateTimeFormatter.ofPattern("mm:ss"))); + } + + if (thirdFloorTime != null) + { + + builder.append("
Third floor: "); + builder.append(thirdFloorTime.format(DateTimeFormatter.ofPattern("mm:ss"))); + } + + if (olmTime != null) + { + builder.append("
Olm: "); + builder.append(olmTime.format(DateTimeFormatter.ofPattern("mm:ss"))); + } + + return builder.toString(); + } + + @Override + public String getText() + { + if (startTime == null) + { + return ""; + } + + if (!stopped) + { + Duration elapsed = Duration.between(startTime, Instant.now()); + time = LocalTime.ofSecondOfDay(elapsed.getSeconds()); + } + + if (time.getHour() > 0) + { + return time.format(DateTimeFormatter.ofPattern("HH:mm")); + } + + return time.format(DateTimeFormatter.ofPattern("mm:ss")); + } + + @Override + public Color getTextColor() + { + if (stopped) + { + return Color.GREEN; + } + + return Color.WHITE; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/SoloBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/SoloBox.java new file mode 100644 index 0000000000..f6b7805782 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/SoloBox.java @@ -0,0 +1,209 @@ +/* + * Copyright (c) 2018, Adam + * Copyright (c) 2018, Psikoi + * 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.newraids; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.DecimalFormat; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.client.game.SkillIconManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.FontManager; +import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.ImageUtil; +import net.runelite.client.util.StackFormatter; + +class SoloBox extends JPanel +{ + static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###"); + private static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00"); + // Templates + private static final String HTML_TOOL_TIP_TEMPLATE = + "%s %s done
" + + "%s %s/hr
" + + "%s till goal lvl"; + private static final String HTML_LABEL_TEMPLATE = + "%s%s"; + + // Instance members + private final JPanel panel; + + @Getter(AccessLevel.PACKAGE) + private final data raid; + + /* The tracker's wrapping container */ + private final JPanel container = new JPanel(); + + /* Contains the skill icon and the stats panel */ + private final JPanel headerPanel = new JPanel(); + + /* Contains all the skill information (exp gained, per hour, etc) */ + private final JPanel statsPanel = new JPanel(); + + + private final JLabel personalPoints = new JLabel(); + private final JLabel hourlyPoints = new JLabel(); + private final JLabel totalPoints = new JLabel(); + private final JLabel raidTime = new JLabel(); + private final JLabel layout = new JLabel(); + private final JLabel teamHour = new JLabel(); + private final JLabel killCount = new JLabel(); + private final JMenuItem pauseSkill = new JMenuItem("Pause"); + + private RaidsConfig raidsConfig; + + private boolean paused = false; + + SoloBox(RaidsPlugin raidsPlugin, RaidsConfig raidsConfig, Client client, JPanel panel, data raid, SkillIconManager iconManager) throws IOException + { + this.raidsConfig = raidsConfig; + this.panel = panel; + this.raid = raid; + + setLayout(new BorderLayout()); + setBorder(new EmptyBorder(5, 0, 0, 0)); + + container.setLayout(new BorderLayout()); + container.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + + // Create reset others menu + //pauseSkill.addActionListener(e -> raidsPlugin.pauseSkill(skill, !paused)); + + BufferedImage image = ImageUtil.getResourceStreamFromClass(this.getClass(), "cox.png"); + ImageIcon i = new ImageIcon(image); + + JLabel skillIcon = new JLabel(i); + skillIcon.setHorizontalAlignment(SwingConstants.CENTER); + skillIcon.setVerticalAlignment(SwingConstants.CENTER); + skillIcon.setPreferredSize(new Dimension(35, 35)); + + headerPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + headerPanel.setLayout(new BorderLayout()); + + + statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + statsPanel.setBorder(new EmptyBorder(9, 2, 9, 2)); + statsPanel.setLayout(new DynamicGridLayout(2, 2)); + personalPoints.setFont(FontManager.getRunescapeSmallFont()); + personalPoints.setText("Personal: " + POINTS_FORMAT.format(raid.personal)); + hourlyPoints.setFont(FontManager.getRunescapeSmallFont()); + hourlyPoints.setText("Points/Hr: " + POINTS_FORMAT.format(raid.hr)); + totalPoints.setFont(FontManager.getRunescapeSmallFont()); + totalPoints.setText("Total: " + POINTS_FORMAT.format(raid.total)); + teamHour.setFont(FontManager.getRunescapeSmallFont()); + teamHour.setText("Points/Hr: " + POINTS_FORMAT.format((((float) raid.total / raid.timeTaken) * 3600))); + killCount.setFont(FontManager.getRunescapeSmallFont()); + killCount.setText("KC: " + raid.kc); + raidTime.setFont(FontManager.getRunescapeSmallFont()); + raidTime.setText("Time: " + formatSeconds(raid.timeTaken)); + //maybe add drop rate + statsPanel.add(personalPoints); + statsPanel.add(hourlyPoints); + statsPanel.add(killCount); + statsPanel.add(raidTime); + + + headerPanel.add(skillIcon, BorderLayout.WEST); + headerPanel.add(statsPanel, BorderLayout.CENTER); + + JPanel progressWrapper = new JPanel(); + progressWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); + progressWrapper.setLayout(new BorderLayout()); + progressWrapper.setBorder(new EmptyBorder(0, 7, 7, 7)); + layout.setFont(FontManager.getRunescapeSmallFont()); + if (raid.cm) + { + layout.setText("

Challenge Mode

"); + } + else + { + layout.setText("

" + raid.raid + "

"); + } + + + progressWrapper.add(layout, BorderLayout.NORTH); + + container.add(headerPanel, BorderLayout.NORTH); + container.add(progressWrapper, BorderLayout.SOUTH); + + + add(container, BorderLayout.NORTH); + } + + + static String htmlLabel(String key, int value) + { + String valueStr = StackFormatter.quantityToRSDecimalStack(value); + return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); + } + + static String htmlLabel(String key, String value) + { + String valueStr = value; + return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); + } + + public static String formatSeconds(int timeInSeconds) + { + int hours = timeInSeconds / 3600; + int secondsLeft = timeInSeconds - hours * 3600; + int minutes = secondsLeft / 60; + int seconds = secondsLeft - minutes * 60; + + String formattedTime = ""; + if (hours < 10) + { + formattedTime += "0"; + } + formattedTime += hours + ":"; + + if (minutes < 10) + { + formattedTime += "0"; + } + formattedTime += minutes + ":"; + + if (seconds < 10) + { + formattedTime += "0"; + } + formattedTime += seconds; + + return formattedTime; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/XpInfoBox.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/XpInfoBox.java new file mode 100644 index 0000000000..defbe55a7a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/XpInfoBox.java @@ -0,0 +1,210 @@ +/* + * Copyright (c) 2018, Adam + * Copyright (c) 2018, Psikoi + * 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.newraids; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.text.DecimalFormat; +import javax.swing.ImageIcon; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.SwingConstants; +import javax.swing.border.EmptyBorder; +import lombok.AccessLevel; +import lombok.Getter; +import net.runelite.api.Client; +import net.runelite.client.game.SkillIconManager; +import net.runelite.client.ui.ColorScheme; +import net.runelite.client.ui.DynamicGridLayout; +import net.runelite.client.ui.FontManager; +import net.runelite.client.util.ColorUtil; +import net.runelite.client.util.ImageUtil; + +class XpInfoBox extends JPanel +{ + static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###"); + private static final DecimalFormat TWO_DECIMAL_FORMAT = new DecimalFormat("0.00"); + // Templates + private static final String HTML_TOOL_TIP_TEMPLATE = + "%s %s done
" + + "%s %s/hr
" + + "%s till goal lvl"; + private static final String HTML_LABEL_TEMPLATE = + "%s%s"; + + // Instance members + private final JPanel panel; + + @Getter(AccessLevel.PACKAGE) + private final data raid; + + /* The tracker's wrapping container */ + private final JPanel container = new JPanel(); + + /* Contains the skill icon and the stats panel */ + private final JPanel headerPanel = new JPanel(); + + /* Contains all the skill information (exp gained, per hour, etc) */ + private final JPanel statsPanel = new JPanel(); + + + private final JLabel personalPoints = new JLabel(); + private final JLabel hourlyPoints = new JLabel(); + private final JLabel totalPoints = new JLabel(); + private final JLabel raidTime = new JLabel(); + private final JLabel layout = new JLabel(); + private final JLabel teamHour = new JLabel(); + private final JLabel killCount = new JLabel(); + private final JMenuItem pauseSkill = new JMenuItem("Pause"); + + private RaidsConfig raidsConfig; + + private boolean paused = false; + + XpInfoBox(RaidsPlugin raidsPlugin, RaidsConfig raidsConfig, Client client, JPanel panel, data raid, SkillIconManager iconManager) throws IOException + { + this.raidsConfig = raidsConfig; + this.panel = panel; + this.raid = raid; + + setLayout(new BorderLayout()); + setBorder(new EmptyBorder(5, 0, 0, 0)); + + container.setLayout(new BorderLayout()); + container.setBackground(ColorScheme.DARKER_GRAY_COLOR); + + + // Create reset others menu + //pauseSkill.addActionListener(e -> raidsPlugin.pauseSkill(skill, !paused)); + + BufferedImage image = ImageUtil.getResourceStreamFromClass(this.getClass(), "cox.png"); + ImageIcon i = new ImageIcon(image); + + JLabel skillIcon = new JLabel(i); + skillIcon.setHorizontalAlignment(SwingConstants.CENTER); + skillIcon.setVerticalAlignment(SwingConstants.CENTER); + skillIcon.setPreferredSize(new Dimension(35, 35)); + + headerPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + headerPanel.setLayout(new BorderLayout()); + + + statsPanel.setBackground(ColorScheme.DARKER_GRAY_COLOR); + statsPanel.setBorder(new EmptyBorder(9, 2, 9, 2)); + statsPanel.setLayout(new DynamicGridLayout(3, 2)); + personalPoints.setFont(FontManager.getRunescapeSmallFont()); + personalPoints.setText("Personal: " + POINTS_FORMAT.format(raid.personal)); + hourlyPoints.setFont(FontManager.getRunescapeSmallFont()); + hourlyPoints.setText("Points/Hr: " + POINTS_FORMAT.format(raid.hr)); + totalPoints.setFont(FontManager.getRunescapeSmallFont()); + totalPoints.setText("Total: " + POINTS_FORMAT.format(raid.total)); + teamHour.setFont(FontManager.getRunescapeSmallFont()); + teamHour.setText("Points/Hr: " + POINTS_FORMAT.format((((float) raid.total / raid.timeTaken) * 3600))); + killCount.setFont(FontManager.getRunescapeSmallFont()); + killCount.setText("KC: " + raid.kc); + raidTime.setFont(FontManager.getRunescapeSmallFont()); + raidTime.setText("Time: " + formatSeconds(raid.timeTaken)); + //maybe add drop rate + statsPanel.add(personalPoints); + statsPanel.add(totalPoints); + statsPanel.add(hourlyPoints); + statsPanel.add(teamHour); + statsPanel.add(killCount); + statsPanel.add(raidTime); + + + headerPanel.add(skillIcon, BorderLayout.WEST); + headerPanel.add(statsPanel, BorderLayout.CENTER); + + JPanel progressWrapper = new JPanel(); + progressWrapper.setBackground(ColorScheme.DARKER_GRAY_COLOR); + progressWrapper.setLayout(new BorderLayout()); + progressWrapper.setBorder(new EmptyBorder(0, 7, 7, 7)); + layout.setFont(FontManager.getRunescapeSmallFont()); + if (raid.cm) + { + layout.setText("

Challenge Mode

"); + } + else + { + layout.setText("

" + raid.raid + "

"); + } + + + progressWrapper.add(layout, BorderLayout.NORTH); + + container.add(headerPanel, BorderLayout.NORTH); + container.add(progressWrapper, BorderLayout.SOUTH); + + + add(container, BorderLayout.NORTH); + } + + + static String htmlLabel(String key, int value) + { + String valueStr = POINTS_FORMAT.format(value); + return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); + } + + static String htmlLabel(String key, String value) + { + String valueStr = value; + return String.format(HTML_LABEL_TEMPLATE, ColorUtil.toHexColor(ColorScheme.LIGHT_GRAY_COLOR), key, valueStr); + } + + public static String formatSeconds(int timeInSeconds) + { + int hours = timeInSeconds / 3600; + int secondsLeft = timeInSeconds - hours * 3600; + int minutes = secondsLeft / 60; + int seconds = secondsLeft - minutes * 60; + + String formattedTime = ""; + if (hours < 10) + { + formattedTime += "0"; + } + formattedTime += hours + ":"; + + if (minutes < 10) + { + formattedTime += "0"; + } + formattedTime += minutes + ":"; + + if (seconds < 10) + { + formattedTime += "0"; + } + formattedTime += seconds; + + return formattedTime; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/data.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/data.java new file mode 100644 index 0000000000..0e5be9e9c4 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/data.java @@ -0,0 +1,29 @@ +package net.runelite.client.plugins.newraids; + +import java.util.Date; + +public class data +{ + int total; + int personal; + int hr; + int timeTaken; + int kc; + boolean cm; + String raid; + Date start; + Date finish; + + void add(int total, int personal, int hr, int timeTaken, int kc, boolean cm, String raid, Date start, Date finish) + { + this.total = total; + this.personal = personal; + this.hr = hr; + this.kc = kc; + this.cm = cm; + this.timeTaken = timeTaken; + this.raid = raid; + this.start = start; + this.finish = finish; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/Layout.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/Layout.java new file mode 100644 index 0000000000..0ad48a725a --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/Layout.java @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids.solver; + +import java.util.ArrayList; +import java.util.List; +import lombok.Getter; + +public class Layout +{ + @Getter + private final List rooms = new ArrayList<>(); + + public void add(Room room) + { + rooms.add(room); + } + + public Room getRoomAt(int position) + { + for (Room room : rooms) + { + if (room.getPosition() == position) + { + return room; + } + } + + return null; + } + + public String toCode() + { + StringBuilder builder = new StringBuilder(); + + for (Room room : rooms) + { + builder.append(room.getSymbol()); + } + + return builder.toString(); + } + + public String toCodeString() + { + return toCode().replaceAll("#", "").replaceAll("¤", ""); + } + +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/LayoutSolver.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/LayoutSolver.java new file mode 100644 index 0000000000..f766ea9d3c --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/LayoutSolver.java @@ -0,0 +1,226 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids.solver; + +import com.google.inject.Singleton; +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import lombok.Getter; +import lombok.extern.slf4j.Slf4j; + +/* + * Implementation of https://github.com/WooxSolo/raids-layout + * Copyright (c) 2017 WooxSolo + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + */ +@Slf4j +@Singleton +public class LayoutSolver +{ + @Getter + private static final List layouts = new ArrayList<>(); + private static final Pattern regex = Pattern.compile("^([A-Z]*)\\.([A-Z]*) - #([A-Z]*)#([A-Z]*)$"); + private static final String[] codes = + { + "FSCCP.PCSCF - #WNWSWN#ESEENW", + "FSCCS.PCPSF - #WSEEEN#WSWNWS", + "FSCPC.CSCPF - #WNWWSE#EENWWW", + "SCCFC.PSCSF - #EEENWW#WSEEEN", + "SCCFP.CCSPF - #NESEEN#WSWNWS", + "SCFCP.CCSPF - #ESEENW#ESWWNW", + "SCFCP.CSCFS - #ENEESW#ENWWSW", + "SCFCPC.CSPCSF - #ESWWNWS#NESENES", + "SCFPC.CSPCF - #WSWWNE#WSEENE", + "SCFPC.PCCSF - #WSEENE#WWWSEE", + "SCFPC.SCPCF - #NESENE#WSWWNE", + "SCPFC.CCPSF - #NWWWSE#WNEESE", + "SCPFC.CSPCF - #NEEESW#WWNEEE", + "SCPFC.CSPSF - #WWSEEE#NWSWWN", + "SCSPF.CCSPF - #ESWWNW#ESENES", + "SFCCP.CSCPF - #WNEESE#NWSWWN", + "SFCCS.PCPSF - #ENWWSW#ENESEN", + "SPCFC.CSPCF - #WWNEEE#WSWNWS", + "SPCFC.SCCPF - #ESENES#WWWNEE", + "SPSFP.CCCSF - #NWSWWN#ESEENW", + "SCFCP.CSCPF - #ENESEN#WWWSEE", + "SCPFC.PCSCF - #WNEEES#NWSWNW", + "SFCCPC.PCSCPF - #WSEENES#WWWNEEE", + "FSPCC.PSCCF - #WWWSEE#ENWWSW", + "FSCCP.PCSCF - #ENWWWS#NEESEN", + "SCPFC.CCSSF - #NEESEN#WSWWNE" + }; + + public LayoutSolver() + { + build(); + } + + public Layout findLayout(String code) + { + Layout solution = null; + int matches = 0; + boolean match; + + for (Layout layout : layouts) + { + match = true; + + for (int i = 0; i < code.length(); i++) + { + Room room = layout.getRoomAt(i); + char c = code.charAt(i); + + if (room != null && c != ' ' && c != room.getSymbol()) + { + match = false; + break; + } + } + + if (match) + { + solution = layout; + matches++; + log.debug("Found matching layout: " + layout.toCode()); + } + else + { + + } + } + + if (matches == 1) + { + return solution; + } + + return null; + } + + private int calcStart(String directions) + { + int startPos = 0; + int position = 0; + + for (int i = 0; i < directions.length(); i++) + { + char c = directions.charAt(i); + int delta = dirToPosDelta(c); + position += delta; + + if (position < 0 || position >= 8 || (position == 3 && delta == -1) || (position == 4 && delta == 1)) + { + position -= delta; + startPos -= delta; + } + } + + return startPos; + } + + private int dirToPosDelta(char direction) + { + switch (String.valueOf(direction)) + { + case "N": + return -4; + + case "E": + return 1; + + case "S": + return 4; + + case "W": + return -1; + + default: + return 0; + } + } + + private void build() + { + for (String code : codes) + { + Matcher match = regex.matcher(code); + + if (!match.find()) + { + continue; + } + + String symbols, directions; + int position = calcStart(match.group(3)); + Layout layout = new Layout(); + Room lastRoom = null; + Room room; + + for (int floor = 0; floor < 2; floor++) + { + symbols = match.group(1 + floor); + directions = match.group(3 + floor); + + for (int i = 0; i < directions.length(); i++) + { + char symbol = (i == 0 ? '#' : symbols.charAt(i - 1)); + + room = new Room(position, symbol); + + if (lastRoom != null) + { + lastRoom.setNext(room); + room.setPrevious(lastRoom); + } + + layout.add(room); + lastRoom = room; + + int delta = dirToPosDelta(directions.charAt(i)); + position += delta; + } + + room = new Room(position, '¤'); + room.setPrevious(lastRoom); + lastRoom.setNext(room); + layout.add(room); + position += 8; + } + + layouts.add(layout); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/Room.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/Room.java new file mode 100644 index 0000000000..f63d7cd779 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/Room.java @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids.solver; + +import lombok.Getter; +import lombok.Setter; + +public class Room +{ + @Getter + private final int position; + + @Getter + private final char symbol; + + @Getter + @Setter + private Room next; + + @Getter + @Setter + private Room previous; + + Room(int position, char symbol) + { + this.position = position; + this.symbol = symbol; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/RotationSolver.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/RotationSolver.java new file mode 100644 index 0000000000..eb27ede0ca --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/solver/RotationSolver.java @@ -0,0 +1,150 @@ +/* + * Copyright (c) 2018, Kamiel + * 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.newraids.solver; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import net.runelite.client.plugins.newraids.RaidRoom; +import net.runelite.client.plugins.newraids.RaidRoom.Boss; + +public class RotationSolver +{ + private static final Rotation[] ROTATIONS = + { + new Rotation<>(Arrays.asList(Boss.TEKTON, Boss.VASA, Boss.GUARDIANS, Boss.MYSTICS, Boss.SHAMANS, Boss.MUTTADILES, Boss.VANGUARDS, Boss.VESPULA)), + new Rotation<>(Arrays.asList(Boss.TEKTON, Boss.MUTTADILES, Boss.GUARDIANS, Boss.VESPULA, Boss.SHAMANS, Boss.VASA, Boss.VANGUARDS, Boss.MYSTICS)), + new Rotation<>(Arrays.asList(Boss.VESPULA, Boss.VANGUARDS, Boss.MUTTADILES, Boss.SHAMANS, Boss.MYSTICS, Boss.GUARDIANS, Boss.VASA, Boss.TEKTON)), + new Rotation<>(Arrays.asList(Boss.MYSTICS, Boss.VANGUARDS, Boss.VASA, Boss.SHAMANS, Boss.VESPULA, Boss.GUARDIANS, Boss.MUTTADILES, Boss.TEKTON)) + }; + + public static boolean solve(RaidRoom[] rooms) + { + if (rooms == null) + { + return false; + } + + Rotation match = null; + Integer start = null; + Integer index = null; + int known = 0; + + for (int i = 0; i < rooms.length; i++) + { + if (rooms[i] == null || rooms[i].getBoss() == null || rooms[i].getBoss() == Boss.UNKNOWN) + { + continue; + } + + if (start == null) + { + start = i; + } + + known++; + } + + if (known < 2) + { + return false; + } + + if (known == rooms.length) + { + return true; + } + + for (Rotation rotation : ROTATIONS) + { + COMPARE: + for (int i = 0; i < rotation.size(); i++) + { + if (rooms[start].getBoss() == rotation.get(i)) + { + for (int j = start + 1; j < rooms.length; j++) + { + if (rooms[j].getBoss() == null || rooms[j].getBoss() == Boss.UNKNOWN) + { + continue; + } + + if (rooms[j].getBoss() != rotation.get(i + j - start)) + { + break COMPARE; + } + } + + if (match != null && match != rotation) + { + return false; + } + + index = i - start; + match = rotation; + } + } + } + + if (match == null) + { + return false; + } + + for (int i = 0; i < rooms.length; i++) + { + if (rooms[i] == null) + { + continue; + } + + if (rooms[i].getBoss() == null || rooms[i].getBoss() == Boss.UNKNOWN) + { + rooms[i].setBoss(match.get(index + i)); + } + } + + return true; + } + + private static class Rotation extends ArrayList + { + Rotation(Collection bosses) + { + super(bosses); + } + + @Override + public E get(int index) + { + if (index < 0) + { + index = index + size(); + } + + return super.get(index % size()); + } + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/newraids/timerSpecial.java b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/timerSpecial.java new file mode 100644 index 0000000000..5be382f076 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/newraids/timerSpecial.java @@ -0,0 +1,63 @@ +package net.runelite.client.plugins.newraids; + +public class timerSpecial +{ + public boolean started = false; + private long startTime = 0; + private long difference = 0; + private long pause = 0; + private long unpaused = 0; + private long tempTime = 0; + private boolean paused = false; + + public void start() + { + startTime = System.currentTimeMillis(); + tempTime = startTime; + } + + public void start(long offset) + { + startTime = System.currentTimeMillis() - offset * 1000; + tempTime = startTime; + started = true; + } + + public void pause() + { + pause = System.currentTimeMillis(); + tempTime = (System.currentTimeMillis() - startTime) / 1000; + paused = true; + } + + public void unpause() + { + paused = false; + unpaused = System.currentTimeMillis(); + difference = unpaused - pause; + startTime = startTime + difference; + } + + public void reset() + { + startTime = 0; + difference = 0; + pause = 0; + unpaused = 0; + tempTime = 0; + paused = false; + startTime = System.currentTimeMillis(); + tempTime = startTime; + started = false; + } + + public long getElapsedTime() + { + if (paused) + { + return (tempTime); + } + return (System.currentTimeMillis() - startTime) / 1000; + // return (System.currentTimeMillis() - startTime + difference) / 1000; //returns in seconds + } +}