diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningOverlay.java index 135a55a71d..1dfeb1c1a0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningOverlay.java @@ -34,12 +34,14 @@ import java.awt.Polygon; import java.awt.Rectangle; import java.time.Duration; import java.time.Instant; -import java.util.Set; +import java.util.Iterator; +import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; import net.runelite.api.Client; import net.runelite.api.Perspective; import net.runelite.api.Point; +import net.runelite.api.Projectile; import net.runelite.api.coords.WorldPoint; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayLayer; @@ -90,35 +92,32 @@ public class AoeWarningOverlay extends Overlay } Instant now = Instant.now(); - Set projectiles = plugin.getProjectiles(); - projectiles.forEach(proj -> + Map projectiles = plugin.getProjectiles(); + for (Iterator it = projectiles.values().iterator(); it.hasNext(); ) { - if (proj.getTargetPoint() == null) - { - return; - } - + AoeProjectile aoeProjectile = it.next(); Color color; - - if (now.isAfter(proj.getStartTime().plus(Duration.ofMillis(proj.getLifetime())))) + if (now.isAfter(aoeProjectile.getStartTime().plus(Duration.ofMillis(aoeProjectile.getProjectileLifetime())))) { - return; + it.remove(); + continue; } - final Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, proj.getTargetPoint(), proj.getAoeProjectileInfo().getAoeSize()); + Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, aoeProjectile.getTargetPoint(), aoeProjectile.getAoeProjectileInfo().getAoeSize()); if (tilePoly == null) { - return; + continue; } - final double progress = (System.currentTimeMillis() - proj.getStartTime().toEpochMilli()) / (double) proj.getLifetime(); + // how far through the projectiles lifetime between 0-1. + double progress = (System.currentTimeMillis() - aoeProjectile.getStartTime().toEpochMilli()) / (double) aoeProjectile.getProjectileLifetime(); - final int tickProgress = proj.getFinalTick() - client.getTickCount(); + int tickProgress = aoeProjectile.getFinalTick() - client.getTickCount(); int fillAlpha, outlineAlpha; if (plugin.isConfigFadeEnabled()) { - fillAlpha = (int) ((1 - progress) * FILL_START_ALPHA); + fillAlpha = (int) ((1 - progress) * FILL_START_ALPHA);//alpha drop off over lifetime outlineAlpha = (int) ((1 - progress) * OUTLINE_START_ALPHA); } else @@ -166,8 +165,7 @@ public class AoeWarningOverlay extends Overlay graphics.setColor(new Color(setAlphaComponent(plugin.getOverlayColor().getRGB(), fillAlpha), true)); graphics.fillPolygon(tilePoly); - }); - projectiles.removeIf(proj -> now.isAfter(proj.getStartTime().plus(Duration.ofMillis(proj.getLifetime())))); + } return null; } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningPlugin.java index 55f5c6838a..8c7a98d914 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/AoeWarningPlugin.java @@ -32,10 +32,9 @@ import java.awt.Color; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; +import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; import lombok.AccessLevel; @@ -45,6 +44,7 @@ import net.runelite.api.Client; import net.runelite.api.GameObject; import net.runelite.api.GameState; import net.runelite.api.GraphicID; +import net.runelite.api.GraphicsObject; import net.runelite.api.NullObjectID; import net.runelite.api.ObjectID; import net.runelite.api.Projectile; @@ -57,7 +57,6 @@ import net.runelite.api.events.GameObjectSpawned; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameTick; import net.runelite.api.events.ProjectileMoved; -import net.runelite.api.events.ProjectileSpawned; import net.runelite.client.Notifier; import net.runelite.client.config.ConfigManager; import net.runelite.client.eventbus.EventBus; @@ -80,7 +79,7 @@ public class AoeWarningPlugin extends Plugin @Getter(AccessLevel.PACKAGE) private final Map bombs = new HashMap<>(); @Getter(AccessLevel.PACKAGE) - private final Set projectiles = new HashSet<>(); + private final Map projectiles = new HashMap<>(); @Inject public AoeWarningConfig config; @Inject @@ -103,6 +102,13 @@ public class AoeWarningPlugin extends Plugin private List CrystalSpike = new ArrayList<>(); @Getter(AccessLevel.PACKAGE) private List WintertodtSnowFall = new ArrayList<>(); + + @Provides + AoeWarningConfig getConfig(ConfigManager configManager) + { + return configManager.getConfig(AoeWarningConfig.class); + } + // Config values private boolean aoeNotifyAll; @Getter(AccessLevel.PACKAGE) @@ -162,17 +168,12 @@ public class AoeWarningPlugin extends Plugin private boolean configDemonicGorillaEnabled; private boolean configDemonicGorillaNotifyEnabled; - @Provides - AoeWarningConfig getConfig(ConfigManager configManager) - { - return configManager.getConfig(AoeWarningConfig.class); - } - @Override protected void startUp() throws Exception { updateConfig(); addSubscriptions(); + overlayManager.add(coreOverlay); overlayManager.add(bombOverlay); reset(); @@ -196,7 +197,6 @@ public class AoeWarningPlugin extends Plugin eventbus.subscribe(GameObjectDespawned.class, this, this::onGameObjectDespawned); eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); eventbus.subscribe(GameTick.class, this, this::onGameTick); - eventbus.subscribe(ProjectileSpawned.class, this, this::onProjectileSpawned); } private void onConfigChanged(ConfigChanged event) @@ -209,52 +209,33 @@ public class AoeWarningPlugin extends Plugin updateConfig(); } - private void onProjectileSpawned(ProjectileSpawned event) + private void onProjectileMoved(ProjectileMoved event) { - final Projectile projectile = event.getProjectile(); + Projectile projectile = event.getProjectile(); - if (AoeProjectileInfo.getById(projectile.getId()) == null) - { - return; - } - - final int id = projectile.getId(); - final int lifetime = this.delay + (projectile.getRemainingCycles() * 20); + int projectileId = projectile.getId(); + int projectileLifetime = this.delay + (projectile.getRemainingCycles() * 20); int ticksRemaining = projectile.getRemainingCycles() / 30; - if (!isTickTimersEnabledForProjectileID(id)) + if (!isTickTimersEnabledForProjectileID(projectileId)) { ticksRemaining = 0; } - final int tickCycle = client.getTickCount() + ticksRemaining; - if (isConfigEnabledForProjectileId(id, false)) + int tickCycle = client.getTickCount() + ticksRemaining; + AoeProjectileInfo aoeProjectileInfo = AoeProjectileInfo.getById(projectileId); + if (aoeProjectileInfo != null + && isConfigEnabledForProjectileId(projectileId, false)) { - projectiles.add(new ProjectileContainer(projectile, Instant.now(), lifetime, tickCycle)); + LocalPoint targetPoint = event.getPosition(); + AoeProjectile aoeProjectile = new AoeProjectile(Instant.now(), targetPoint, aoeProjectileInfo, projectileLifetime, tickCycle); + projectiles.put(projectile, aoeProjectile); - if (this.aoeNotifyAll || isConfigEnabledForProjectileId(id, true)) + if (this.aoeNotifyAll || isConfigEnabledForProjectileId(projectileId, true)) { notifier.notify("AoE attack detected!"); } } } - private void onProjectileMoved(ProjectileMoved event) - { - if (projectiles.isEmpty()) - { - return; - } - - final Projectile projectile = event.getProjectile(); - - projectiles.forEach(proj -> - { - if (proj.getProjectile() == projectile) - { - proj.setTargetPoint(event.getPosition()); - } - }); - } - private void onGameObjectSpawned(GameObjectSpawned event) { final GameObject gameObject = event.getGameObject(); @@ -277,6 +258,7 @@ public class AoeWarningPlugin extends Plugin CrystalSpike.add(wp); break; case NullObjectID.NULL_26690: + //Wintertodt Snowfall if (this.configWintertodtEnabled) { WintertodtSnowFall.add(wp); @@ -306,7 +288,11 @@ public class AoeWarningPlugin extends Plugin CrystalSpike.remove(wp); break; case NullObjectID.NULL_26690: - WintertodtSnowFall.remove(wp); + //Wintertodt Snowfall + if (this.configWintertodtEnabled) + { + WintertodtSnowFall.remove(wp); + } break; } } @@ -321,11 +307,10 @@ public class AoeWarningPlugin extends Plugin private void onGameTick(GameTick event) { - LightningTrail.clear(); - if (this.configLightningTrail) { - client.getGraphicsObjects().forEach(o -> + LightningTrail.clear(); + for (GraphicsObject o : client.getGraphicsObjects()) { if (o.getId() == GraphicID.OLM_LIGHTNING) { @@ -336,29 +321,34 @@ public class AoeWarningPlugin extends Plugin notifier.notify("Lightning!"); } } - }); + } } - bombs.forEach((k, v) -> + for (Map.Entry entry : bombs.entrySet()) { - v.bombClockUpdate(); - }); + CrystalBomb bomb = entry.getValue(); + bomb.bombClockUpdate(); + //bombClockUpdate smooths the shown timer; not using this results in 1.2 --> .6 vs. 1.2 --> 1.1, etc. + } } private void purgeBombs(Map bombs) { + Iterator> it = bombs.entrySet().iterator(); Tile[][][] tiles = client.getScene().getTiles(); - bombs.forEach((k, v) -> + while (it.hasNext()) { - LocalPoint local = LocalPoint.fromWorld(client, k); + Map.Entry entry = it.next(); + WorldPoint world = entry.getKey(); + LocalPoint local = LocalPoint.fromWorld(client, world); if (local == null) { return; } - Tile tile = tiles[k.getPlane()][local.getSceneX()][local.getSceneY()]; + Tile tile = tiles[world.getPlane()][local.getSceneX()][local.getSceneY()]; GameObject[] objects = tile.getGameObjects(); boolean containsObjects = false; @@ -372,9 +362,10 @@ public class AoeWarningPlugin extends Plugin if (!containsObjects) { - bombs.remove(k, v); + it.remove(); } - }); + + } } private boolean isTickTimersEnabledForProjectileID(int projectileId) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/BombOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/BombOverlay.java index 9d56056d93..d51b52aee3 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/BombOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/BombOverlay.java @@ -33,11 +33,13 @@ import java.text.DecimalFormat; import java.text.NumberFormat; import java.time.Instant; import java.util.Locale; +import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; import lombok.extern.slf4j.Slf4j; import net.runelite.api.Client; import net.runelite.api.Perspective; +import net.runelite.api.Player; import net.runelite.api.Point; import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.WorldPoint; @@ -53,13 +55,24 @@ public class BombOverlay extends Overlay { private static final String SAFE = "#00cc00"; + //safe private static final String CAUTION = "#ffff00"; + //1 tile in range (minor damage) private static final String WARNING = "#ff9933"; + //2 tiles in range (moderate damage) private static final String DANGER = "#ff6600"; + //3 tiles in range/adjacent to bomb (major damage) private static final String LETHAL = "#cc0000"; + //On the bomb, using it as a makeshift space launch vehicle. (massive damage) + private static final int BOMB_AOE = 7; private static final int BOMB_DETONATE_TIME = 8; + //This is in ticks. It should be 10, but it varies from 8 to 11. private static final double ESTIMATED_TICK_LENGTH = .6; + //Thank you Woox & co. for this assumption. .6 seconds/tick. + + + //Utilized from the npc highlight code for formatting text being displayed on the client canvas. private static final NumberFormat TIME_LEFT_FORMATTER = DecimalFormat.getInstance(Locale.US); @@ -86,72 +99,84 @@ public class BombOverlay extends Overlay { if (plugin.isConfigbombDisplay()) { - drawDangerZone(graphics); + drawBombs(graphics); } return null; } - private void drawDangerZone(Graphics2D graphics) + private void drawBombs(Graphics2D graphics) + //I can condense drawDangerZone into this. Ambivalent though. { - final WorldPoint loc = client.getLocalPlayer().getWorldLocation(); - plugin.getBombs().forEach((k, v) -> + for (Map.Entry entry : plugin.getBombs().entrySet()) { - LocalPoint localLoc = LocalPoint.fromWorld(client, v.getWorldLocation()); + CrystalBomb bomb = entry.getValue(); + drawDangerZone(graphics, bomb); + } + } - if (localLoc == null) - { - return; - } + private void drawDangerZone(Graphics2D graphics, CrystalBomb bomb) + { + final Player localPlayer = client.getLocalPlayer(); + LocalPoint localLoc = LocalPoint.fromWorld(client, bomb.getWorldLocation()); + if (localLoc == null) + { + return; + } + double distance_x = Math.abs(bomb.getWorldLocation().getX() - localPlayer.getWorldLocation().getX()); + double distance_y = Math.abs(bomb.getWorldLocation().getY() - localPlayer.getWorldLocation().getY()); + Color color_code = Color.decode(SAFE); + //defaults to this unless conditionals met below. - final double distance_x = Math.abs(v.getWorldLocation().getX() - loc.getX()); - final double distance_y = Math.abs(v.getWorldLocation().getY() - loc.getY()); + if (distance_x < 1 && distance_y < 1) + { + color_code = Color.decode(LETHAL); + } + else if (distance_x < 2 && distance_y < 2) + { + color_code = Color.decode(DANGER); + } + else if (distance_x < 3 && distance_y < 3) + { + color_code = Color.decode(WARNING); + } + else if (distance_x < 4 && distance_y < 4) + { + color_code = Color.decode(CAUTION); + } + LocalPoint CenterPoint = new LocalPoint(localLoc.getX(), localLoc.getY()); + Polygon poly = Perspective.getCanvasTileAreaPoly(client, CenterPoint, BOMB_AOE); - Color color_code = Color.decode(SAFE); + if (poly != null) + { + //manually generating the polygon so as to assign a custom alpha value. Request adtl' arg for alpha maybe? + graphics.setColor(color_code); + graphics.setStroke(new BasicStroke(1)); + graphics.drawPolygon(poly); + graphics.setColor(new Color(0, 0, 0, 10)); + graphics.fillPolygon(poly); + } - if (distance_x < 1 && distance_y < 1) - { - color_code = Color.decode(LETHAL); - } - else if (distance_x < 2 && distance_y < 2) - { - color_code = Color.decode(DANGER); - } - else if (distance_x < 3 && distance_y < 3) - { - color_code = Color.decode(WARNING); - } - else if (distance_x < 4 && distance_y < 4) - { - color_code = Color.decode(CAUTION); - } - final LocalPoint CenterPoint = new LocalPoint(localLoc.getX(), localLoc.getY()); - final Polygon poly = Perspective.getCanvasTileAreaPoly(client, CenterPoint, BOMB_AOE); + Instant now = Instant.now(); + double timeLeft = ((BOMB_DETONATE_TIME - (client.getTickCount() - + bomb.getTickStarted())) * ESTIMATED_TICK_LENGTH) - + (now.toEpochMilli() - bomb.getLastClockUpdate().toEpochMilli()) / 1000.0; + //divided by 1000.00 because of milliseconds :) - if (poly != null) - { - graphics.setColor(color_code); - graphics.setStroke(new BasicStroke(1)); - graphics.drawPolygon(poly); - graphics.setColor(new Color(0, 0, 0, 10)); - graphics.fillPolygon(poly); - } + timeLeft = Math.max(0.0, timeLeft); + String bombTimerString = TIME_LEFT_FORMATTER.format(timeLeft); + int textWidth = graphics.getFontMetrics().stringWidth(bombTimerString); + int textHeight = graphics.getFontMetrics().getAscent(); + Point canvasPoint = Perspective.localToCanvas(client, localLoc.getX(), + localLoc.getY(), bomb.getWorldLocation().getPlane()); - final Instant now = Instant.now(); - double timeLeft = ((BOMB_DETONATE_TIME - (client.getTickCount() - v.getTickStarted())) * ESTIMATED_TICK_LENGTH) - - (now.toEpochMilli() - v.getLastClockUpdate().toEpochMilli()) / 1000.0; + if (canvasPoint != null) + { + Point canvasCenterPoint = new Point( + canvasPoint.getX() - textWidth / 2, + canvasPoint.getY() + textHeight / 2); + OverlayUtil.renderTextLocation(graphics, canvasCenterPoint, bombTimerString, color_code); + } - timeLeft = Math.max(0.0, timeLeft); - final String bombTimerString = TIME_LEFT_FORMATTER.format(timeLeft); - final int textWidth = graphics.getFontMetrics().stringWidth(bombTimerString); - final int textHeight = graphics.getFontMetrics().getAscent(); - final Point canvasPoint = Perspective.localToCanvas(client, localLoc.getX(), localLoc.getY(), v.getWorldLocation().getPlane()); - - if (canvasPoint != null) - { - Point canvasCenterPoint = new Point(canvasPoint.getX() - textWidth / 2, canvasPoint.getY() + textHeight / 2); - OverlayUtil.renderTextLocation(graphics, canvasCenterPoint, bombTimerString, color_code); - } - }); } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/CrystalBomb.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/CrystalBomb.java index eb54538881..0a63fa4b97 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/CrystalBomb.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/CrystalBomb.java @@ -32,13 +32,22 @@ import net.runelite.api.GameObject; import net.runelite.api.coords.WorldPoint; @Slf4j -@Getter(AccessLevel.PACKAGE) class CrystalBomb { + @Getter(AccessLevel.PACKAGE) private Instant plantedOn; + + @Getter(AccessLevel.PACKAGE) private Instant lastClockUpdate; + + @Getter(AccessLevel.PACKAGE) private int objectId; + + @Getter(AccessLevel.PACKAGE) private int tickStarted; + // + + @Getter(AccessLevel.PACKAGE) private WorldPoint worldLocation; CrystalBomb(GameObject gameObject, int startTick) diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/ProjectileContainer.java b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/ProjectileContainer.java deleted file mode 100644 index cb226d6262..0000000000 --- a/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/ProjectileContainer.java +++ /dev/null @@ -1,30 +0,0 @@ -package net.runelite.client.plugins.aoewarnings; - -import java.time.Instant; -import lombok.AccessLevel; -import lombok.Getter; -import lombok.Setter; -import net.runelite.api.Projectile; -import net.runelite.api.coords.LocalPoint; - -@Getter(AccessLevel.PACKAGE) -class ProjectileContainer -{ - private Projectile projectile; - private Instant startTime; - private AoeProjectileInfo aoeProjectileInfo; - private int lifetime; - private int finalTick; - @Setter(AccessLevel.PACKAGE) - private LocalPoint targetPoint; - - ProjectileContainer(Projectile projectile, Instant startTime, int lifetime, int finalTick) - { - this.projectile = projectile; - this.startTime = startTime; - this.targetPoint = null; - this.aoeProjectileInfo = AoeProjectileInfo.getById(projectile.getId()); - this.lifetime = lifetime; - this.finalTick = finalTick; - } -}