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
This commit is contained in:
Ganom
2019-07-18 20:29:38 -04:00
committed by GitHub
parent b228ac322b
commit 948444b771
5 changed files with 159 additions and 152 deletions

View File

@@ -34,14 +34,12 @@ import java.awt.Polygon;
import java.awt.Rectangle; import java.awt.Rectangle;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Iterator; import java.util.Set;
import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.Perspective; import net.runelite.api.Perspective;
import net.runelite.api.Point; import net.runelite.api.Point;
import net.runelite.api.Projectile;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayLayer;
@@ -92,32 +90,35 @@ public class AoeWarningOverlay extends Overlay
} }
Instant now = Instant.now(); Instant now = Instant.now();
Map<Projectile, AoeProjectile> projectiles = plugin.getProjectiles(); Set<ProjectileContainer> projectiles = plugin.getProjectiles();
for (Iterator<AoeProjectile> it = projectiles.values().iterator(); it.hasNext(); ) projectiles.forEach(proj ->
{ {
AoeProjectile aoeProjectile = it.next(); if (proj.getTargetPoint() == null)
Color color;
if (now.isAfter(aoeProjectile.getStartTime().plus(Duration.ofMillis(aoeProjectile.getProjectileLifetime()))))
{ {
it.remove(); return;
continue;
} }
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) if (tilePoly == null)
{ {
continue; return;
} }
// how far through the projectiles lifetime between 0-1. final double progress = (System.currentTimeMillis() - proj.getStartTime().toEpochMilli()) / (double) proj.getLifetime();
double progress = (System.currentTimeMillis() - aoeProjectile.getStartTime().toEpochMilli()) / (double) aoeProjectile.getProjectileLifetime();
int tickProgress = aoeProjectile.getFinalTick() - client.getTickCount(); final int tickProgress = proj.getFinalTick() - client.getTickCount();
int fillAlpha, outlineAlpha; int fillAlpha, outlineAlpha;
if (plugin.isConfigFadeEnabled()) 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); outlineAlpha = (int) ((1 - progress) * OUTLINE_START_ALPHA);
} }
else else
@@ -165,7 +166,8 @@ public class AoeWarningOverlay extends Overlay
graphics.setColor(new Color(setAlphaComponent(plugin.getOverlayColor().getRGB(), fillAlpha), true)); graphics.setColor(new Color(setAlphaComponent(plugin.getOverlayColor().getRGB(), fillAlpha), true));
graphics.fillPolygon(tilePoly); graphics.fillPolygon(tilePoly);
} });
projectiles.removeIf(proj -> now.isAfter(proj.getStartTime().plus(Duration.ofMillis(proj.getLifetime()))));
return null; return null;
} }

View File

