From 948444b77113f090879d0199d5267f0ec0b84774 Mon Sep 17 00:00:00 2001 From: Ganom Date: Thu, 18 Jul 2019 20:29:38 -0400 Subject: [PATCH] aoewarnings: rework AoE Warnings to use projectile spawned, rather than projectile moved. (#1032) * Rework AoE Warnings to use projectile spawned, rather than projectile moved. * Various Check Style fixes --- .../aoewarnings/AoeWarningOverlay.java | 36 ++--- .../plugins/aoewarnings/AoeWarningPlugin.java | 103 +++++++------- .../plugins/aoewarnings/BombOverlay.java | 131 +++++++----------- .../plugins/aoewarnings/CrystalBomb.java | 11 +- .../aoewarnings/ProjectileContainer.java | 30 ++++ 5 files changed, 159 insertions(+), 152 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/ProjectileContainer.java 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 1dfeb1c1a0..135a55a71d 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,14 +34,12 @@ import java.awt.Polygon; import java.awt.Rectangle; import java.time.Duration; import java.time.Instant; -import java.util.Iterator; -import java.util.Map; +import java.util.Set; 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; @@ -92,32 +90,35 @@ public class AoeWarningOverlay extends Overlay } Instant now = Instant.now(); - Map projectiles = plugin.getProjectiles(); - for (Iterator it = projectiles.values().iterator(); it.hasNext(); ) + Set projectiles = plugin.getProjectiles(); + projectiles.forEach(proj -> { - AoeProjectile aoeProjectile = it.next(); - Color color; - if (now.isAfter(aoeProjectile.getStartTime().plus(Duration.ofMillis(aoeProjectile.getProjectileLifetime())))) + if (proj.getTargetPoint() == null) { - it.remove(); - continue; + return; } - Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, aoeProjectile.getTargetPoint(), aoeProjectile.getAoeProjectileInfo().getAoeSize()); + Color color; + + if (now.isAfter(proj.getStartTime().plus(Duration.ofMillis(proj.getLifetime())))) + { + return; + } + + final Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, proj.getTargetPoint(), proj.getAoeProjectileInfo().getAoeSize()); if (tilePoly == null) { - continue; + return; } - // how far through the projectiles lifetime between 0-1. - double progress = (System.currentTimeMillis() - aoeProjectile.getStartTime().toEpochMilli()) / (double) aoeProjectile.getProjectileLifetime(); + final double progress = (System.currentTimeMillis() - proj.getStartTime().toEpochMilli()) / (double) proj.getLifetime(); - int tickProgress = aoeProjectile.getFinalTick() - client.getTickCount(); + final int tickProgress = proj.getFinalTick() - client.getTickCount(); int fillAlpha, outlineAlpha; if (plugin.isConfigFadeEnabled()) { - fillAlpha = (int) ((1 - progress) * FILL_START_ALPHA);//alpha drop off over lifetime + fillAlpha = (int) ((1 - progress) * FILL_START_ALPHA); outlineAlpha = (int) ((1 - progress) * OUTLINE_START_ALPHA); } else @@ -165,7 +166,8 @@ 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 8c7a98d914..55f5c6838a 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,9 +32,10 @@ import java.awt.Color; import java.time.Instant; import java.util.ArrayList; import java.util.HashMap; -import java.util.Iterator; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import javax.inject.Inject; import javax.inject.Singleton; import lombok.AccessLevel; @@ -44,7 +45,6 @@ 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,6 +57,7 @@ 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; @@ -79,7 +80,7 @@ public class AoeWarningPlugin extends Plugin @Getter(AccessLevel.PACKAGE) private final Map bombs = new HashMap<>(); @Getter(AccessLevel.PACKAGE) - private final Map projectiles = new HashMap<>(); + private final Set projectiles = new HashSet<>(); @Inject public AoeWarningConfig config; @Inject @@ -102,13 +103,6 @@ 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) @@ -168,12 +162,17 @@ 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(); @@ -197,6 +196,7 @@ 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,33 +209,52 @@ public class AoeWarningPlugin extends Plugin updateConfig(); } - private void onProjectileMoved(ProjectileMoved event) + private void onProjectileSpawned(ProjectileSpawned event) { - Projectile projectile = event.getProjectile(); + final Projectile projectile = event.getProjectile(); - int projectileId = projectile.getId(); - int projectileLifetime = this.delay + (projectile.getRemainingCycles() * 20); + if (AoeProjectileInfo.getById(projectile.getId()) == null) + { + return; + } + + final int id = projectile.getId(); + final int lifetime = this.delay + (projectile.getRemainingCycles() * 20); int ticksRemaining = projectile.getRemainingCycles() / 30; - if (!isTickTimersEnabledForProjectileID(projectileId)) + if (!isTickTimersEnabledForProjectileID(id)) { ticksRemaining = 0; } - int tickCycle = client.getTickCount() + ticksRemaining; - AoeProjectileInfo aoeProjectileInfo = AoeProjectileInfo.getById(projectileId); - if (aoeProjectileInfo != null - && isConfigEnabledForProjectileId(projectileId, false)) + final int tickCycle = client.getTickCount() + ticksRemaining; + if (isConfigEnabledForProjectileId(id, false)) { - LocalPoint targetPoint = event.getPosition(); - AoeProjectile aoeProjectile = new AoeProjectile(Instant.now(), targetPoint, aoeProjectileInfo, projectileLifetime, tickCycle); - projectiles.put(projectile, aoeProjectile); + projectiles.add(new ProjectileContainer(projectile, Instant.now(), lifetime, tickCycle)); - if (this.aoeNotifyAll || isConfigEnabledForProjectileId(projectileId, true)) + if (this.aoeNotifyAll || isConfigEnabledForProjectileId(id, 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(); @@ -258,7 +277,6 @@ public class AoeWarningPlugin extends Plugin CrystalSpike.add(wp); break; case NullObjectID.NULL_26690: - //Wintertodt Snowfall if (this.configWintertodtEnabled) { WintertodtSnowFall.add(wp); @@ -288,11 +306,7 @@ public class AoeWarningPlugin extends Plugin CrystalSpike.remove(wp); break; case NullObjectID.NULL_26690: - //Wintertodt Snowfall - if (this.configWintertodtEnabled) - { - WintertodtSnowFall.remove(wp); - } + WintertodtSnowFall.remove(wp); break; } } @@ -307,10 +321,11 @@ public class AoeWarningPlugin extends Plugin private void onGameTick(GameTick event) { + LightningTrail.clear(); + if (this.configLightningTrail) { - LightningTrail.clear(); - for (GraphicsObject o : client.getGraphicsObjects()) + client.getGraphicsObjects().forEach(o -> { if (o.getId() == GraphicID.OLM_LIGHTNING) { @@ -321,34 +336,29 @@ public class AoeWarningPlugin extends Plugin notifier.notify("Lightning!"); } } - } + }); } - for (Map.Entry entry : bombs.entrySet()) + bombs.forEach((k, v) -> { - 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. - } + v.bombClockUpdate(); + }); } private void purgeBombs(Map bombs) { - Iterator> it = bombs.entrySet().iterator(); Tile[][][] tiles = client.getScene().getTiles(); - while (it.hasNext()) + bombs.forEach((k, v) -> { - Map.Entry entry = it.next(); - WorldPoint world = entry.getKey(); - LocalPoint local = LocalPoint.fromWorld(client, world); + LocalPoint local = LocalPoint.fromWorld(client, k); if (local == null) { return; } - Tile tile = tiles[world.getPlane()][local.getSceneX()][local.getSceneY()]; + Tile tile = tiles[k.getPlane()][local.getSceneX()][local.getSceneY()]; GameObject[] objects = tile.getGameObjects(); boolean containsObjects = false; @@ -362,10 +372,9 @@ public class AoeWarningPlugin extends Plugin if (!containsObjects) { - it.remove(); + bombs.remove(k, v); } - - } + }); } 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 d51b52aee3..9d56056d93 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,13 +33,11 @@ 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; @@ -55,24 +53,13 @@ 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); @@ -99,84 +86,72 @@ public class BombOverlay extends Overlay { if (plugin.isConfigbombDisplay()) { - drawBombs(graphics); + drawDangerZone(graphics); } return null; } - private void drawBombs(Graphics2D graphics) - //I can condense drawDangerZone into this. Ambivalent though. + private void drawDangerZone(Graphics2D graphics) { - for (Map.Entry entry : plugin.getBombs().entrySet()) + final WorldPoint loc = client.getLocalPlayer().getWorldLocation(); + plugin.getBombs().forEach((k, v) -> { - CrystalBomb bomb = entry.getValue(); - drawDangerZone(graphics, bomb); - } - } + LocalPoint localLoc = LocalPoint.fromWorld(client, v.getWorldLocation()); - 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. + if (localLoc == null) + { + return; + } - 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); + final double distance_x = Math.abs(v.getWorldLocation().getX() - loc.getX()); + final double distance_y = Math.abs(v.getWorldLocation().getY() - loc.getY()); - 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); - } + Color color_code = Color.decode(SAFE); - 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 (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); - 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()); + 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); + } - if (canvasPoint != null) - { - Point canvasCenterPoint = new Point( - canvasPoint.getX() - textWidth / 2, - canvasPoint.getY() + textHeight / 2); - OverlayUtil.renderTextLocation(graphics, canvasCenterPoint, bombTimerString, color_code); - } + final Instant now = Instant.now(); + double timeLeft = ((BOMB_DETONATE_TIME - (client.getTickCount() - v.getTickStarted())) * ESTIMATED_TICK_LENGTH) - + (now.toEpochMilli() - v.getLastClockUpdate().toEpochMilli()) / 1000.0; + 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 0a63fa4b97..eb54538881 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,22 +32,13 @@ 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 new file mode 100644 index 0000000000..cb226d6262 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/plugins/aoewarnings/ProjectileContainer.java @@ -0,0 +1,30 @@ +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; + } +}