Add notification on NPC spawn

This commit is contained in:
William Maga
2019-01-19 03:45:31 -07:00
parent fc200ee651
commit 98ca1bc056
5 changed files with 96 additions and 26 deletions

View File

@@ -24,6 +24,7 @@
*/ */
package net.runelite.client.plugins.npchighlight; package net.runelite.client.plugins.npchighlight;
import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import lombok.Getter; import lombok.Getter;
@@ -76,4 +77,12 @@ class MemorizedNpc
this.npcSize = composition.getSize(); this.npcSize = composition.getSize();
} }
} }
public double getSecondsFromRespawn(int tickCount, Instant lastTickUpdate)
{
final Instant now = Instant.now();
final double baseTick = NpcIndicatorsPlugin.ESTIMATED_TICK_LENGTH * (diedOnTick + respawnTime - tickCount);
final double sinceLast = (now.toEpochMilli() - lastTickUpdate.toEpochMilli()) / 1000.0;
return Math.max(0.0, baseTick - sinceLast);
}
} }

View File

@@ -107,4 +107,24 @@ public interface NpcIndicatorsConfig extends Config
{ {
return false; return false;
} }
@ConfigItem(
position = 7,
keyName = "notifyOnRespawn",
name = "Notify on Respawn",
description = "Enable notification on respawn")
default boolean getNotifyOnRespawn()
{
return false;
}
@ConfigItem(
position = 8,
keyName = "notifyOnRespawnDelay",
name = "Notification Delay",
description = "Notify when NPC is x ms from respawning")
default int getNotifyOnRespawnDelay()
{
return -1;
}
} }

View File