@@ -32,9 +32,10 @@ import java.awt.Color;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.AccessLevel; import lombok.AccessLevel;
@@ -44,7 +45,6 @@ import net.runelite.api.Client;
import net.runelite.api.GameObject; import net.runelite.api.GameObject;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.GraphicID; import net.runelite.api.GraphicID;
import net.runelite.api.GraphicsObject;
import net.runelite.api.NullObjectID; import net.runelite.api.NullObjectID;
import net.runelite.api.ObjectID; import net.runelite.api.ObjectID;
import net.runelite.api.Projectile; 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.GameStateChanged;
import net.runelite.api.events.GameTick; import net.runelite.api.events.GameTick;
import net.runelite.api.events.ProjectileMoved; import net.runelite.api.events.ProjectileMoved;
import net.runelite.api.events.ProjectileSpawned;
import net.runelite.client.Notifier; import net.runelite.client.Notifier;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus; import net.runelite.client.eventbus.EventBus;
@@ -79,7 +80,7 @@ public class AoeWarningPlugin extends Plugin
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private final Map<WorldPoint, CrystalBomb> bombs = new HashMap<>(); private final Map<WorldPoint, CrystalBomb> bombs = new HashMap<>();
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private final Map<Projectile, AoeProjectile> projectiles = new HashMap<>(); private final Set<ProjectileContainer> projectiles = new HashSet<>();
@Inject @Inject
public AoeWarningConfig config; public AoeWarningConfig config;
@Inject @Inject
@@ -102,13 +103,6 @@ public class AoeWarningPlugin extends Plugin
private List<WorldPoint> CrystalSpike = new ArrayList<>(); private List<WorldPoint> CrystalSpike = new ArrayList<>();
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private List<WorldPoint> WintertodtSnowFall = new ArrayList<>(); private List<WorldPoint> WintertodtSnowFall = new ArrayList<>();
@Provides
AoeWarningConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(AoeWarningConfig.class);
}
// Config values // Config values
private boolean aoeNotifyAll; private boolean aoeNotifyAll;
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
@@ -168,12 +162,17 @@ public class AoeWarningPlugin extends Plugin
private boolean configDemonicGorillaEnabled; private boolean configDemonicGorillaEnabled;
private boolean configDemonicGorillaNotifyEnabled; private boolean configDemonicGorillaNotifyEnabled;
@Provides
AoeWarningConfig getConfig(ConfigManager configManager)
{
return configManager.getConfig(AoeWarningConfig.class);
}
@Override @Override
protected void startUp() throws Exception protected void startUp() throws Exception
{ {
updateConfig(); updateConfig();
addSubscriptions(); addSubscriptions();
overlayManager.add(coreOverlay); overlayManager.add(coreOverlay);
overlayManager.add(bombOverlay); overlayManager.add(bombOverlay);
reset(); reset();
@@ -197,6 +196,7 @@ public class AoeWarningPlugin extends Plugin
eventbus.subscribe(GameObjectDespawned.class, this, this::onGameObjectDespawned); eventbus.subscribe(GameObjectDespawned.class, this, this::onGameObjectDespawned);
eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged); eventbus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
eventbus.subscribe(GameTick.class, this, this::onGameTick); eventbus.subscribe(GameTick.class, this, this::onGameTick);
eventbus.subscribe(ProjectileSpawned.class, this, this::onProjectileSpawned);
} }
private void onConfigChanged(ConfigChanged event) private void onConfigChanged(ConfigChanged event)
@@ -209,33 +209,52 @@ public class AoeWarningPlugin extends Plugin
updateConfig(); updateConfig();
} }
private void onProjectileMoved(ProjectileMoved event) private void onProjectileSpawned(ProjectileSpawned event)
{ {
Projectile projectile = event.getProjectile(); final Projectile projectile = event.getProjectile();
int projectileId = projectile.getId(); if (AoeProjectileInfo.getById(projectile.getId()) == null)
int projectileLifetime = this.delay + (projectile.getRemainingCycles() * 20); {
return;
}
final int id = projectile.getId();
final int lifetime = this.delay + (projectile.getRemainingCycles() * 20);
int ticksRemaining = projectile.getRemainingCycles() / 30; int ticksRemaining = projectile.getRemainingCycles() / 30;
if (!isTickTimersEnabledForProjectileID(projectileId)) if (!isTickTimersEnabledForProjectileID(id))
{ {
ticksRemaining = 0; ticksRemaining = 0;
} }
int tickCycle = client.getTickCount() + ticksRemaining; final int tickCycle = client.getTickCount() + ticksRemaining;
AoeProjectileInfo aoeProjectileInfo = AoeProjectileInfo.getById(projectileId); if (isConfigEnabledForProjectileId(id, false))
if (aoeProjectileInfo != null
&& isConfigEnabledForProjectileId(projectileId, false))
{ {
LocalPoint targetPoint = event.getPosition(); projectiles.add(new ProjectileContainer(projectile, Instant.now(), lifetime, tickCycle));
AoeProjectile aoeProjectile = new AoeProjectile(Instant.now(), targetPoint, aoeProjectileInfo, projectileLifetime, tickCycle);
projectiles.put(projectile, aoeProjectile);
if (this.aoeNotifyAll || isConfigEnabledForProjectileId(projectileId, true)) if (this.aoeNotifyAll || isConfigEnabledForProjectileId(id, true))
{ {
notifier.notify("AoE attack detected!"); 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) private void onGameObjectSpawned(GameObjectSpawned event)
{ {
final GameObject gameObject = event.getGameObject(); final GameObject gameObject = event.getGameObject();
@@ -258,7 +277,6 @@ public class AoeWarningPlugin extends Plugin
CrystalSpike.add(wp); CrystalSpike.add(wp);
break; break;
case NullObjectID.NULL_26690: case NullObjectID.NULL_26690:
//Wintertodt Snowfall
if (this.configWintertodtEnabled) if (this.configWintertodtEnabled)
{ {
WintertodtSnowFall.add(wp); WintertodtSnowFall.add(wp);
@@ -288,11 +306,7 @@ public class AoeWarningPlugin extends Plugin
CrystalSpike.remove(wp); CrystalSpike.remove(wp);
break; break;
case NullObjectID.NULL_26690: case NullObjectID.NULL_26690:
//Wintertodt Snowfall WintertodtSnowFall.remove(wp);
if (this.configWintertodtEnabled)
{
WintertodtSnowFall.remove(wp);
}
break; break;
} }
} }
@@ -307,10 +321,11 @@ public class AoeWarningPlugin extends Plugin
private void onGameTick(GameTick event) private void onGameTick(GameTick event)
{ {
LightningTrail.clear();
if (this.configLightningTrail) if (this.configLightningTrail)
{ {
LightningTrail.clear(); client.getGraphicsObjects().forEach(o ->
for (GraphicsObject o : client.getGraphicsObjects())
{ {
if (o.getId() == GraphicID.OLM_LIGHTNING) if (o.getId() == GraphicID.OLM_LIGHTNING)
{ {
@@ -321,34 +336,29 @@ public class AoeWarningPlugin extends Plugin
notifier.notify("Lightning!"); notifier.notify("Lightning!");
} }
} }
} });
} }
for (Map.Entry<WorldPoint, CrystalBomb> entry : bombs.entrySet()) bombs.forEach((k, v) ->
{ {
CrystalBomb bomb = entry.getValue(); v.bombClockUpdate();
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<WorldPoint, CrystalBomb> bombs) private void purgeBombs(Map<WorldPoint, CrystalBomb> bombs)
{ {
Iterator<Map.Entry<WorldPoint, CrystalBomb>> it = bombs.entrySet().iterator();
Tile[][][] tiles = client.getScene().getTiles(); Tile[][][] tiles = client.getScene().getTiles();
while (it.hasNext()) bombs.forEach((k, v) ->
{ {
Map.Entry<WorldPoint, CrystalBomb> entry = it.next(); LocalPoint local = LocalPoint.fromWorld(client, k);
WorldPoint world = entry.getKey();
LocalPoint local = LocalPoint.fromWorld(client, world);
if (local == null) if (local == null)
{ {
return; return;
} }
Tile tile = tiles[world.getPlane()][local.getSceneX()][local.getSceneY()]; Tile tile = tiles[k.getPlane()][local.getSceneX()][local.getSceneY()];
GameObject[] objects = tile.getGameObjects(); GameObject[] objects = tile.getGameObjects();
boolean containsObjects = false; boolean containsObjects = false;
@@ -362,10 +372,9 @@ public class AoeWarningPlugin extends Plugin
if (!containsObjects) if (!containsObjects)
{ {
it.remove(); bombs.remove(k, v);
} }
});
}
} }
private boolean isTickTimersEnabledForProjectileID(int projectileId) private boolean isTickTimersEnabledForProjectileID(int projectileId)

View File

@@ -33,13 +33,11 @@ import java.text.DecimalFormat;
import java.text.NumberFormat; import java.text.NumberFormat;
import java.time.Instant; import java.time.Instant;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.Perspective; import net.runelite.api.Perspective;
import net.runelite.api.Player;
import net.runelite.api.Point; import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
@@ -55,24 +53,13 @@ public class BombOverlay extends Overlay
{ {
private static final String SAFE = "#00cc00"; private static final String SAFE = "#00cc00";
//safe
private static final String CAUTION = "#ffff00"; private static final String CAUTION = "#ffff00";
//1 tile in range (minor damage)
private static final String WARNING = "#ff9933"; private static final String WARNING = "#ff9933";
//2 tiles in range (moderate damage)
private static final String DANGER = "#ff6600"; private static final String DANGER = "#ff6600";
//3 tiles in range/adjacent to bomb (major damage)
private static final String LETHAL = "#cc0000"; 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_AOE = 7;
private static final int BOMB_DETONATE_TIME = 8; 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; 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 = private static final NumberFormat TIME_LEFT_FORMATTER =
DecimalFormat.getInstance(Locale.US); DecimalFormat.getInstance(Locale.US);
@@ -99,84 +86,72 @@ public class BombOverlay extends Overlay
{ {
if (plugin.isConfigbombDisplay()) if (plugin.isConfigbombDisplay())
{ {
drawBombs(graphics); drawDangerZone(graphics);
} }
return null; return null;
} }
private void drawBombs(Graphics2D graphics) private void drawDangerZone(Graphics2D graphics)
//I can condense drawDangerZone into this. Ambivalent though.
{ {
for (Map.Entry<WorldPoint, CrystalBomb> entry : plugin.getBombs().entrySet()) final WorldPoint loc = client.getLocalPlayer().getWorldLocation();
plugin.getBombs().forEach((k, v) ->
{ {
CrystalBomb bomb = entry.getValue(); LocalPoint localLoc = LocalPoint.fromWorld(client, v.getWorldLocation());
drawDangerZone(graphics, bomb);
}
}
private void drawDangerZone(Graphics2D graphics, CrystalBomb bomb) if (localLoc == null)
{ {
final Player localPlayer = client.getLocalPlayer(); return;
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 (distance_x < 1 && distance_y < 1) final double distance_x = Math.abs(v.getWorldLocation().getX() - loc.getX());
{ final double distance_y = Math.abs(v.getWorldLocation().getY() - loc.getY());
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);
if (poly != null) Color color_code = Color.decode(SAFE);
{
//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);
}
Instant now = Instant.now(); if (distance_x < 1 && distance_y < 1)
double timeLeft = ((BOMB_DETONATE_TIME - (client.getTickCount() - {
bomb.getTickStarted())) * ESTIMATED_TICK_LENGTH) - color_code = Color.decode(LETHAL);
(now.toEpochMilli() - bomb.getLastClockUpdate().toEpochMilli()) / 1000.0; }
//divided by 1000.00 because of milliseconds :) 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); if (poly != null)
String bombTimerString = TIME_LEFT_FORMATTER.format(timeLeft); {
int textWidth = graphics.getFontMetrics().stringWidth(bombTimerString); graphics.setColor(color_code);
int textHeight = graphics.getFontMetrics().getAscent(); graphics.setStroke(new BasicStroke(1));
Point canvasPoint = Perspective.localToCanvas(client, localLoc.getX(), graphics.drawPolygon(poly);
localLoc.getY(), bomb.getWorldLocation().getPlane()); graphics.setColor(new Color(0, 0, 0, 10));
graphics.fillPolygon(poly);
}
if (canvasPoint != null) final Instant now = Instant.now();
{ double timeLeft = ((BOMB_DETONATE_TIME - (client.getTickCount() - v.getTickStarted())) * ESTIMATED_TICK_LENGTH) -
Point canvasCenterPoint = new Point( (now.toEpochMilli() - v.getLastClockUpdate().toEpochMilli()) / 1000.0;
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);
}
});
} }
} }

View File

@@ -32,22 +32,13 @@ import net.runelite.api.GameObject;
import net.runelite.api.coords.WorldPoint; import net.runelite.api.coords.WorldPoint;
@Slf4j @Slf4j
@Getter(AccessLevel.PACKAGE)
class CrystalBomb class CrystalBomb
{ {
@Getter(AccessLevel.PACKAGE)
private Instant plantedOn; private Instant plantedOn;
@Getter(AccessLevel.PACKAGE)
private Instant lastClockUpdate; private Instant lastClockUpdate;
@Getter(AccessLevel.PACKAGE)
private int objectId; private int objectId;
@Getter(AccessLevel.PACKAGE)
private int tickStarted; private int tickStarted;
//
@Getter(AccessLevel.PACKAGE)
private WorldPoint worldLocation; private WorldPoint worldLocation;
CrystalBomb(GameObject gameObject, int startTick) CrystalBomb(GameObject gameObject, int startTick)

View File

@@ -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;
}
}