From 54dbe446edaeb9a3b3a465b17045a21f1b9ba119 Mon Sep 17 00:00:00 2001 From: Adam Date: Sun, 18 Jun 2017 12:57:29 -0400 Subject: [PATCH] runelite-client: add actor death event, use in bosstimer plugin --- .../src/main/java/net/runelite/api/Actor.java | 34 +++++++- .../net/runelite/client/callback/Hooks.java | 11 +++ .../runelite/client/events/ActorDeath.java | 42 ++++++++++ .../runelite/client/game/DeathChecker.java | 82 +++++++++++++++++++ .../client/plugins/bosstimer/BossTimers.java | 48 +++++++++++ .../plugins/bosstimer/BossTimersOverlay.java | 63 +------------- 6 files changed, 217 insertions(+), 63 deletions(-) create mode 100644 runelite-client/src/main/java/net/runelite/client/events/ActorDeath.java create mode 100644 runelite-client/src/main/java/net/runelite/client/game/DeathChecker.java diff --git a/runelite-api/src/main/java/net/runelite/api/Actor.java b/runelite-api/src/main/java/net/runelite/api/Actor.java index e12329d8e8..0a0464edbd 100644 --- a/runelite-api/src/main/java/net/runelite/api/Actor.java +++ b/runelite-api/src/main/java/net/runelite/api/Actor.java @@ -26,6 +26,7 @@ package net.runelite.api; import java.awt.Graphics2D; import java.awt.Polygon; +import java.util.Objects; import net.runelite.rs.api.CombatInfo1; import net.runelite.rs.api.CombatInfo2; import net.runelite.rs.api.CombatInfoList; @@ -35,7 +36,7 @@ import net.runelite.rs.api.Node; public abstract class Actor extends Renderable { - private Client client; + private final Client client; private net.runelite.rs.api.Actor actor; public Actor(Client client, net.runelite.rs.api.Actor actor) @@ -46,6 +47,37 @@ public abstract class Actor extends Renderable this.actor = actor; } + @Override + public int hashCode() + { + int hash = 5; + hash = 47 * hash + Objects.hashCode(this.client); + return hash; + } + + @Override + public boolean equals(Object obj) + { + if (this == obj) + { + return true; + } + if (obj == null) + { + return false; + } + if (getClass() != obj.getClass()) + { + return false; + } + final Actor other = (Actor) obj; + if (!Objects.equals(this.client, other.client)) + { + return false; + } + return true; + } + public abstract int getCombatLevel(); public abstract String getName(); diff --git a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java index 4c2c803eb3..a686483e31 100644 --- a/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java +++ b/runelite-client/src/main/java/net/runelite/client/callback/Hooks.java @@ -33,6 +33,7 @@ import net.runelite.client.events.MapRegionChanged; import net.runelite.client.events.MenuOptionClicked; import net.runelite.client.events.PlayerMenuOptionsChanged; import net.runelite.client.events.AnimationChanged; +import net.runelite.client.game.DeathChecker; import net.runelite.client.ui.overlay.OverlayRenderer; import net.runelite.rs.api.MainBufferProvider; import org.slf4j.Logger; @@ -43,6 +44,7 @@ public class Hooks private static final Logger logger = LoggerFactory.getLogger(Hooks.class); private static final RuneLite runelite = RuneLite.getRunelite(); + private static final DeathChecker death = new DeathChecker(runelite); public static void draw(Object provider, Graphics graphics, int x, int y) { @@ -52,6 +54,15 @@ public class Hooks OverlayRenderer renderer = runelite.getRenderer(); + try + { + death.check(); + } + catch (Exception ex) + { + logger.warn("error during death check", ex); + } + try { renderer.render(image); diff --git a/runelite-client/src/main/java/net/runelite/client/events/ActorDeath.java b/runelite-client/src/main/java/net/runelite/client/events/ActorDeath.java new file mode 100644 index 0000000000..a7e0537d3f --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/events/ActorDeath.java @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017, Adam + * 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.events; + +import net.runelite.api.Actor; + +public class ActorDeath +{ + private Actor actor; + + public Actor getActor() + { + return actor; + } + + public void setActor(Actor actor) + { + this.actor = actor; + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/game/DeathChecker.java b/runelite-client/src/main/java/net/runelite/client/game/DeathChecker.java new file mode 100644 index 0000000000..4ac6608065 --- /dev/null +++ b/runelite-client/src/main/java/net/runelite/client/game/DeathChecker.java @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2017, Adam + * 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.game; + +import java.lang.ref.WeakReference; +import net.runelite.api.Actor; +import net.runelite.api.Client; +import net.runelite.api.Player; +import net.runelite.client.RuneLite; +import net.runelite.client.events.ActorDeath; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class DeathChecker +{ + private static final Logger logger = LoggerFactory.getLogger(DeathChecker.class); + + private final RuneLite runelite; + private final Client client = RuneLite.getClient(); + private WeakReference last = new WeakReference<>(null); + + public DeathChecker(RuneLite runelite) + { + this.runelite = runelite; + } + + public void check() + { + Actor opponent = getOpponent(); + if (opponent == null || opponent.getHealthRatio() != 0) + { + return; + } + + Actor lastActor = last.get(); + if (lastActor != null && lastActor.equals(opponent)) + { + return; + } + + last = new WeakReference<>(opponent); + logger.debug("Actor {} has died", opponent.getName()); + + ActorDeath death = new ActorDeath(); + death.setActor(opponent); + + runelite.getEventBus().post(death); + } + + private Actor getOpponent() + { + Player player = client.getLocalPlayer(); + if (player == null) + { + return null; + } + + return player.getInteracting(); + } +} diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/BossTimers.java b/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/BossTimers.java index b0742f1904..c8a73d28f0 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/BossTimers.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/BossTimers.java @@ -25,12 +25,18 @@ */ package net.runelite.client.plugins.bosstimer; +import com.google.common.eventbus.Subscribe; import com.google.common.reflect.TypeToken; import com.google.gson.Gson; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.ArrayList; import java.util.List; +import net.runelite.api.Actor; +import net.runelite.client.events.ActorDeath; import net.runelite.client.plugins.Plugin; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayPosition; @@ -45,6 +51,7 @@ public class BossTimers extends Plugin private final BossTimersOverlay overlay = new BossTimersOverlay(this, OverlayPosition.TOP_LEFT, OverlayPriority.LOW); private final List bosses = loadBossData(); + private final List timers = new ArrayList<>(); @Override protected void startUp() throws Exception @@ -62,6 +69,35 @@ public class BossTimers extends Plugin return overlay; } + public List getTimers() + { + return timers; + } + + @Subscribe + public void onActorDeath(ActorDeath death) + { + Actor actor = death.getActor(); + + Boss boss = findBoss(actor.getName()); + if (boss == null) + { + return; + } + + if (findTimerFor(actor.getName()) != null) + { + return; + } + + logger.debug("Creating spawn timer for {} ({} seconds)", actor.getName(), boss.getSpawnTime()); + + Instant respawnTime = Instant.now().plus(boss.getSpawnTime(), ChronoUnit.SECONDS); + RespawnTimer respawnTimer = new RespawnTimer(boss, respawnTime); + + timers.add(respawnTimer); + } + public Boss findBoss(String name) { for (Boss boss : bosses) @@ -74,6 +110,18 @@ public class BossTimers extends Plugin return null; } + private RespawnTimer findTimerFor(String name) + { + for (RespawnTimer timer : timers) + { + if (timer.getBoss().getName().equals(name)) + { + return timer; + } + } + return null; + } + private static List loadBossData() { Gson gson = new Gson(); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/BossTimersOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/BossTimersOverlay.java index 5a0c53457a..a1d8d1b32b 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/BossTimersOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/bosstimer/BossTimersOverlay.java @@ -31,13 +31,9 @@ import java.awt.FontMetrics; import java.awt.Graphics2D; import java.time.Duration; import java.time.Instant; -import java.time.temporal.ChronoUnit; import java.util.ArrayList; import java.util.List; -import net.runelite.api.Actor; -import net.runelite.api.Client; import net.runelite.api.GameState; -import net.runelite.api.Player; import net.runelite.client.RuneLite; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayPosition; @@ -58,7 +54,6 @@ public class BossTimersOverlay extends Overlay private static final Color BACKGROUND = new Color(Color.gray.getRed(), Color.gray.getGreen(), Color.gray.getBlue(), 127); private final BossTimers bossTimers; - private final List timers = new ArrayList<>(); public BossTimersOverlay(BossTimers bossTimers, OverlayPosition position, OverlayPriority priority) { @@ -66,49 +61,6 @@ public class BossTimersOverlay extends Overlay this.bossTimers = bossTimers; } - private Actor getOpponent() - { - Client client = RuneLite.getClient(); - - Player player = client.getLocalPlayer(); - if (player == null) - { - return null; - } - - return player.getInteracting(); - } - - private void checkDead() - { - Actor actor = getOpponent(); - - if (actor == null || actor.getHealthRatio() != 0) - { - return; - } - - logger.debug("NPC {} has died", actor.getName()); - - Boss boss = bossTimers.findBoss(actor.getName()); - if (boss == null) - { - return; - } - - if (findTimerFor(actor.getName()) != null) - { - return; - } - - logger.debug("Creating spawn timer for {} ({} seconds)", actor.getName(), boss.getSpawnTime()); - - Instant respawnTime = Instant.now().plus(boss.getSpawnTime(), ChronoUnit.SECONDS); - RespawnTimer respawnTimer = new RespawnTimer(boss, respawnTime); - - timers.add(respawnTimer); - } - @Override public Dimension render(Graphics2D graphics) { @@ -117,8 +69,7 @@ public class BossTimersOverlay extends Overlay return null; } - // find new dead bosses - checkDead(); + List timers = bossTimers.getTimers(); if (timers.isEmpty()) { @@ -157,16 +108,4 @@ public class BossTimersOverlay extends Overlay return new Dimension(WIDTH, height); } - - private RespawnTimer findTimerFor(String name) - { - for (RespawnTimer timer : timers) - { - if (timer.getBoss().getName().equals(name)) - { - return timer; - } - } - return null; - } }