Merge pull request #1818 from open-osrs/spawn

* Add notification on NPC spawn

Co-authored-by: William Maga <magaw@mymacewan.ca>
Co-authored-by: Owain van Brakel <owain.vanbrakel@gmail.com>
This commit is contained in:
Kyle
2020-01-29 12:10:56 +00:00
committed by GitHub
4 changed files with 106 additions and 16 deletions

View File

@@ -129,4 +129,24 @@ public interface NpcIndicatorsConfig extends Config
{
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,6 +29,9 @@ import com.google.common.annotations.VisibleForTesting;
import com.google.common.collect.ImmutableSet;
import com.google.inject.Provides;
import java.awt.Color;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Collections;
@@ -38,6 +41,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
@@ -61,6 +65,7 @@ import net.runelite.api.events.MenuOptionClicked;
import net.runelite.api.events.NpcDefinitionChanged;
import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned;
import net.runelite.client.Notifier;
import net.runelite.api.util.Text;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.config.ConfigManager;
@@ -86,6 +91,16 @@ public class NpcIndicatorsPlugin extends Plugin
{
private static final int MAX_ACTOR_VIEW_RANGE = 15;
// 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.getDefault());
static
{
((DecimalFormat)TIME_LEFT_FORMATTER).applyPattern("#0.0");
}
// Option added to NPC menu
private static final String TAG = "Tag";
private static final String UNTAG = "Un-tag";
@@ -125,12 +140,21 @@ public class NpcIndicatorsPlugin extends Plugin
@Setter(AccessLevel.PACKAGE)
private boolean hotKeyPressed = false;
@Inject
private Notifier notifier;
/**
* NPCs to highlight
*/
@Getter(AccessLevel.PACKAGE)
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.
*/
@@ -206,6 +230,10 @@ public class NpcIndicatorsPlugin extends Plugin
private boolean highlightMenuNames;
@Getter(AccessLevel.PACKAGE)
private boolean showRespawnTimer;
@Getter(AccessLevel.PACKAGE)
private boolean getNotifyOnRespawn;
@Getter(AccessLevel.PACKAGE)
private int getNotifyOnRespawnDelay;
@Provides
NpcIndicatorsConfig provideConfig(ConfigManager configManager)
@@ -235,6 +263,7 @@ public class NpcIndicatorsPlugin extends Plugin
overlayManager.remove(npcSceneOverlay);
overlayManager.remove(npcMinimapOverlay);
deadNpcsToDisplay.clear();
pendingNotificationNpcs.clear();
memorizedNpcs.clear();
spawnedNpcsThisTick.clear();
despawnedNpcsThisTick.clear();
@@ -252,6 +281,7 @@ public class NpcIndicatorsPlugin extends Plugin
{
highlightedNpcs.clear();
deadNpcsToDisplay.clear();
pendingNotificationNpcs.clear();
memorizedNpcs.forEach((id, npc) -> npc.setDiedOnTick(-1));
lastPlayerLocation = null;
skipNextSpawnCheck = true;
@@ -388,6 +418,12 @@ public class NpcIndicatorsPlugin extends Plugin
if (memorizedNpcs.containsKey(npc.getIndex()))
{
despawnedNpcsThisTick.add(npc);
MemorizedNpc mn = memorizedNpcs.get(npc.getIndex());
if (!mn.getPossibleRespawnLocations().isEmpty())
{
pendingNotificationNpcs.add(mn);
}
}
highlightedNpcs.remove(npc);
@@ -409,6 +445,7 @@ public class NpcIndicatorsPlugin extends Plugin
{
removeOldHighlightedRespawns();
validateSpawnedNpcs();
checkNotifyNpcs();
lastTickUpdate = Instant.now();
lastPlayerLocation = client.getLocalPlayer().getWorldLocation();
}
@@ -486,6 +523,16 @@ public class NpcIndicatorsPlugin extends Plugin
highlightedNpcs.remove(npc);
}
public double getTimeLeftForNpc(MemorizedNpc npc)
{
final Instant now = Instant.now();
final double baseTick = NpcIndicatorsPlugin.ESTIMATED_TICK_LENGTH * (
npc.getDiedOnTick() + npc.getRespawnTime() - client.getTickCount()
);
final double sinceLast = (now.toEpochMilli() - lastTickUpdate.toEpochMilli()) / 1000.0;
return Math.max(0.0, baseTick - sinceLast);
}
private void memorizeNpc(NPC npc)
{
final int npcIndex = npc.getIndex();
@@ -561,6 +608,33 @@ public class NpcIndicatorsPlugin extends Plugin
}
}
public String formatTime(double time)
{
return TIME_LEFT_FORMATTER.format(time);
}
private void checkNotifyNpcs()
{
if (!this.getNotifyOnRespawn)
{
return;
}
final double notifyDelay = ((double)this.getNotifyOnRespawnDelay) / 1000;
final String notifyDelayStr = notifyDelay > 0
? " is less than " + formatTime(notifyDelay) + " seconds from respawn"
: " respawned.";
for (MemorizedNpc npc : pendingNotificationNpcs)
{
if (getTimeLeftForNpc(npc) <= notifyDelay)
{
pendingNotificationNpcs.remove(npc);
notifier.notify(npc.getNpcNames() + notifyDelayStr);
}
}
}
private void validateSpawnedNpcs()
{
if (skipNextSpawnCheck)
@@ -569,6 +643,7 @@ public class NpcIndicatorsPlugin extends Plugin
}
else
{
for (NPC npc : despawnedNpcsThisTick)
{
if (!teleportGraphicsObjectSpawnedThisTick.isEmpty() && teleportGraphicsObjectSpawnedThisTick.contains(npc.getWorldLocation()))
@@ -657,5 +732,7 @@ public class NpcIndicatorsPlugin extends Plugin
this.drawMinimapNames = config.drawMinimapNames();
this.highlightMenuNames = config.highlightMenuNames();
this.showRespawnTimer = config.showRespawnTimer();
this.getNotifyOnRespawn = config.getNotifyOnRespawn();
this.getNotifyOnRespawnDelay = config.getNotifyOnRespawnDelay();
}
}
}

