diff --git a/runelite-api/src/main/java/net/runelite/api/Varbits.java b/runelite-api/src/main/java/net/runelite/api/Varbits.java index b4bf6a477f..84dd4cf55e 100644 --- a/runelite-api/src/main/java/net/runelite/api/Varbits.java +++ b/runelite-api/src/main/java/net/runelite/api/Varbits.java @@ -179,6 +179,8 @@ public enum Varbits * Raids */ IN_RAID(5432), + TOTAL_POINTS(5431), + PERSONAL_POINTS(5422), /** * Nightmare Zone diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java index caf2207f14..a7b151f409 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/Raid.java @@ -24,7 +24,9 @@ */ package net.runelite.client.plugins.raids; +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.raids.solver.Layout; @@ -109,6 +111,11 @@ public class Raid 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(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java index 8879806273..4beeb15957 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsConfig.java @@ -36,6 +36,7 @@ import net.runelite.client.config.ConfigItem; public interface RaidsConfig extends Config { @ConfigItem( + position = 0, keyName = "raidsTimer", name = "Display elapsed raid time", description = "Display elapsed raid time" @@ -46,6 +47,7 @@ public interface RaidsConfig extends Config } @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" @@ -56,6 +58,7 @@ public interface RaidsConfig extends Config } @ConfigItem( + position = 2, keyName = "scoutOverlay", name = "Show scout overlay", description = "Display an overlay that shows the current raid layout (when entering lobby)" @@ -66,6 +69,7 @@ public interface RaidsConfig extends Config } @ConfigItem( + position = 3, keyName = "scoutOverlayAtBank", name = "Show scout overlay outside lobby", description = "Keep the overlay active while at the raids area" @@ -76,6 +80,18 @@ public interface RaidsConfig extends Config } @ConfigItem( + position = 4, + 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 = 5, keyName = "blacklistedRooms", name = "Blacklisted rooms", description = "Display blacklisted rooms in red on the overlay. Separate with comma (full name)" @@ -84,4 +100,48 @@ public interface RaidsConfig extends Config { return ""; } + + @ConfigItem( + position = 6, + keyName = "enableRotationWhitelist", + name = "Enable rotation whitelist", + description = "Enable the rotation whitelist" + ) + default boolean enableRotationWhitelist() + { + return false; + } + + @ConfigItem( + position = 7, + 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 = 8, + keyName = "enableLayoutWhitelist", + name = "Enable layout whitelist", + description = "Enable the layout whitelist" + ) + default boolean enableLayoutWhitelist() + { + return false; + } + + @ConfigItem( + position = 9, + 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 ""; + } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsOverlay.java index 48388e89be..802b4dd19e 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsOverlay.java @@ -74,6 +74,26 @@ public class RaidsOverlay extends Overlay panelComponent.setTitleColor(Color.WHITE); panelComponent.setTitle("Raid scouter"); + Color color = Color.WHITE; + String layout = plugin.getRaid().getLayout().toCode().replaceAll("#", "").replaceAll("ยค", ""); + + if (config.enableLayoutWhitelist() && !plugin.getLayoutWhitelist().contains(layout.toLowerCase())) + { + color = Color.RED; + } + + panelComponent.getLines().add(new PanelComponent.Line( + "Layout", Color.WHITE, layout, color + )); + + int bossMatches = 0; + int bossCount = 0; + + if (config.enableRotationWhitelist()) + { + bossMatches = plugin.getRotationMatches(); + } + for (Room layoutRoom : plugin.getRaid().getLayout().getRooms()) { int position = layoutRoom.getPosition(); @@ -84,12 +104,18 @@ public class RaidsOverlay extends Overlay continue; } - Color color = Color.WHITE; + color = Color.WHITE; switch (room.getType()) { case COMBAT: - if (plugin.getBlacklist().contains(room.getBoss().getName().toLowerCase())) + 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; } @@ -100,7 +126,11 @@ public class RaidsOverlay extends Overlay break; case PUZZLE: - if (plugin.getBlacklist().contains(room.getPuzzle().getName().toLowerCase())) + if (plugin.getRoomWhitelist().contains(room.getPuzzle().getName().toLowerCase())) + { + color = Color.GREEN; + } + else if (plugin.getRoomBlacklist().contains(room.getPuzzle().getName().toLowerCase())) { color = Color.RED; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java index e9fa977526..51c1e62f8f 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsPlugin.java @@ -35,17 +35,25 @@ import java.text.DecimalFormat; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.imageio.ImageIO; import javax.inject.Inject; import lombok.Getter; import lombok.extern.slf4j.Slf4j; -import net.runelite.api.*; +import net.runelite.api.ChatMessageType; +import net.runelite.api.Client; +import net.runelite.api.GameState; +import net.runelite.api.InstanceTemplates; +import net.runelite.api.ObjectID; +import net.runelite.api.Point; +import net.runelite.api.Setting; +import net.runelite.api.Tile; +import net.runelite.api.Varbits; import static net.runelite.api.Perspective.SCENE_SIZE; import net.runelite.api.events.ChatMessage; import net.runelite.api.events.ConfigChanged; import net.runelite.api.events.VarbitChanged; -import net.runelite.api.widgets.Widget; -import net.runelite.api.widgets.WidgetInfo; import net.runelite.client.chat.ChatColor; import net.runelite.client.chat.ChatColorType; import net.runelite.client.chat.ChatMessageBuilder; @@ -68,9 +76,12 @@ public class RaidsPlugin extends Plugin { 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 RAID_COMPLETE_MESSAGE = "Congratulations - your raid is complete!"; - private static final int TOTAL_POINTS = 0, PERSONAL_POINTS = 1, TEXT_CHILD = 4; private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("###.##"); + private static final DecimalFormat POINTS_FORMAT = new DecimalFormat("#,###"); + private static final String SPLIT_REGEX = "\\s*,\\s*"; + private static final Pattern ROTATION_REGEX = Pattern.compile("\\[(.*?)\\]"); private BufferedImage raidsIcon; private RaidsTimer timer; @@ -98,7 +109,16 @@ public class RaidsPlugin extends Plugin private Raid raid; @Getter - private ArrayList blacklist = new ArrayList<>(); + private ArrayList roomWhitelist = new ArrayList<>(); + + @Getter + private ArrayList roomBlacklist = new ArrayList<>(); + + @Getter + private ArrayList rotationWhitelist = new ArrayList<>(); + + @Getter + private ArrayList layoutWhitelist = new ArrayList<>(); @Provides RaidsConfig provideConfig(ConfigManager configManager) @@ -132,7 +152,7 @@ public class RaidsPlugin extends Plugin cacheColors(); } - updateBlacklist(); + updateLists(); } @Override @@ -157,9 +177,24 @@ public class RaidsPlugin extends Plugin updateInfoBoxState(); } + if (event.getKey().equals("whitelistedRooms")) + { + updateList(roomWhitelist, config.whitelistedRooms()); + } + if (event.getKey().equals("blacklistedRooms")) { - updateBlacklist(); + updateList(roomBlacklist, config.blacklistedRooms()); + } + + if (event.getKey().equals("whitelistedRotations")) + { + updateList(rotationWhitelist, config.whitelistedRotations()); + } + + if (event.getKey().equals("whitelistedLayouts")) + { + updateList(layoutWhitelist, config.whitelistedLayouts()); } } @@ -222,19 +257,23 @@ public class RaidsPlugin extends Plugin infoBoxManager.addInfoBox(timer); } + if (timer != null && message.contains(LEVEL_COMPLETE_MESSAGE)) + { + timer.timeFloor(); + } + if (message.startsWith(RAID_COMPLETE_MESSAGE)) { if (timer != null) { + timer.timeFloor(); timer.setStopped(true); } if (config.pointsMessage()) { - Widget raidsWidget = client.getWidget(WidgetInfo.RAIDS_POINTS_INFOBOX).getChild(TEXT_CHILD); - String[] raidPoints = raidsWidget.getText().split("
"); - int totalPoints = Integer.parseInt(raidPoints[TOTAL_POINTS].replace(",", "")); - int personalPoints = Integer.parseInt(raidPoints[PERSONAL_POINTS].replace(",", "")); + int totalPoints = client.getSetting(Varbits.TOTAL_POINTS); + int personalPoints = client.getSetting(Varbits.PERSONAL_POINTS); double percentage = personalPoints / (totalPoints / 100.0); @@ -242,11 +281,11 @@ public class RaidsPlugin extends Plugin .append(ChatColorType.NORMAL) .append("Total points: ") .append(ChatColorType.HIGHLIGHT) - .append(raidPoints[TOTAL_POINTS]) + .append(POINTS_FORMAT.format(totalPoints)) .append(ChatColorType.NORMAL) .append(", Personal points: ") .append(ChatColorType.HIGHLIGHT) - .append(raidPoints[PERSONAL_POINTS]) + .append(POINTS_FORMAT.format(personalPoints)) .append(ChatColorType.NORMAL) .append(" (") .append(ChatColorType.HIGHLIGHT) @@ -284,10 +323,35 @@ public class RaidsPlugin extends Plugin } } - private void updateBlacklist() + private void updateLists() { - blacklist.clear(); - blacklist.addAll(Arrays.asList(config.blacklistedRooms().toLowerCase().split("\\s*,\\s*"))); + updateList(roomWhitelist, config.blacklistedRooms()); + 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); + + if (!list.contains(rotation)) + { + list.add(rotation); + } + } + } + else + { + list.addAll(Arrays.asList(input.toLowerCase().split(SPLIT_REGEX))); + } } private void cacheColors() @@ -299,6 +363,43 @@ public class RaidsPlugin extends Plugin .refreshAll(); } + public 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.getRegion().getTiles()[LOBBY_PLANE]; diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java index 562105924a..8331789b64 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/raids/RaidsTimer.java @@ -36,7 +36,11 @@ import net.runelite.client.ui.overlay.infobox.InfoBox; public class RaidsTimer extends InfoBox { private final Instant startTime; + private Instant floorTime; private LocalTime time; + private LocalTime firstFloorTime; + private LocalTime secondFloorTime; + private LocalTime olmTime; @Setter private boolean stopped; @@ -45,9 +49,30 @@ public class RaidsTimer extends InfoBox { super(image); 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 (olmTime == null) + { + olmTime = LocalTime.ofSecondOfDay(elapsed.getSeconds()); + } + + floorTime = Instant.now(); + } + @Override public String getText() { @@ -84,6 +109,28 @@ public class RaidsTimer extends InfoBox @Override public String getTooltip() { - return "Elapsed raid time: " + time.format(DateTimeFormatter.ofPattern("HH:mm:ss")); + 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 (olmTime != null) + { + builder.append("
Olm: "); + builder.append(olmTime.format(DateTimeFormatter.ofPattern("mm:ss"))); + } + + return builder.toString(); } }