@@ -29,14 +29,11 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Splitter; import com.google.common.base.Splitter;
import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList;
import com.google.inject.Provides; import com.google.inject.Provides;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.AccessLevel; import lombok.AccessLevel;
import lombok.Getter; import lombok.Getter;
@@ -56,6 +53,7 @@ import net.runelite.api.events.GraphicsObjectCreated;
import net.runelite.api.events.MenuOptionClicked; import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.NpcDespawned; import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned; import net.runelite.api.events.NpcSpawned;
import net.runelite.client.Notifier;
import net.runelite.client.callback.ClientThread; import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager; import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe; import net.runelite.client.eventbus.Subscribe;
@@ -76,6 +74,11 @@ public class NpcIndicatorsPlugin extends Plugin
{ {
private static final int MAX_ACTOR_VIEW_RANGE = 15; private static final int MAX_ACTOR_VIEW_RANGE = 15;
public static final NumberFormat TIME_LEFT_FORMATTER = DecimalFormat.getInstance(Locale.US);
// Estimated time of a game tick in seconds
public static final double ESTIMATED_TICK_LENGTH = 0.6;
// Option added to NPC menu // Option added to NPC menu
private static final String TAG = "Tag"; private static final String TAG = "Tag";
@@ -85,6 +88,11 @@ public class NpcIndicatorsPlugin extends Plugin
// Regex for splitting the hidden items in the config. // Regex for splitting the hidden items in the config.
private static final Splitter COMMA_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults(); private static final Splitter COMMA_SPLITTER = Splitter.on(",").omitEmptyStrings().trimResults();
static
{
((DecimalFormat)TIME_LEFT_FORMATTER).applyPattern("#0.0");
}
@Inject @Inject
private Client client; private Client client;
@@ -112,12 +120,21 @@ public class NpcIndicatorsPlugin extends Plugin
@Inject @Inject
private ClientThread clientThread; private ClientThread clientThread;
@Inject
private Notifier notifier;
/** /**
* NPCs to highlight * NPCs to highlight
*/ */
@Getter(AccessLevel.PACKAGE) @Getter(AccessLevel.PACKAGE)
private final Set<NPC> highlightedNpcs = new HashSet<>(); private final Set<NPC> highlightedNpcs = new HashSet<>();
/**
* NPCs to notify when close to spawning
*/
@Getter(AccessLevel.PACKAGE)
private final Set<MemorizedNpc> pendingNotificationNpcs = new HashSet<>();
/** /**
* Dead NPCs that should be displayed with a respawn indicator if the config is on. * Dead NPCs that should be displayed with a respawn indicator if the config is on.
*/ */
@@ -203,6 +220,7 @@ public class NpcIndicatorsPlugin extends Plugin
overlayManager.remove(npcSceneOverlay); overlayManager.remove(npcSceneOverlay);
overlayManager.remove(npcMinimapOverlay); overlayManager.remove(npcMinimapOverlay);
deadNpcsToDisplay.clear(); deadNpcsToDisplay.clear();
pendingNotificationNpcs.clear();
memorizedNpcs.clear(); memorizedNpcs.clear();
spawnedNpcsThisTick.clear(); spawnedNpcsThisTick.clear();
despawnedNpcsThisTick.clear(); despawnedNpcsThisTick.clear();
@@ -220,6 +238,7 @@ public class NpcIndicatorsPlugin extends Plugin
{ {
highlightedNpcs.clear(); highlightedNpcs.clear();
deadNpcsToDisplay.clear(); deadNpcsToDisplay.clear();
pendingNotificationNpcs.clear();
memorizedNpcs.forEach((id, npc) -> npc.setDiedOnTick(-1)); memorizedNpcs.forEach((id, npc) -> npc.setDiedOnTick(-1));
lastPlayerLocation = null; lastPlayerLocation = null;
skipNextSpawnCheck = true; skipNextSpawnCheck = true;
@@ -319,6 +338,12 @@ public class NpcIndicatorsPlugin extends Plugin
if (memorizedNpcs.containsKey(npc.getIndex())) if (memorizedNpcs.containsKey(npc.getIndex()))
{ {
despawnedNpcsThisTick.add(npc); despawnedNpcsThisTick.add(npc);
MemorizedNpc mn = memorizedNpcs.get(npc.getIndex());
if (!mn.getPossibleRespawnLocations().isEmpty())
{
pendingNotificationNpcs.add(mn);
}
} }
highlightedNpcs.remove(npc); highlightedNpcs.remove(npc);
@@ -340,6 +365,7 @@ public class NpcIndicatorsPlugin extends Plugin
{ {
removeOldHighlightedRespawns(); removeOldHighlightedRespawns();
validateSpawnedNpcs(); validateSpawnedNpcs();
checkNotifyNpcs();
lastTickUpdate = Instant.now(); lastTickUpdate = Instant.now();
lastPlayerLocation = client.getLocalPlayer().getWorldLocation(); lastPlayerLocation = client.getLocalPlayer().getWorldLocation();
} }
@@ -477,6 +503,29 @@ public class NpcIndicatorsPlugin extends Plugin
} }
} }
private void checkNotifyNpcs()
{
if (!config.getNotifyOnRespawn())
{
return;
}
final double notifyDelay = ((double)config.getNotifyOnRespawnDelay()) / 1000;
final int tickCount = client.getTickCount();
final String notifyDelayStr = notifyDelay > 0
? " is less than " + TIME_LEFT_FORMATTER.format(notifyDelay) + " seconds from respawn"
: " respawned.";
for (MemorizedNpc npc : pendingNotificationNpcs)
{
if (npc.getSecondsFromRespawn(tickCount, lastTickUpdate) <= notifyDelay)
{
pendingNotificationNpcs.remove(npc);
notifier.notify(npc.getNpcName() + notifyDelayStr);
}
}
}
private void validateSpawnedNpcs() private void validateSpawnedNpcs()
{ {
if (skipNextSpawnCheck) if (skipNextSpawnCheck)
@@ -485,6 +534,7 @@ public class NpcIndicatorsPlugin extends Plugin
} }
else else
{ {
for (NPC npc : despawnedNpcsThisTick) for (NPC npc : despawnedNpcsThisTick)
{ {
if (!teleportGraphicsObjectSpawnedThisTick.isEmpty()) if (!teleportGraphicsObjectSpawnedThisTick.isEmpty())

View File

@@ -30,10 +30,6 @@ import java.awt.Color;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Polygon; import java.awt.Polygon;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.Instant;
import java.util.Locale;
import javax.inject.Inject; import javax.inject.Inject;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.NPC; import net.runelite.api.NPC;
@@ -53,16 +49,6 @@ public class NpcSceneOverlay extends Overlay
// a dark background // a dark background
private static final Color TEXT_COLOR = Color.WHITE; private static final Color TEXT_COLOR = Color.WHITE;
// Estimated time of a game tick in seconds
private static final double ESTIMATED_TICK_LENGTH = 0.6;
private static final NumberFormat TIME_LEFT_FORMATTER = DecimalFormat.getInstance(Locale.US);
static
{
((DecimalFormat)TIME_LEFT_FORMATTER).applyPattern("#0.0");
}
private final Client client; private final Client client;
private final NpcIndicatorsConfig config; private final NpcIndicatorsConfig config;
private final NpcIndicatorsPlugin plugin; private final NpcIndicatorsPlugin plugin;
@@ -121,11 +107,10 @@ public class NpcSceneOverlay extends Overlay
OverlayUtil.renderPolygon(graphics, poly, color); OverlayUtil.renderPolygon(graphics, poly, color);
} }
final Instant now = Instant.now(); final String timeLeftStr = NpcIndicatorsPlugin.TIME_LEFT_FORMATTER.format(npc.getSecondsFromRespawn(
final double baseTick = ((npc.getDiedOnTick() + npc.getRespawnTime()) - client.getTickCount()) * ESTIMATED_TICK_LENGTH; client.getTickCount(),
final double sinceLast = (now.toEpochMilli() - plugin.getLastTickUpdate().toEpochMilli()) / 1000.0; plugin.getLastTickUpdate()
final double timeLeft = Math.max(0.0, baseTick - sinceLast); ));
final String timeLeftStr = TIME_LEFT_FORMATTER.format(timeLeft);
final int textWidth = graphics.getFontMetrics().stringWidth(timeLeftStr); final int textWidth = graphics.getFontMetrics().stringWidth(timeLeftStr);
final int textHeight = graphics.getFontMetrics().getAscent(); final int textHeight = graphics.getFontMetrics().getAscent();

View File

@@ -33,6 +33,8 @@ import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject; import javax.inject.Inject;
import net.runelite.api.Client; import net.runelite.api.Client;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import net.runelite.client.Notifier;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
@@ -55,6 +57,10 @@ public class NpcIndicatorsPluginTest
@Bind @Bind
private NpcIndicatorsConfig npcIndicatorsConfig; private NpcIndicatorsConfig npcIndicatorsConfig;
@Mock
@Bind
private Notifier notifier;
@Inject @Inject
private NpcIndicatorsPlugin npcIndicatorsPlugin; private NpcIndicatorsPlugin npcIndicatorsPlugin;