View File

@@ -31,15 +31,10 @@ import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.Instant;
import java.util.List;
import java.util.Locale;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.Constants;
import net.runelite.api.NPC;
import net.runelite.api.NPCDefinition;
import net.runelite.api.Perspective;
@@ -63,12 +58,7 @@ public class NpcSceneOverlay extends Overlay
// a dark background
private static final Color TEXT_COLOR = Color.WHITE;
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 NpcIndicatorsPlugin plugin;
@@ -128,11 +118,8 @@ public class NpcSceneOverlay extends Overlay
OverlayUtil.renderPolygon(graphics, poly, color);
}
final Instant now = Instant.now();
final double baseTick = ((npc.getDiedOnTick() + npc.getRespawnTime()) - client.getTickCount()) * (Constants.GAME_TICK_LENGTH / 1000.0);
final double sinceLast = (now.toEpochMilli() - plugin.getLastTickUpdate().toEpochMilli()) / 1000.0;
final double timeLeft = Math.max(0.0, baseTick - sinceLast);
final String timeLeftStr = TIME_LEFT_FORMATTER.format(timeLeft);
final String timeLeftStr = plugin.formatTime(plugin.getTimeLeftForNpc(npc));
final int textWidth = graphics.getFontMetrics().stringWidth(timeLeftStr);
final int textHeight = graphics.getFontMetrics().getAscent();

View File

@@ -34,6 +34,8 @@ import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.client.config.OpenOSRSConfig;
import static org.junit.Assert.assertEquals;
import net.runelite.client.Notifier;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -59,6 +61,10 @@ public class NpcIndicatorsPluginTest
@Bind
private OpenOSRSConfig openOSRSConfig;
@Mock
@Bind
private Notifier notifier;
@Inject
private NpcIndicatorsPlugin npcIndicatorsPlugin;