Merge remote-tracking branch 'upstream/master' into 2310-merge
This commit is contained in:
@@ -59,6 +59,7 @@ import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.game.ClanManager;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.game.LootManager;
|
||||
import net.runelite.client.game.XpDropManager;
|
||||
import net.runelite.client.game.chatbox.ChatboxPanelManager;
|
||||
import net.runelite.client.graphics.ModelOutlineRenderer;
|
||||
import net.runelite.client.menus.MenuManager;
|
||||
@@ -151,6 +152,9 @@ public class RuneLite
|
||||
@Inject
|
||||
private Provider<LootManager> lootManager;
|
||||
|
||||
@Inject
|
||||
private Provider<XpDropManager> xpDropManager;
|
||||
|
||||
@Inject
|
||||
private Provider<ChatboxPanelManager> chatboxPanelManager;
|
||||
|
||||
@@ -368,6 +372,7 @@ public class RuneLite
|
||||
chatMessageManager.get();
|
||||
commandManager.get();
|
||||
lootManager.get();
|
||||
xpDropManager.get();
|
||||
chatboxPanelManager.get();
|
||||
|
||||
eventBus.subscribe(GameStateChanged.class, this, hooks::onGameStateChanged);
|
||||
|
||||
@@ -61,9 +61,27 @@ import net.runelite.client.events.PlayerLootReceived;
|
||||
@Slf4j
|
||||
public class LootManager
|
||||
{
|
||||
private static final Map<Integer, Integer> NPC_DEATH_ANIMATIONS = ImmutableMap.of(
|
||||
NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH
|
||||
);
|
||||
private static final Map<Integer, Integer> NPC_DEATH_ANIMATIONS = ImmutableMap.<Integer, Integer>builder()
|
||||
.put(NpcID.CAVE_KRAKEN, AnimationID.CAVE_KRAKEN_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_BAT, AnimationID.CRYSTALLINE_BAT_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_RAT, AnimationID.CRYSTALLINE_RAT_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH)
|
||||
.put(NpcID.CRYSTALLINE_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH)
|
||||
.put(NpcID.CORRUPTED_BAT, AnimationID.CRYSTALLINE_BAT_DEATH)
|
||||
.put(NpcID.CORRUPTED_RAT, AnimationID.CRYSTALLINE_RAT_DEATH)
|
||||
.put(NpcID.CORRUPTED_SPIDER, AnimationID.CRYSTALLINE_SPIDER_DEATH)
|
||||
.put(NpcID.CORRUPTED_WOLF, AnimationID.CRYSTALLINE_WOLF_DEATH)
|
||||
.put(NpcID.CORRUPTED_UNICORN, AnimationID.CRYSTALLINE_UNICORN_DEATH)
|
||||
.put(NpcID.CORRUPTED_SCORPION, AnimationID.CORRUPTED_SCORPION_DEATH)
|
||||
.put(NpcID.CORRUPTED_DRAGON, AnimationID.CRYSTALLINE_DRAGON_DEATH)
|
||||
.put(NpcID.CORRUPTED_DARK_BEAST, AnimationID.CRYSTALLINE_DARK_BEAST_DEATH)
|
||||
.put(NpcID.CORRUPTED_BEAR, AnimationID.CRYSTALLINE_BEAR_DEATH)
|
||||
.build();
|
||||
|
||||
private final EventBus eventBus;
|
||||
private final Client client;
|
||||
@@ -167,6 +185,7 @@ public class LootManager
|
||||
final Tile tile = itemSpawned.getTile();
|
||||
final LocalPoint location = tile.getLocalLocation();
|
||||
final int packed = location.getSceneX() << 8 | location.getSceneY();
|
||||
log.debug("storing items in {}", packed);
|
||||
itemSpawns.put(packed, new ItemStack(item.getId(), item.getQuantity(), location));
|
||||
log.debug("Item spawn {} ({}) location {}", item.getId(), item.getQuantity(), location);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package net.runelite.client.game;
|
||||
|
||||
import lombok.Data;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.events.Event;
|
||||
|
||||
@Data
|
||||
public class XpDropEvent implements Event
|
||||
{
|
||||
private Skill skill;
|
||||
private int exp;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package net.runelite.client.game;
|
||||
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.events.ExperienceChanged;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
|
||||
@Singleton
|
||||
public class XpDropManager
|
||||
{
|
||||
|
||||
private final Map<Skill, Integer> previousSkillExpTable = new EnumMap<>(Skill.class);
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private int damage = 0;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private int tickShow = 0;
|
||||
private int previousExpGained;
|
||||
private Client client;
|
||||
private EventBus eventBus;
|
||||
|
||||
@Inject
|
||||
private XpDropManager(
|
||||
final EventBus eventBus,
|
||||
final Client client
|
||||
)
|
||||
{
|
||||
this.client = client;
|
||||
this.eventBus = eventBus;
|
||||
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
|
||||
eventBus.subscribe(ExperienceChanged.class, this, this::onExperienceChanged);
|
||||
}
|
||||
|
||||
private void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
damage = 0;
|
||||
tickShow = 0;
|
||||
}
|
||||
|
||||
private void onExperienceChanged(ExperienceChanged event)
|
||||
{
|
||||
final Skill skill = event.getSkill();
|
||||
final int xp = client.getSkillExperience(skill);
|
||||
Integer previous = previousSkillExpTable.put(skill, xp);
|
||||
if (previous != null)
|
||||
{
|
||||
previousExpGained = xp - previous;
|
||||
XpDropEvent xpDropEvent = new XpDropEvent();
|
||||
xpDropEvent.setExp(previousExpGained);
|
||||
xpDropEvent.setSkill(skill);
|
||||
eventBus.post(XpDropEvent.class, xpDropEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -91,6 +91,8 @@ public class MenuManager
|
||||
private MenuEntry leftClickEntry = null;
|
||||
private MenuEntry firstEntry = null;
|
||||
|
||||
private int playerAttackIdx = -1;
|
||||
|
||||
@Inject
|
||||
private MenuManager(Client client, EventBus eventBus)
|
||||
{
|
||||
@@ -500,6 +502,29 @@ public class MenuManager
|
||||
return index;
|
||||
}
|
||||
|
||||
public int getPlayerAttackOpcode()
|
||||
{
|
||||
final String[] playerMenuOptions = client.getPlayerOptions();
|
||||
|
||||
if (playerAttackIdx != -1 && playerMenuOptions[playerAttackIdx].equals("Attack"))
|
||||
{
|
||||
return client.getPlayerMenuTypes()[playerAttackIdx];
|
||||
}
|
||||
|
||||
playerAttackIdx = -1;
|
||||
|
||||
for (int i = IDX_LOWER; i < IDX_UPPER; i++)
|
||||
{
|
||||
if ("Attack".equals(playerMenuOptions[i]))
|
||||
{
|
||||
playerAttackIdx = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return playerAttackIdx >= 0 ? client.getPlayerMenuTypes()[playerAttackIdx] : -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds to the set of menu entries which when present, will remove all entries except for this one
|
||||
*/
|
||||
@@ -522,7 +547,7 @@ public class MenuManager
|
||||
|
||||
AbstractComparableEntry entry = newBaseComparableEntry(option, target);
|
||||
|
||||
priorityEntries.removeIf(entry::equals);
|
||||
priorityEntries.remove(entry);
|
||||
}
|
||||
|
||||
|
||||
@@ -562,7 +587,7 @@ public class MenuManager
|
||||
|
||||
public void removePriorityEntry(AbstractComparableEntry entry)
|
||||
{
|
||||
priorityEntries.removeIf(entry::equals);
|
||||
priorityEntries.remove(entry);
|
||||
}
|
||||
|
||||
public void removePriorityEntry(String option)
|
||||
@@ -571,7 +596,7 @@ public class MenuManager
|
||||
|
||||
AbstractComparableEntry entry = newBaseComparableEntry(option, "", false);
|
||||
|
||||
priorityEntries.removeIf(entry::equals);
|
||||
priorityEntries.remove(entry);
|
||||
}
|
||||
|
||||
public void removePriorityEntry(String option, boolean strictOption)
|
||||
@@ -581,7 +606,17 @@ public class MenuManager
|
||||
AbstractComparableEntry entry =
|
||||
newBaseComparableEntry(option, "", -1, -1, false, strictOption);
|
||||
|
||||
priorityEntries.removeIf(entry::equals);
|
||||
priorityEntries.remove(entry);
|
||||
}
|
||||
|
||||
public void addPriorityEntries(Collection<AbstractComparableEntry> entries)
|
||||
{
|
||||
priorityEntries.addAll(entries);
|
||||
}
|
||||
|
||||
public void removePriorityEntries(Collection<AbstractComparableEntry> entries)
|
||||
{
|
||||
priorityEntries.removeAll(entries);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -223,7 +223,7 @@ public class CoxPlugin extends Plugin
|
||||
{
|
||||
final String fixedPlayerName = Text.sanitize(rawPlayerName);
|
||||
|
||||
if (fixedPlayerName.equals(tpMatcher.group(1)))
|
||||
if (fixedPlayerName.equals(Text.sanitize(tpMatcher.group(1))))
|
||||
{
|
||||
victims.add(new Victim(player, Victim.Type.TELEPORT));
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
package net.runelite.client.plugins.deathindicator;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.runelite.api.Scene;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import static net.runelite.client.plugins.deathindicator.DeathIndicatorPlugin.HIJACKED_ITEMID;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
import net.runelite.client.util.MiscUtils;
|
||||
|
||||
@EqualsAndHashCode
|
||||
@Getter
|
||||
@Setter
|
||||
class Bone
|
||||
{
|
||||
private String name;
|
||||
private WorldPoint loc;
|
||||
private Instant time;
|
||||
|
||||
void addToScene(Scene scene)
|
||||
{
|
||||
scene.addItem(HIJACKED_ITEMID, 1, loc);
|
||||
}
|
||||
|
||||
void removeFromScene(Scene scene)
|
||||
{
|
||||
scene.removeItem(HIJACKED_ITEMID, 1, loc);
|
||||
}
|
||||
|
||||
String getName()
|
||||
{
|
||||
return ColorUtil.colorStartTag(0xff9040) + "Bones (" + name + ")";
|
||||
}
|
||||
|
||||
String getExamine()
|
||||
{
|
||||
return name + " died here " + MiscUtils.formatTimeAgo(Duration.between(time, Instant.now()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package net.runelite.client.plugins.deathindicator;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Scene;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import static net.runelite.http.api.RuneLiteAPI.GSON;
|
||||
|
||||
@Slf4j
|
||||
public class Bones
|
||||
{
|
||||
private static final String CONFIG_GROUP = "deathIndicator";
|
||||
private static final String BONES_PREFIX = "bones_";
|
||||
|
||||
private ImmutableMap<Integer, Map<WorldPoint, List<Bone>>> map;
|
||||
private boolean changed = false;
|
||||
|
||||
void init(Client client, ConfigManager configManager)
|
||||
{
|
||||
// Clone is important here as the normal array changes
|
||||
int[] regions = client.getMapRegions().clone();
|
||||
Bone[][] bones = getBones(configManager, regions);
|
||||
if (log.isDebugEnabled())
|
||||
{
|
||||
log.debug("Regions are now {}", Arrays.toString(regions));
|
||||
|
||||
int n = 0;
|
||||
for (Bone[] ar : bones)
|
||||
{
|
||||
n += ar.length;
|
||||
}
|
||||
log.debug("Loaded {} Bones", n);
|
||||
}
|
||||
|
||||
initMap(regions, bones);
|
||||
|
||||
showBones(client.getScene());
|
||||
}
|
||||
|
||||
private Bone[][] getBones(ConfigManager configManager, int[] regions)
|
||||
{
|
||||
Bone[][] bones = new Bone[regions.length][];
|
||||
|
||||
for (int i = 0; i < regions.length; i++)
|
||||
{
|
||||
int region = regions[i];
|
||||
bones[i] = getBones(configManager, region);
|
||||
}
|
||||
|
||||
return bones;
|
||||
}
|
||||
|
||||
private Bone[] getBones(ConfigManager configManager, int regionId)
|
||||
{
|
||||
String json = configManager.getConfiguration(CONFIG_GROUP, BONES_PREFIX + regionId);
|
||||
if (json == null)
|
||||
{
|
||||
return new Bone[0];
|
||||
}
|
||||
|
||||
return GSON.fromJson(json, Bone[].class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private void initMap(int[] regions, Bone[][] bones)
|
||||
{
|
||||
ImmutableMap.Builder<Integer, Map<WorldPoint, List<Bone>>> builder = ImmutableMap.builder();
|
||||
|
||||
for (int i = 0; i < regions.length; i++)
|
||||
{
|
||||
int region = regions[i];
|
||||
Bone[] boneA = bones[i];
|
||||
if (boneA.length == 0)
|
||||
{
|
||||
builder.put(region, Collections.EMPTY_MAP);
|
||||
continue;
|
||||
}
|
||||
|
||||
Map<WorldPoint, List<Bone>> map = new HashMap(boneA.length);
|
||||
for (Bone b : boneA)
|
||||
{
|
||||
List<Bone> list = map.computeIfAbsent(b.getLoc(), wp -> new ArrayList<>());
|
||||
list.add(b);
|
||||
}
|
||||
|
||||
builder.put(region, map);
|
||||
}
|
||||
|
||||
this.map = builder.build();
|
||||
}
|
||||
|
||||
private void showBones(Scene scene)
|
||||
{
|
||||
this.forEach(bone -> bone.addToScene(scene));
|
||||
}
|
||||
|
||||
void save(ConfigManager configManager)
|
||||
{
|
||||
if (this.map == null || !changed)
|
||||
{
|
||||
this.changed = false;
|
||||
return;
|
||||
}
|
||||
|
||||
for (Map.Entry<Integer, Map<WorldPoint, List<Bone>>> entry : this.map.entrySet())
|
||||
{
|
||||
final String key = BONES_PREFIX + entry.getKey();
|
||||
final Map<WorldPoint, List<Bone>> map = entry.getValue();
|
||||
if (map.size() == 0)
|
||||
{
|
||||
configManager.unsetConfiguration(CONFIG_GROUP, key);
|
||||
continue;
|
||||
}
|
||||
|
||||
List<Bone> list = new ArrayList<>(map.values().size());
|
||||
for (List<Bone> lb : map.values())
|
||||
{
|
||||
list.addAll(lb);
|
||||
}
|
||||
|
||||
String val = GSON.toJson(list.toArray(new Bone[0]));
|
||||
configManager.setConfiguration(CONFIG_GROUP, key, val);
|
||||
}
|
||||
|
||||
this.changed = false;
|
||||
}
|
||||
|
||||
public boolean add(Bone bone)
|
||||
{
|
||||
if (this.map == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
this.changed = true;
|
||||
final int region = bone.getLoc().getRegionID();
|
||||
final Map<WorldPoint, List<Bone>> map = this.map.get(region);
|
||||
final List<Bone> list = map.computeIfAbsent(bone.getLoc(), wp -> new ArrayList<>());
|
||||
list.add(bone);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void remove(Bone bone)
|
||||
{
|
||||
this.changed = true;
|
||||
final int region = bone.getLoc().getRegionID();
|
||||
final Map<WorldPoint, List<Bone>> map = this.map.get(region);
|
||||
final List<Bone> list = map.get(bone.getLoc());
|
||||
list.remove(bone);
|
||||
}
|
||||
|
||||
public void clear(Scene scene)
|
||||
{
|
||||
if (map == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.forEach(bone -> bone.removeFromScene(scene));
|
||||
}
|
||||
|
||||
public Bone get(WorldPoint point, int i)
|
||||
{
|
||||
return get(point).get(i);
|
||||
}
|
||||
|
||||
public List<Bone> get(WorldPoint point)
|
||||
{
|
||||
final int reg = point.getRegionID();
|
||||
final Map<WorldPoint, List<Bone>> map = this.map.get(reg);
|
||||
if (map == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return map.get(point);
|
||||
}
|
||||
|
||||
private void forEach(Consumer<Bone> consumer)
|
||||
{
|
||||
for (Map<WorldPoint, List<Bone>> map : this.map.values())
|
||||
{
|
||||
for (Map.Entry<WorldPoint, List<Bone>> entry : map.entrySet())
|
||||
{
|
||||
for (Bone bone : entry.getValue())
|
||||
{
|
||||
consumer.accept(bone);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -152,4 +152,14 @@ public interface DeathIndicatorConfig extends Config
|
||||
description = ""
|
||||
)
|
||||
void timeOfDeath(Instant timeOfDeath);
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "permaBones",
|
||||
name = "Permanent bones",
|
||||
description = "Show right clickable bones with the name of who died permanently, after seeing someone die"
|
||||
)
|
||||
default boolean permaBones()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,18 +30,32 @@ import java.awt.image.BufferedImage;
|
||||
import java.time.Duration;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.ChatMessageType;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.ItemDefinition;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.MenuOpcode;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.ItemDespawned;
|
||||
import net.runelite.api.events.LocalPlayerDeath;
|
||||
import net.runelite.api.events.MenuEntryAdded;
|
||||
import net.runelite.api.events.MenuOpened;
|
||||
import net.runelite.api.events.MenuOptionClicked;
|
||||
import net.runelite.api.events.PlayerDeath;
|
||||
import net.runelite.api.events.PostItemDefinition;
|
||||
import net.runelite.api.util.Text;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
@@ -61,6 +75,10 @@ import net.runelite.client.util.ImageUtil;
|
||||
@Slf4j
|
||||
public class DeathIndicatorPlugin extends Plugin
|
||||
{
|
||||
private static final Object BONES = new Object();
|
||||
// A random number, that jagex probably won't actually use in the near future
|
||||
static final int HIJACKED_ITEMID = 0x69696969;
|
||||
|
||||
private static final Set<Integer> RESPAWN_REGIONS = ImmutableSet.of(
|
||||
12850, // Lumbridge
|
||||
11828, // Falador
|
||||
@@ -88,6 +106,14 @@ public class DeathIndicatorPlugin extends Plugin
|
||||
@Inject
|
||||
private EventBus eventBus;
|
||||
|
||||
@Inject
|
||||
private ConfigManager configManager;
|
||||
|
||||
@Inject
|
||||
private ClientThread clientThread;
|
||||
|
||||
private final Bones bones = new Bones();
|
||||
|
||||
private BufferedImage mapArrow;
|
||||
|
||||
private Timer deathTimer;
|
||||
@@ -95,7 +121,7 @@ public class DeathIndicatorPlugin extends Plugin
|
||||
private WorldPoint lastDeath;
|
||||
private Instant lastDeathTime;
|
||||
private int lastDeathWorld;
|
||||
|
||||
private int despawnIdx = 0;
|
||||
@Provides
|
||||
DeathIndicatorConfig deathIndicatorConfig(ConfigManager configManager)
|
||||
{
|
||||
@@ -129,12 +155,18 @@ public class DeathIndicatorPlugin extends Plugin
|
||||
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
|
||||
worldMapPointManager.add(new DeathWorldMapPoint(new WorldPoint(config.deathLocationX(), config.deathLocationY(), config.deathLocationPlane()), this));
|
||||
}
|
||||
|
||||
if (config.permaBones() && client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
clientThread.invokeLater(this::initBones);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
eventBus.unregister(this);
|
||||
eventBus.unregister(BONES);
|
||||
|
||||
if (client.hasHintArrow())
|
||||
{
|
||||
@@ -148,6 +180,24 @@ public class DeathIndicatorPlugin extends Plugin
|
||||
}
|
||||
|
||||
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
|
||||
|
||||
clientThread.invokeLater(this::clearBones);
|
||||
saveBones();
|
||||
}
|
||||
|
||||
private void initBones()
|
||||
{
|
||||
bones.init(client, configManager);
|
||||
}
|
||||
|
||||
private void saveBones()
|
||||
{
|
||||
bones.save(configManager);
|
||||
}
|
||||
|
||||
private void clearBones()
|
||||
{
|
||||
bones.clear(client.getScene());
|
||||
}
|
||||
|
||||
private void addSubscriptions()
|
||||
@@ -156,6 +206,100 @@ public class DeathIndicatorPlugin extends Plugin
|
||||
eventBus.subscribe(LocalPlayerDeath.class, this, this::onLocalPlayerDeath);
|
||||
eventBus.subscribe(GameTick.class, this, this::onGameTick);
|
||||
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
|
||||
if (config.permaBones())
|
||||
{
|
||||
addBoneSubs();
|
||||
}
|
||||
}
|
||||
|
||||
private void addBoneSubs()
|
||||
{
|
||||
eventBus.subscribe(ItemDespawned.class, BONES, this::onItemDespawn);
|
||||
eventBus.subscribe(PlayerDeath.class, BONES, this::onPlayerDeath);
|
||||
eventBus.subscribe(MenuEntryAdded.class, BONES, this::onMenuEntryAdded);
|
||||
eventBus.subscribe(MenuOptionClicked.class, BONES, this::onMenuOptionClicked);
|
||||
eventBus.subscribe(MenuOpened.class, BONES, this::onMenuOpened);
|
||||
eventBus.subscribe(PostItemDefinition.class, BONES, this::onPostItemDefinition);
|
||||
}
|
||||
|
||||
private void onPostItemDefinition(PostItemDefinition def)
|
||||
{
|
||||
ItemDefinition itemDef = def.getItemDefinition();
|
||||
if (itemDef.getId() == HIJACKED_ITEMID)
|
||||
{
|
||||
itemDef.setModelOverride(ItemID.BONES);
|
||||
itemDef.setName("Bones");
|
||||
// This is so never hide untradeables doesn't not hide it
|
||||
itemDef.setTradeable(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void onPlayerDeath(PlayerDeath death)
|
||||
{
|
||||
Player p = death.getPlayer();
|
||||
Bone b = new Bone();
|
||||
|
||||
b.setName(Text.sanitize(p.getName()));
|
||||
b.setTime(Instant.now());
|
||||
b.setLoc(p.getWorldLocation());
|
||||
|
||||
while (!bones.add(b))
|
||||
{
|
||||
initBones();
|
||||
}
|
||||
|
||||
b.addToScene(client.getScene());
|
||||
}
|
||||
|
||||
private void onMenuEntryAdded(MenuEntryAdded event)
|
||||
{
|
||||
if (event.getIdentifier() == HIJACKED_ITEMID)
|
||||
{
|
||||
if (event.getOpcode() == MenuOpcode.GROUND_ITEM_THIRD_OPTION.getId())
|
||||
{
|
||||
client.setMenuOptionCount(client.getMenuOptionCount() - 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onMenuOpened(MenuOpened event)
|
||||
{
|
||||
int idx = 0;
|
||||
|
||||
for (MenuEntry entry : event)
|
||||
{
|
||||
if (entry.getIdentifier() != HIJACKED_ITEMID)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Only entries with appropriate identifier here will be examine so that's easy
|
||||
// Add idx to id field, so we can find that back from clicked event
|
||||
entry.setIdentifier(HIJACKED_ITEMID + idx);
|
||||
|
||||
Bone bone = bones.get(
|
||||
WorldPoint.fromScene(client, entry.getParam0(), entry.getParam1(), client.getPlane()),
|
||||
idx++
|
||||
);
|
||||
|
||||
entry.setTarget(bone.getName());
|
||||
event.setModified();
|
||||
}
|
||||
}
|
||||
|
||||
private void onMenuOptionClicked(MenuOptionClicked event)
|
||||
{
|
||||
if (event.getIdentifier() >= HIJACKED_ITEMID
|
||||
&& event.getOpcode() == MenuOpcode.EXAMINE_ITEM_GROUND.getId())
|
||||
{
|
||||
Bone b = bones.get(
|
||||
WorldPoint.fromScene(client, event.getParam0(), event.getParam1(), client.getPlane()),
|
||||
event.getIdentifier() - HIJACKED_ITEMID
|
||||
);
|
||||
|
||||
client.addChatMessage(ChatMessageType.ITEM_EXAMINE, "", b.getExamine(), "");
|
||||
event.consume();
|
||||
}
|
||||
}
|
||||
|
||||
private void onLocalPlayerDeath(LocalPlayerDeath death)
|
||||
@@ -238,6 +382,29 @@ public class DeathIndicatorPlugin extends Plugin
|
||||
{
|
||||
if (event.getGroup().equals("deathIndicator"))
|
||||
{
|
||||
if ("permaBones".equals(event.getKey()))
|
||||
{
|
||||
if (config.permaBones())
|
||||
{
|
||||
addBoneSubs();
|
||||
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
clientThread.invokeLater(this::initBones);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
eventBus.unregister(BONES);
|
||||
|
||||
if (client.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
clientThread.invokeLater(this::clearBones);
|
||||
saveBones();
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (!config.showDeathHintArrow() && hasDied())
|
||||
{
|
||||
client.clearHintArrow();
|
||||
@@ -267,32 +434,62 @@ public class DeathIndicatorPlugin extends Plugin
|
||||
|
||||
private void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
if (!hasDied())
|
||||
switch (event.getGameState())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.getGameState() == GameState.LOGGED_IN)
|
||||
{
|
||||
if (client.getWorld() == config.deathWorld())
|
||||
{
|
||||
WorldPoint deathPoint = new WorldPoint(config.deathLocationX(), config.deathLocationY(), config.deathLocationPlane());
|
||||
|
||||
if (config.showDeathHintArrow())
|
||||
case LOADING:
|
||||
clearBones();
|
||||
saveBones();
|
||||
break;
|
||||
case LOGGED_IN:
|
||||
if (config.permaBones())
|
||||
{
|
||||
client.setHintArrow(deathPoint);
|
||||
initBones();
|
||||
}
|
||||
|
||||
if (config.showDeathOnWorldMap())
|
||||
if (!hasDied())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (client.getWorld() == config.deathWorld())
|
||||
{
|
||||
WorldPoint deathPoint = new WorldPoint(config.deathLocationX(), config.deathLocationY(), config.deathLocationPlane());
|
||||
|
||||
if (config.showDeathHintArrow())
|
||||
{
|
||||
client.setHintArrow(deathPoint);
|
||||
}
|
||||
|
||||
if (config.showDeathOnWorldMap())
|
||||
{
|
||||
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
|
||||
worldMapPointManager.add(new DeathWorldMapPoint(deathPoint, this));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
|
||||
worldMapPointManager.add(new DeathWorldMapPoint(deathPoint, this));
|
||||
}
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onItemDespawn(ItemDespawned event)
|
||||
{
|
||||
if (event.getItem().getId() == HIJACKED_ITEMID)
|
||||
{
|
||||
List<Bone> list = bones.get(event.getTile().getWorldLocation());
|
||||
if (list == null)
|
||||
{
|
||||
worldMapPointManager.removeIf(DeathWorldMapPoint.class::isInstance);
|
||||
return;
|
||||
}
|
||||
if (list.size() <= despawnIdx)
|
||||
{
|
||||
despawnIdx = 0;
|
||||
}
|
||||
Bone bone = list.get(despawnIdx++);
|
||||
bone.addToScene(client.getScene());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,8 +29,6 @@ package net.runelite.client.plugins.experiencedrop;
|
||||
import com.google.inject.Provides;
|
||||
import java.awt.Color;
|
||||
import java.util.Arrays;
|
||||
import java.util.EnumMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.IntStream;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
@@ -46,7 +44,6 @@ import net.runelite.api.SpriteID;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.WorldType;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.ExperienceChanged;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.ScriptCallbackEvent;
|
||||
@@ -57,6 +54,7 @@ import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.game.NPCManager;
|
||||
import net.runelite.client.game.XpDropEvent;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
@@ -65,30 +63,23 @@ import net.runelite.client.util.ColorUtil;
|
||||
@PluginDescriptor(
|
||||
name = "XP Drop",
|
||||
description = "Enable customization of the way XP drops are displayed",
|
||||
tags = {"experience", "levels", "tick"}
|
||||
)
|
||||
tags = {"experience", "levels", "tick"})
|
||||
@Singleton
|
||||
public class XpDropPlugin extends Plugin
|
||||
{
|
||||
private static final int XPDROP_PADDING = 2; // space between xp drop icons
|
||||
private static final double HITPOINT_RATIO = 1.33; // Base rate of hp xp per point damage
|
||||
private static final double DMM_MULTIPLIER_RATIO = 10;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
|
||||
@Inject
|
||||
private XpDropConfig config;
|
||||
|
||||
@Inject
|
||||
private NPCManager npcManager;
|
||||
|
||||
@Inject
|
||||
private OverlayManager overlayManager;
|
||||
|
||||
@Inject
|
||||
private XpDropOverlay overlay;
|
||||
|
||||
@Inject
|
||||
private EventBus eventBus;
|
||||
|
||||
@@ -106,16 +97,15 @@ public class XpDropPlugin extends Plugin
|
||||
private boolean hasDropped = false;
|
||||
private boolean correctPrayer;
|
||||
private Skill lastSkill = null;
|
||||
private final Map<Skill, Integer> previousSkillExpTable = new EnumMap<>(Skill.class);
|
||||
private PrayerType currentTickPrayer;
|
||||
private XpDropConfig.DamageMode damageMode;
|
||||
|
||||
private boolean hideSkillIcons;
|
||||
private Color getMeleePrayerColor;
|
||||
private Color getRangePrayerColor;
|
||||
private Color getMagePrayerColor;
|
||||
private int fakeXpDropDelay;
|
||||
private XpDropConfig.DamageMode showdamagedrops;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private Color damageColor;
|
||||
|
||||
@@ -153,10 +143,17 @@ public class XpDropPlugin extends Plugin
|
||||
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
|
||||
eventBus.subscribe(WidgetHiddenChanged.class, this, this::onWidgetHiddenChanged);
|
||||
eventBus.subscribe(GameTick.class, this, this::onGameTick);
|
||||
eventBus.subscribe(ExperienceChanged.class, this, this::onExperienceChanged);
|
||||
eventBus.subscribe(XpDropEvent.class, this, this::onXpDropEvent);
|
||||
eventBus.subscribe(ScriptCallbackEvent.class, this, this::onScriptCallbackEvent);
|
||||
}
|
||||
|
||||
private void onXpDropEvent(XpDropEvent event)
|
||||
{
|
||||
previousExpGained = event.getExp();
|
||||
lastSkill = event.getSkill();
|
||||
hasDropped = true;
|
||||
}
|
||||
|
||||
private void onConfigChanged(ConfigChanged event)
|
||||
{
|
||||
if (!event.getGroup().equals("xpdrop"))
|
||||
@@ -188,8 +185,8 @@ public class XpDropPlugin extends Plugin
|
||||
|
||||
private void onGameStateChanged(GameStateChanged event)
|
||||
{
|
||||
damage = 0;
|
||||
tickShow = 0;
|
||||
damage = 0;
|
||||
}
|
||||
|
||||
private void onWidgetHiddenChanged(WidgetHiddenChanged event)
|
||||
@@ -269,9 +266,12 @@ public class XpDropPlugin extends Plugin
|
||||
switch (prayer)
|
||||
{
|
||||
case MELEE:
|
||||
if (spriteIDs.anyMatch(id ->
|
||||
id == SpriteID.SKILL_ATTACK || id == SpriteID.SKILL_STRENGTH || id == SpriteID.SKILL_DEFENCE
|
||||
|| correctPrayer))
|
||||
if (spriteIDs.anyMatch(
|
||||
id ->
|
||||
id == SpriteID.SKILL_ATTACK
|
||||
|| id == SpriteID.SKILL_STRENGTH
|
||||
|| id == SpriteID.SKILL_DEFENCE
|
||||
|| correctPrayer))
|
||||
{
|
||||
color = this.getMeleePrayerColor.getRGB();
|
||||
correctPrayer = true;
|
||||
@@ -351,21 +351,6 @@ public class XpDropPlugin extends Plugin
|
||||
client.runScript(XPDROP_DISABLED, lastSkill.ordinal(), previousExpGained);
|
||||
}
|
||||
|
||||
private void onExperienceChanged(ExperienceChanged event)
|
||||
{
|
||||
final Skill skill = event.getSkill();
|
||||
final int xp = client.getSkillExperience(skill);
|
||||
|
||||
lastSkill = skill;
|
||||
|
||||
Integer previous = previousSkillExpTable.put(skill, xp);
|
||||
if (previous != null)
|
||||
{
|
||||
previousExpGained = xp - previous;
|
||||
hasDropped = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void onScriptCallbackEvent(ScriptCallbackEvent e)
|
||||
{
|
||||
if (this.showdamagedrops == XpDropConfig.DamageMode.NONE)
|
||||
@@ -404,16 +389,19 @@ public class XpDropPlugin extends Plugin
|
||||
final int exp = intStack[intStackSize - 1];
|
||||
calculateDamageDealt(exp);
|
||||
}
|
||||
else if (eventName.equals("xpDropAddDamage") &&
|
||||
damageMode == XpDropConfig.DamageMode.IN_XP_DROP &&
|
||||
damage > 0)
|
||||
else if (eventName.equals("xpDropAddDamage")
|
||||
&& damageMode == XpDropConfig.DamageMode.IN_XP_DROP
|
||||
&& damage > 0)
|
||||
{
|
||||
final String[] stringStack = client.getStringStack();
|
||||
final int stringStackSize = client.getStringStackSize();
|
||||
|
||||
String builder = stringStack[stringStackSize - 1] +
|
||||
ColorUtil.colorTag(this.damageColor) +
|
||||
" (" + damage + ")";
|
||||
String builder =
|
||||
stringStack[stringStackSize - 1]
|
||||
+ ColorUtil.colorTag(this.damageColor)
|
||||
+ " ("
|
||||
+ damage
|
||||
+ ")";
|
||||
stringStack[stringStackSize - 1] = builder;
|
||||
}
|
||||
}
|
||||
@@ -431,7 +419,8 @@ public class XpDropPlugin extends Plugin
|
||||
Actor a = client.getLocalPlayer().getInteracting();
|
||||
if (!(a instanceof NPC) && !(a instanceof Player))
|
||||
{
|
||||
// If we are interacting with nothing we may have clicked away at the perfect time fall back to last tick
|
||||
// If we are interacting with nothing we may have clicked away at the perfect time fall back
|
||||
// to last tick
|
||||
if (!(lastOpponent instanceof NPC) && !(lastOpponent instanceof Player))
|
||||
{
|
||||
damage = (int) Math.rint(damageDealt);
|
||||
|
||||
@@ -321,4 +321,16 @@ public interface GauntletConfig extends Config
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
position = 21,
|
||||
keyName = "displayResources",
|
||||
name = "Show raw resources gathered",
|
||||
description = "Displays how much of each resource you have gathered.",
|
||||
titleSection = "resources"
|
||||
)
|
||||
default boolean displayGatheredResources()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,6 +43,7 @@ import net.runelite.api.Client;
|
||||
import net.runelite.api.GameObject;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.HeadIcon;
|
||||
import net.runelite.api.ItemID;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.NPCDefinition;
|
||||
import net.runelite.api.NpcID;
|
||||
@@ -50,6 +51,7 @@ import net.runelite.api.ObjectID;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Projectile;
|
||||
import net.runelite.api.ProjectileID;
|
||||
import net.runelite.api.Skill;
|
||||
import net.runelite.api.SoundEffectID;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.api.events.AnimationChanged;
|
||||
@@ -58,6 +60,7 @@ import net.runelite.api.events.GameObjectDespawned;
|
||||
import net.runelite.api.events.GameObjectSpawned;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.MenuOptionClicked;
|
||||
import net.runelite.api.events.NpcDespawned;
|
||||
import net.runelite.api.events.NpcSpawned;
|
||||
import net.runelite.api.events.ProjectileSpawned;
|
||||
@@ -65,6 +68,8 @@ import net.runelite.api.events.VarbitChanged;
|
||||
import net.runelite.client.callback.ClientThread;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.events.NpcLootReceived;
|
||||
import net.runelite.client.game.ItemManager;
|
||||
import net.runelite.client.game.SkillIconManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
@@ -73,7 +78,10 @@ import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.LIGHTNING;
|
||||
import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.MAGIC;
|
||||
import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.PRAYER;
|
||||
import static net.runelite.client.plugins.gauntlet.Hunllef.BossAttack.RANGE;
|
||||
import net.runelite.client.game.XpDropEvent;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.ui.overlay.infobox.Counter;
|
||||
import net.runelite.client.ui.overlay.infobox.InfoBoxManager;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Gauntlet",
|
||||
@@ -104,7 +112,8 @@ public class GauntletPlugin extends Plugin
|
||||
ObjectID.PHREN_ROOTS_36066, ObjectID.FISHING_SPOT_36068, ObjectID.FISHING_SPOT_35971, ObjectID.GRYM_ROOT, ObjectID.GRYM_ROOT_36070,
|
||||
ObjectID.LINUM_TIRINUM, ObjectID.LINUM_TIRINUM_36072
|
||||
);
|
||||
|
||||
private static final int GATHERING_HERB = 0;
|
||||
private static final int GATHERING_CLOTH = 1;
|
||||
@Inject
|
||||
@Getter(AccessLevel.NONE)
|
||||
private Client client;
|
||||
@@ -138,6 +147,10 @@ public class GauntletPlugin extends Plugin
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
@Nullable
|
||||
private Hunllef hunllef;
|
||||
@Inject
|
||||
private InfoBoxManager infoBoxManager;
|
||||
@Inject
|
||||
private ItemManager itemManager;
|
||||
private boolean attackVisualOutline;
|
||||
private boolean completeStartup = false;
|
||||
private boolean displayTimerChat;
|
||||
@@ -160,11 +173,24 @@ public class GauntletPlugin extends Plugin
|
||||
private final Map<String, Integer> items = new HashMap<>();
|
||||
private final Set<Missiles> projectiles = new HashSet<>();
|
||||
private final Set<Resources> resources = new HashSet<>();
|
||||
|
||||
private GauntletConfig.CounterDisplay countAttacks;
|
||||
private int resourceIconSize;
|
||||
private Set<Tornado> tornadoes = new HashSet<>();
|
||||
private int projectileIconSize;
|
||||
|
||||
private boolean displayResources;
|
||||
private Counter oreCounter;
|
||||
private Counter woodCounter;
|
||||
private Counter clothCounter;
|
||||
private Counter fishCounter;
|
||||
private Counter herbCounter;
|
||||
private int oresGathered;
|
||||
private int woodGathered;
|
||||
private int clothGathered;
|
||||
private int fishGathered;
|
||||
private int herbGathered;
|
||||
private int currentFarmingAction = -1;
|
||||
private boolean countersVisible = false;
|
||||
|
||||
@Provides
|
||||
GauntletConfig getConfig(ConfigManager configManager)
|
||||
@@ -177,6 +203,7 @@ public class GauntletPlugin extends Plugin
|
||||
{
|
||||
addSubscriptions();
|
||||
updateConfig();
|
||||
initializeCounters();
|
||||
overlayManager.add(overlay);
|
||||
overlayManager.add(infoboxoverlay);
|
||||
overlayManager.add(GauntletCounter);
|
||||
@@ -200,6 +227,47 @@ public class GauntletPlugin extends Plugin
|
||||
}
|
||||
}
|
||||
|
||||
private void addCounters()
|
||||
{
|
||||
if (!countersVisible)
|
||||
{
|
||||
infoBoxManager.addInfoBox(oreCounter);
|
||||
infoBoxManager.addInfoBox(woodCounter);
|
||||
infoBoxManager.addInfoBox(clothCounter);
|
||||
infoBoxManager.addInfoBox(fishCounter);
|
||||
infoBoxManager.addInfoBox(herbCounter);
|
||||
countersVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void initializeCounters()
|
||||
{
|
||||
resetGatheringCounters();
|
||||
oreCounter = new Counter(itemManager.getImage(ItemID.CORRUPTED_ORE), this, 0);
|
||||
woodCounter = new Counter(itemManager.getImage(ItemID.PHREN_BARK_23878), this, 0);
|
||||
clothCounter = new Counter(itemManager.getImage(ItemID.LINUM_TIRINUM_23876), this, 0);
|
||||
fishCounter = new Counter(itemManager.getImage(ItemID.RAW_PADDLEFISH), this, 0);
|
||||
herbCounter = new Counter(itemManager.getImage(ItemID.GRYM_LEAF_23875), this, 0);
|
||||
}
|
||||
|
||||
private void resetGatheringCounters()
|
||||
{
|
||||
oresGathered = 0;
|
||||
fishGathered = 0;
|
||||
woodGathered = 0;
|
||||
clothGathered = 0;
|
||||
herbGathered = 0;
|
||||
}
|
||||
|
||||
private void updateCounters()
|
||||
{
|
||||
oreCounter.setCount(oresGathered);
|
||||
woodCounter.setCount(woodGathered);
|
||||
clothCounter.setCount(clothGathered);
|
||||
fishCounter.setCount(fishGathered);
|
||||
herbCounter.setCount(herbGathered);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void shutDown()
|
||||
{
|
||||
@@ -213,12 +281,24 @@ public class GauntletPlugin extends Plugin
|
||||
overlayManager.remove(overlay);
|
||||
overlayManager.remove(infoboxoverlay);
|
||||
overlayManager.remove(GauntletCounter);
|
||||
removeCounters();
|
||||
resetGatheringCounters();
|
||||
resources.clear();
|
||||
projectiles.clear();
|
||||
tornadoes.clear();
|
||||
setHunllef(null);
|
||||
}
|
||||
|
||||
private void removeCounters()
|
||||
{
|
||||
infoBoxManager.removeInfoBox(oreCounter);
|
||||
infoBoxManager.removeInfoBox(woodCounter);
|
||||
infoBoxManager.removeInfoBox(clothCounter);
|
||||
infoBoxManager.removeInfoBox(fishCounter);
|
||||
infoBoxManager.removeInfoBox(herbCounter);
|
||||
countersVisible = false;
|
||||
}
|
||||
|
||||
private void addSubscriptions()
|
||||
{
|
||||
eventBus.subscribe(AnimationChanged.class, this, this::onAnimationChanged);
|
||||
@@ -231,6 +311,56 @@ public class GauntletPlugin extends Plugin
|
||||
eventBus.subscribe(NpcSpawned.class, this, this::onNpcSpawned);
|
||||
eventBus.subscribe(ProjectileSpawned.class, this, this::onProjectileSpawned);
|
||||
eventBus.subscribe(VarbitChanged.class, this, this::onVarbitChanged);
|
||||
eventBus.subscribe(XpDropEvent.class, this, this::onXpDropEvent);
|
||||
eventBus.subscribe(NpcLootReceived.class, this, this::onNpcLootReceived);
|
||||
eventBus.subscribe(MenuOptionClicked.class, this, this::onMenuOptionClicked);
|
||||
}
|
||||
|
||||
private void onMenuOptionClicked(MenuOptionClicked menuOptionClicked)
|
||||
{
|
||||
if (menuOptionClicked.getTarget().toUpperCase().contains("LINUM"))
|
||||
{
|
||||
currentFarmingAction = GATHERING_CLOTH;
|
||||
}
|
||||
if (menuOptionClicked.getTarget().toUpperCase().contains("GRYM"))
|
||||
{
|
||||
currentFarmingAction = GATHERING_HERB;
|
||||
}
|
||||
}
|
||||
|
||||
private void onNpcLootReceived(NpcLootReceived npcLootReceived)
|
||||
{
|
||||
fishGathered += (int) npcLootReceived.getItems().stream().filter(item -> item.getId() == ItemID.RAW_PADDLEFISH).count();
|
||||
herbGathered += (int) npcLootReceived.getItems().stream().filter(item -> item.getId() == ItemID.GRYM_LEAF || item.getId() == ItemID.GRYM_LEAF_23875).count();
|
||||
updateCounters();
|
||||
}
|
||||
|
||||
private void onXpDropEvent(XpDropEvent experienceChanged)
|
||||
{
|
||||
if (experienceChanged.getSkill().compareTo(Skill.MINING) == 0)
|
||||
{
|
||||
oresGathered++;
|
||||
}
|
||||
if (experienceChanged.getSkill().compareTo(Skill.WOODCUTTING) == 0)
|
||||
{
|
||||
woodGathered++;
|
||||
}
|
||||
if (experienceChanged.getSkill().compareTo(Skill.FARMING) == 0)
|
||||
{
|
||||
if (currentFarmingAction == GATHERING_HERB)
|
||||
{
|
||||
herbGathered++;
|
||||
}
|
||||
else if (currentFarmingAction == GATHERING_CLOTH)
|
||||
{
|
||||
clothGathered++;
|
||||
}
|
||||
}
|
||||
if (experienceChanged.getSkill().compareTo(Skill.FISHING) == 0)
|
||||
{
|
||||
fishGathered++;
|
||||
}
|
||||
updateCounters();
|
||||
}
|
||||
|
||||
private void onAnimationChanged(AnimationChanged event)
|
||||
@@ -325,6 +455,18 @@ public class GauntletPlugin extends Plugin
|
||||
timerVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (event.getKey().equals("displayResources"))
|
||||
{
|
||||
if (this.displayResources && this.startedGauntlet())
|
||||
{
|
||||
addCounters();
|
||||
}
|
||||
else
|
||||
{
|
||||
removeCounters();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void onGameObjectDespawned(GameObjectDespawned event)
|
||||
@@ -379,6 +521,7 @@ public class GauntletPlugin extends Plugin
|
||||
if (HUNLLEF_NPC_IDS.contains(npc.getId()))
|
||||
{
|
||||
setHunllef(null);
|
||||
resetGatheringCounters();
|
||||
}
|
||||
else if (TORNADO_NPC_IDS.contains(npc.getId()))
|
||||
{
|
||||
@@ -436,6 +579,14 @@ public class GauntletPlugin extends Plugin
|
||||
{
|
||||
timer.checkStates(true);
|
||||
}
|
||||
if (startedGauntlet() && displayResources)
|
||||
{
|
||||
addCounters();
|
||||
}
|
||||
else
|
||||
{
|
||||
removeCounters();
|
||||
}
|
||||
}
|
||||
|
||||
boolean fightingBoss()
|
||||
@@ -468,5 +619,6 @@ public class GauntletPlugin extends Plugin
|
||||
this.displayTimerChat = config.displayTimerChat();
|
||||
this.attackVisualOutline = config.attackVisualOutline();
|
||||
this.highlightPrayerInfobox = config.highlightPrayerInfobox();
|
||||
this.displayResources = config.displayGatheredResources();
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -33,6 +33,7 @@ import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.SpriteID;
|
||||
import net.runelite.client.game.SpriteManager;
|
||||
import net.runelite.client.plugins.inferno.displaymodes.InfernoPrayerDisplayMode;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.OverlayPriority;
|
||||
@@ -41,16 +42,19 @@ import net.runelite.client.ui.overlay.components.ImageComponent;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
|
||||
@Singleton
|
||||
public class InfernoJadOverlay extends Overlay
|
||||
public class InfernoInfoBoxOverlay extends Overlay
|
||||
{
|
||||
private static final Color NOT_ACTIVATED_BACKGROUND_COLOR = new Color(150, 0, 0, 150);
|
||||
private final Client client;
|
||||
private final InfernoPlugin plugin;
|
||||
private final SpriteManager spriteManager;
|
||||
private final PanelComponent imagePanelComponent = new PanelComponent();
|
||||
private BufferedImage prayMeleeSprite;
|
||||
private BufferedImage prayRangedSprite;
|
||||
private BufferedImage prayMagicSprite;
|
||||
|
||||
@Inject
|
||||
private InfernoJadOverlay(final Client client, final InfernoPlugin plugin, final SpriteManager spriteManager)
|
||||
private InfernoInfoBoxOverlay(final Client client, final InfernoPlugin plugin, final SpriteManager spriteManager)
|
||||
{
|
||||
setPosition(OverlayPosition.BOTTOM_RIGHT);
|
||||
setPriority(OverlayPriority.HIGH);
|
||||
@@ -62,48 +66,56 @@ public class InfernoJadOverlay extends Overlay
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!plugin.isShowPrayerHelp() || (plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTTOM_RIGHT
|
||||
&& plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTH))
|
||||
if (plugin.getPrayerDisplayMode() != InfernoPrayerDisplayMode.BOTTOM_RIGHT
|
||||
&& plugin.getPrayerDisplayMode() != InfernoPrayerDisplayMode.BOTH)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
InfernoJad.Attack attack = null;
|
||||
int leastTicks = 999;
|
||||
|
||||
for (InfernoJad jad : plugin.getJads())
|
||||
{
|
||||
if (jad.getNextAttack() == null || jad.getTicksTillNextAttack() < 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (jad.getTicksTillNextAttack() < leastTicks)
|
||||
{
|
||||
leastTicks = jad.getTicksTillNextAttack();
|
||||
attack = jad.getNextAttack();
|
||||
}
|
||||
}
|
||||
|
||||
if (attack == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
final BufferedImage prayerImage = getPrayerImage(attack);
|
||||
|
||||
imagePanelComponent.getChildren().clear();
|
||||
imagePanelComponent.getChildren().add(new ImageComponent(prayerImage));
|
||||
imagePanelComponent.setBackgroundColor(client.isPrayerActive(attack.getPrayer())
|
||||
? ComponentConstants.STANDARD_BACKGROUND_COLOR
|
||||
: NOT_ACTIVATED_BACKGROUND_COLOR);
|
||||
|
||||
if (plugin.getClosestAttack() != null)
|
||||
{
|
||||
final BufferedImage prayerImage = getPrayerImage(plugin.getClosestAttack());
|
||||
|
||||
imagePanelComponent.getChildren().add(new ImageComponent(prayerImage));
|
||||
imagePanelComponent.setBackgroundColor(client.isPrayerActive(plugin.getClosestAttack().getPrayer())
|
||||
? ComponentConstants.STANDARD_BACKGROUND_COLOR
|
||||
: NOT_ACTIVATED_BACKGROUND_COLOR);
|
||||
}
|
||||
else
|
||||
{
|
||||
imagePanelComponent.setBackgroundColor(ComponentConstants.STANDARD_BACKGROUND_COLOR);
|
||||
}
|
||||
|
||||
return imagePanelComponent.render(graphics);
|
||||
}
|
||||
|
||||
private BufferedImage getPrayerImage(InfernoJad.Attack attack)
|
||||
private BufferedImage getPrayerImage(InfernoNPC.Attack attack)
|
||||
{
|
||||
final int prayerSpriteID = attack == InfernoJad.Attack.MAGIC ? SpriteID.PRAYER_PROTECT_FROM_MAGIC : SpriteID.PRAYER_PROTECT_FROM_MISSILES;
|
||||
return spriteManager.getSprite(prayerSpriteID, 0);
|
||||
if (prayMeleeSprite == null)
|
||||
{
|
||||
prayMeleeSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MELEE, 0);
|
||||
}
|
||||
if (prayRangedSprite == null)
|
||||
{
|
||||
prayRangedSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MISSILES, 0);
|
||||
}
|
||||
if (prayMagicSprite == null)
|
||||
{
|
||||
prayMagicSprite = spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MAGIC, 0);
|
||||
}
|
||||
|
||||
switch (attack)
|
||||
{
|
||||
case MELEE:
|
||||
return prayMeleeSprite;
|
||||
case RANGED:
|
||||
return prayRangedSprite;
|
||||
case MAGIC:
|
||||
return prayMagicSprite;
|
||||
}
|
||||
|
||||
return prayMagicSprite;
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2017, Devin French <https://github.com/devinfrench>
|
||||
* 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.plugins.inferno;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.api.AnimationID;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Prayer;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
public class InfernoJad
|
||||
{
|
||||
private static final int TICKS_AFTER_ANIMATION = 4;
|
||||
|
||||
private NPC npc;
|
||||
private Attack nextAttack;
|
||||
private int ticksTillNextAttack;
|
||||
|
||||
InfernoJad(NPC npc)
|
||||
{
|
||||
this.npc = npc;
|
||||
nextAttack = null;
|
||||
ticksTillNextAttack = -1;
|
||||
}
|
||||
|
||||
void updateNextAttack(Attack nextAttack)
|
||||
{
|
||||
this.nextAttack = nextAttack;
|
||||
this.ticksTillNextAttack = TICKS_AFTER_ANIMATION;
|
||||
}
|
||||
|
||||
void gameTick()
|
||||
{
|
||||
if (ticksTillNextAttack < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.ticksTillNextAttack--;
|
||||
|
||||
if (ticksTillNextAttack < 0)
|
||||
{
|
||||
nextAttack = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
enum Attack
|
||||
{
|
||||
MAGIC(AnimationID.JALTOK_JAD_MAGE_ATTACK, Prayer.PROTECT_FROM_MAGIC),
|
||||
RANGE(AnimationID.JALTOK_JAD_RANGE_ATTACK, Prayer.PROTECT_FROM_MISSILES);
|
||||
|
||||
private final int animation;
|
||||
private final Prayer prayer;
|
||||
|
||||
Attack(final int animation, final Prayer prayer)
|
||||
{
|
||||
this.animation = animation;
|
||||
this.prayer = prayer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
|
||||
* Copyright (c) 2017, Devin French <https://github.com/devinfrench>
|
||||
* 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.
|
||||
* 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 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
|
||||
@@ -25,162 +25,376 @@
|
||||
package net.runelite.client.plugins.inferno;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import net.runelite.api.AnimationID;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.NpcID;
|
||||
import net.runelite.api.Prayer;
|
||||
import net.runelite.api.coords.WorldArea;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
|
||||
public class InfernoNPC
|
||||
{
|
||||
public enum Attackstyle
|
||||
{
|
||||
MAGE("Mage", Color.CYAN),
|
||||
RANGE("Range", Color.GREEN),
|
||||
MELEE("Melee", Color.WHITE),
|
||||
RANDOM("Random", Color.ORANGE);
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private String name;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private Color color;
|
||||
|
||||
Attackstyle(String s, Color c)
|
||||
{
|
||||
this.name = s;
|
||||
this.color = c;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private NPC npc;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private String name;
|
||||
|
||||
private Type type;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private Attackstyle attackstyle;
|
||||
|
||||
private Attack nextAttack;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private int attackTicks;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private int priority;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private int ticksTillAttack = -1;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private boolean attacking = false;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private int attackAnimation;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean isMidAttack = false;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
@Setter(AccessLevel.PACKAGE)
|
||||
private int distanceToPlayer = 0;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
int textLocHeight;
|
||||
private int ticksTillNextAttack;
|
||||
private int lastAnimation;
|
||||
private boolean lastCanAttack;
|
||||
//0 = not in LOS, 1 = in LOS after move, 2 = in LOS
|
||||
private final Map<WorldPoint, Integer> safeSpotCache;
|
||||
|
||||
InfernoNPC(NPC npc)
|
||||
{
|
||||
this.npc = npc;
|
||||
textLocHeight = npc.getLogicalHeight() + 40;
|
||||
switch (npc.getId())
|
||||
this.type = Type.typeFromId(npc.getId());
|
||||
this.nextAttack = type.getDefaultAttack();
|
||||
this.ticksTillNextAttack = 0;
|
||||
this.lastAnimation = -1;
|
||||
this.lastCanAttack = false;
|
||||
this.safeSpotCache = new HashMap();
|
||||
}
|
||||
|
||||
void updateNextAttack(Attack nextAttack, int ticksTillNextAttack)
|
||||
{
|
||||
this.nextAttack = nextAttack;
|
||||
this.ticksTillNextAttack = ticksTillNextAttack;
|
||||
}
|
||||
|
||||
private void updateNextAttack(Attack nextAttack)
|
||||
{
|
||||
this.nextAttack = nextAttack;
|
||||
}
|
||||
|
||||
boolean canAttack(Client client, WorldPoint target)
|
||||
{
|
||||
if (safeSpotCache.containsKey(target))
|
||||
{
|
||||
case NpcID.JALAKREKKET:
|
||||
attackTicks = 4;
|
||||
name = "lil mel";
|
||||
attackAnimation = 7582;
|
||||
attackstyle = Attackstyle.MELEE;
|
||||
priority = 7;
|
||||
break;
|
||||
return safeSpotCache.get(target) == 2;
|
||||
}
|
||||
|
||||
case NpcID.JALAKREKXIL:
|
||||
attackTicks = 4;
|
||||
name = "lil range";
|
||||
attackAnimation = 7583;
|
||||
attackstyle = Attackstyle.RANGE;
|
||||
priority = 6;
|
||||
break;
|
||||
boolean hasLos = new WorldArea(target, 1, 1).hasLineOfSightTo(client, this.getNpc().getWorldArea());
|
||||
boolean hasRange = this.getType().getDefaultAttack() == Attack.MELEE ? this.getNpc().getWorldArea().isInMeleeDistance(target)
|
||||
: this.getNpc().getWorldArea().distanceTo(target) <= this.getType().getRange();
|
||||
|
||||
case NpcID.JALAKREKMEJ:
|
||||
attackTicks = 4;
|
||||
name = "lil mage";
|
||||
attackAnimation = 7581;
|
||||
attackstyle = Attackstyle.MAGE;
|
||||
priority = 5;
|
||||
break;
|
||||
if (hasLos && hasRange)
|
||||
{
|
||||
safeSpotCache.put(target, 2);
|
||||
}
|
||||
|
||||
case NpcID.JALMEJRAH:
|
||||
attackTicks = 3;
|
||||
name = "bat";
|
||||
attackAnimation = 7578;
|
||||
attackstyle = Attackstyle.RANGE;
|
||||
priority = 4;
|
||||
break;
|
||||
return hasLos && hasRange;
|
||||
}
|
||||
|
||||
case NpcID.JALAK:
|
||||
attackTicks = 6;
|
||||
name = "blob";
|
||||
attackAnimation = 7583; // also 7581
|
||||
attackstyle = Attackstyle.RANDOM;
|
||||
priority = 3;
|
||||
break;
|
||||
boolean canMoveToAttack(Client client, WorldPoint target, List<WorldPoint> obstacles)
|
||||
{
|
||||
if (safeSpotCache.containsKey(target))
|
||||
{
|
||||
return safeSpotCache.get(target) == 1 || safeSpotCache.get(target) == 2;
|
||||
}
|
||||
|
||||
case NpcID.JALIMKOT:
|
||||
attackTicks = 4;
|
||||
name = "meleer";
|
||||
attackAnimation = 7597;
|
||||
attackstyle = Attackstyle.MELEE;
|
||||
priority = 2;
|
||||
break;
|
||||
final List<WorldPoint> realObstacles = new ArrayList<>();
|
||||
for (WorldPoint obstacle : obstacles)
|
||||
{
|
||||
if (this.getNpc().getWorldArea().toWorldPointList().contains(obstacle))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
case NpcID.JALXIL:
|
||||
attackTicks = 4;
|
||||
name = "ranger";
|
||||
attackAnimation = 7605;
|
||||
attackstyle = Attackstyle.RANGE;
|
||||
priority = 1;
|
||||
break;
|
||||
realObstacles.add(obstacle);
|
||||
}
|
||||
|
||||
case NpcID.JALZEK:
|
||||
attackTicks = 4;
|
||||
name = "mager";
|
||||
attackAnimation = 7610;
|
||||
attackstyle = Attackstyle.MAGE;
|
||||
priority = 0;
|
||||
break;
|
||||
final WorldArea targetArea = new WorldArea(target, 1, 1);
|
||||
WorldArea currentWorldArea = this.getNpc().getWorldArea();
|
||||
|
||||
default:
|
||||
attackTicks = 0;
|
||||
int steps = 0;
|
||||
while (true)
|
||||
{
|
||||
// Prevent infinite loop in case of pathfinding failure
|
||||
steps++;
|
||||
if (steps > 30)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final WorldArea predictedWorldArea = currentWorldArea.calculateNextTravellingPoint(client, targetArea, true, x ->
|
||||
{
|
||||
for (WorldPoint obstacle : realObstacles)
|
||||
{
|
||||
if (new WorldArea(x, 1, 1).intersectsWith(new WorldArea(obstacle, 1, 1)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
// Will only happen when NPC is underneath player or moving out of scene (but this will never show on overlay)
|
||||
if (predictedWorldArea == null)
|
||||
{
|
||||
safeSpotCache.put(target, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (predictedWorldArea == currentWorldArea)
|
||||
{
|
||||
safeSpotCache.put(target, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean hasLos = new WorldArea(target, 1, 1).hasLineOfSightTo(client, predictedWorldArea);
|
||||
boolean hasRange = this.getType().getDefaultAttack() == Attack.MELEE ? predictedWorldArea.isInMeleeDistance(target)
|
||||
: predictedWorldArea.distanceTo(target) <= this.getType().getRange();
|
||||
|
||||
if (hasLos && hasRange)
|
||||
{
|
||||
safeSpotCache.put(target, 1);
|
||||
return true;
|
||||
}
|
||||
|
||||
currentWorldArea = predictedWorldArea;
|
||||
}
|
||||
}
|
||||
|
||||
public String info()
|
||||
private boolean couldAttackPrevTick(Client client, WorldPoint lastPlayerLocation)
|
||||
{
|
||||
String info = "";
|
||||
|
||||
if (attacking)
|
||||
{
|
||||
info += ticksTillAttack;
|
||||
}
|
||||
//info += " D: " + distanceToPlayer;
|
||||
|
||||
return info;
|
||||
return new WorldArea(lastPlayerLocation, 1, 1).hasLineOfSightTo(client, this.getNpc().getWorldArea());
|
||||
}
|
||||
|
||||
void attacked()
|
||||
void gameTick(Client client, WorldPoint lastPlayerLocation, boolean finalPhase)
|
||||
{
|
||||
ticksTillAttack = attackTicks;
|
||||
attacking = true;
|
||||
safeSpotCache.clear();
|
||||
|
||||
if (ticksTillNextAttack > 0)
|
||||
{
|
||||
this.ticksTillNextAttack--;
|
||||
}
|
||||
|
||||
//Jad animation detection
|
||||
if (this.getType() == Type.JAD && this.getNpc().getAnimation() != -1 && this.getNpc().getAnimation() != this.lastAnimation)
|
||||
{
|
||||
final InfernoNPC.Attack currentAttack = InfernoNPC.Attack.attackFromId(this.getNpc().getAnimation());
|
||||
|
||||
if (currentAttack != null && currentAttack != Attack.UNKNOWN)
|
||||
{
|
||||
this.updateNextAttack(currentAttack, this.getType().getTicksAfterAnimation());
|
||||
}
|
||||
}
|
||||
|
||||
if (ticksTillNextAttack <= 0)
|
||||
{
|
||||
switch (this.getType())
|
||||
{
|
||||
case ZUK:
|
||||
if (this.getNpc().getAnimation() == AnimationID.TZKAL_ZUK)
|
||||
{
|
||||
if (finalPhase)
|
||||
{
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), 7);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), 10);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case JAD:
|
||||
if (this.getNextAttack() != Attack.UNKNOWN)
|
||||
{
|
||||
// Jad's cycle continuous after his animation + attack but there's no animation to alert it
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), 8);
|
||||
}
|
||||
break;
|
||||
case BLOB:
|
||||
//RS pathfinding + LOS = hell, so if it can attack you the tick you were on previously, start attack cycle
|
||||
if (!this.lastCanAttack && this.couldAttackPrevTick(client, lastPlayerLocation))
|
||||
{
|
||||
this.updateNextAttack(Attack.UNKNOWN, 3);
|
||||
}
|
||||
//If there's no animation when coming out of the safespot, the blob is detecting prayer
|
||||
else if (!this.lastCanAttack && this.canAttack(client, client.getLocalPlayer().getWorldLocation()))
|
||||
{
|
||||
this.updateNextAttack(Attack.UNKNOWN, 4);
|
||||
}
|
||||
//This will activate another attack cycle
|
||||
else if (this.getNpc().getAnimation() != -1)
|
||||
{
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
|
||||
}
|
||||
break;
|
||||
case BAT:
|
||||
// Range + LOS check for bat because it suffers from the defense animation bug, also dont activate on "stand" animation
|
||||
if (this.canAttack(client, client.getLocalPlayer().getWorldLocation())
|
||||
&& this.getNpc().getAnimation() != AnimationID.JAL_MEJRAH_STAND && this.getNpc().getAnimation() != -1)
|
||||
{
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
|
||||
}
|
||||
break;
|
||||
case MELEE:
|
||||
case RANGER:
|
||||
case MAGE:
|
||||
// For the meleer, ranger and mage the attack animation is always prioritized so only check for those
|
||||
// Normal attack animation, doesnt suffer from defense animation bug. Activate usual attack cycle
|
||||
if (this.getNpc().getAnimation() == AnimationID.JAL_IMKOT
|
||||
|| this.getNpc().getAnimation() == AnimationID.JAL_XIL_RANGE_ATTACK || this.getNpc().getAnimation() == AnimationID.JAL_XIL_MELEE_ATTACK
|
||||
|| this.getNpc().getAnimation() == AnimationID.JAL_ZEK_MAGE_ATTACK || this.getNpc().getAnimation() == AnimationID.JAL_ZEK_MELEE_ATTACK)
|
||||
{
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
|
||||
}
|
||||
// Burrow into ground animation for meleer
|
||||
else if (this.getNpc().getAnimation() == 7600)
|
||||
{
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), 12);
|
||||
}
|
||||
// Respawn enemy animation for mage
|
||||
else if (this.getNpc().getAnimation() == 7611)
|
||||
{
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), 8);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
if (this.getNpc().getAnimation() != -1)
|
||||
{
|
||||
// This will activate another attack cycle
|
||||
this.updateNextAttack(this.getType().getDefaultAttack(), this.getType().getTicksAfterAnimation());
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Blob prayer detection
|
||||
if (this.getType() == Type.BLOB && this.getTicksTillNextAttack() == 3
|
||||
&& client.getLocalPlayer().getWorldLocation().distanceTo(this.getNpc().getWorldArea()) <= Type.BLOB.getRange())
|
||||
{
|
||||
InfernoNPC.Attack nextBlobAttack = InfernoNPC.Attack.UNKNOWN;
|
||||
if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
|
||||
{
|
||||
nextBlobAttack = InfernoNPC.Attack.MAGIC;
|
||||
}
|
||||
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
|
||||
{
|
||||
nextBlobAttack = InfernoNPC.Attack.RANGED;
|
||||
}
|
||||
|
||||
this.updateNextAttack(nextBlobAttack);
|
||||
}
|
||||
|
||||
// This is for jad (jad's animation lasts till after the attack is launched, which fucks up the attack cycle)
|
||||
lastAnimation = this.getNpc().getAnimation();
|
||||
// This is for blob (to check if player just came out of safespot)
|
||||
lastCanAttack = this.canAttack(client, client.getLocalPlayer().getWorldLocation());
|
||||
}
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
enum Attack
|
||||
{
|
||||
MELEE(Prayer.PROTECT_FROM_MELEE,
|
||||
Color.ORANGE,
|
||||
Color.RED,
|
||||
new int[]{
|
||||
AnimationID.JAL_NIB,
|
||||
AnimationID.JAL_AK_MELEE_ATTACK,
|
||||
AnimationID.JAL_IMKOT,
|
||||
AnimationID.JAL_XIL_MELEE_ATTACK,
|
||||
AnimationID.JAL_ZEK_MELEE_ATTACK, //TODO: Yt-HurKot attack animation
|
||||
}),
|
||||
RANGED(Prayer.PROTECT_FROM_MISSILES,
|
||||
Color.GREEN,
|
||||
new Color(0, 128, 0),
|
||||
new int[]{
|
||||
AnimationID.JAL_MEJRAH,
|
||||
AnimationID.JAL_AK_RANGE_ATTACK,
|
||||
AnimationID.JAL_XIL_RANGE_ATTACK,
|
||||
AnimationID.JALTOK_JAD_RANGE_ATTACK,
|
||||
}),
|
||||
MAGIC(Prayer.PROTECT_FROM_MAGIC,
|
||||
Color.CYAN,
|
||||
Color.BLUE,
|
||||
new int[]{
|
||||
AnimationID.JAL_AK_MAGIC_ATTACK,
|
||||
AnimationID.JAL_ZEK_MAGE_ATTACK,
|
||||
AnimationID.JALTOK_JAD_MAGE_ATTACK
|
||||
}),
|
||||
UNKNOWN(null, Color.WHITE, Color.GRAY, new int[]{});
|
||||
|
||||
private final Prayer prayer;
|
||||
private final Color normalColor;
|
||||
private final Color criticalColor;
|
||||
private final int[] animationIds;
|
||||
|
||||
Attack(Prayer prayer, Color normalColor, Color criticalColor, int[] animationIds)
|
||||
{
|
||||
this.prayer = prayer;
|
||||
this.normalColor = normalColor;
|
||||
this.criticalColor = criticalColor;
|
||||
this.animationIds = animationIds;
|
||||
}
|
||||
|
||||
static Attack attackFromId(int animationId)
|
||||
{
|
||||
for (Attack attack : Attack.values())
|
||||
{
|
||||
if (ArrayUtils.contains(attack.getAnimationIds(), animationId))
|
||||
{
|
||||
return attack;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
enum Type
|
||||
{
|
||||
NIBBLER(new int[]{NpcID.JALNIB}, Attack.MELEE, 4, 99, 100),
|
||||
BAT(new int[]{NpcID.JALMEJRAH}, Attack.RANGED, 3, 4, 7),
|
||||
BLOB(new int[]{NpcID.JALAK}, Attack.UNKNOWN, 6, 15, 4),
|
||||
MELEE(new int[]{NpcID.JALIMKOT}, Attack.MELEE, 4, 1, 3),
|
||||
RANGER(new int[]{NpcID.JALXIL, NpcID.JALXIL_7702}, Attack.RANGED, 4, 98, 2),
|
||||
MAGE(new int[]{NpcID.JALZEK, NpcID.JALZEK_7703}, Attack.MAGIC, 4, 98, 1),
|
||||
JAD(new int[]{NpcID.JALTOKJAD, NpcID.JALTOKJAD_7704}, Attack.UNKNOWN, 3, 99, 0),
|
||||
HEALER_JAD(new int[]{NpcID.YTHURKOT, NpcID.YTHURKOT_7701, NpcID.YTHURKOT_7705}, Attack.MELEE, 4, 1, 6),
|
||||
ZUK(new int[]{NpcID.TZKALZUK}, Attack.UNKNOWN, 10, 99, 99),
|
||||
HEALER_ZUK(new int[]{NpcID.JALMEJJAK}, Attack.UNKNOWN, -1, 99, 100);
|
||||
|
||||
private final int[] npcIds;
|
||||
private final Attack defaultAttack;
|
||||
private final int ticksAfterAnimation;
|
||||
private final int range;
|
||||
private final int priority;
|
||||
|
||||
Type(int[] npcIds, Attack defaultAttack, int ticksAfterAnimation, int range, int priority)
|
||||
{
|
||||
this.npcIds = npcIds;
|
||||
this.defaultAttack = defaultAttack;
|
||||
this.ticksAfterAnimation = ticksAfterAnimation;
|
||||
this.range = range;
|
||||
this.priority = priority;
|
||||
}
|
||||
|
||||
static Type typeFromId(int npcId)
|
||||
{
|
||||
for (Type type : Type.values())
|
||||
{
|
||||
if (ArrayUtils.contains(type.getNpcIds(), npcId))
|
||||
{
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
|
||||
* 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.plugins.inferno;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.table.TableAlignment;
|
||||
import net.runelite.client.ui.overlay.components.table.TableComponent;
|
||||
|
||||
@Singleton
|
||||
public class InfernoNibblerOverlay extends Overlay
|
||||
{
|
||||
private final Client client;
|
||||
private final InfernoPlugin plugin;
|
||||
|
||||
private final PanelComponent panelComponent = new PanelComponent();
|
||||
|
||||
@Inject
|
||||
public InfernoNibblerOverlay(final Client client, final InfernoPlugin plugin)
|
||||
{
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
setPosition(OverlayPosition.TOP_LEFT);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!plugin.isDisplayNibblerOverlay() || plugin.getNibblers().size() == 0 || client.getMapRegions()[0] != 9043)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
panelComponent.getChildren().clear();
|
||||
TableComponent tableComponent = new TableComponent();
|
||||
tableComponent.setColumnAlignments(TableAlignment.LEFT, TableAlignment.RIGHT);
|
||||
|
||||
tableComponent.addRow("Nibblers Left: ", Integer.toString(plugin.getNibblers().size()));
|
||||
|
||||
panelComponent.getChildren().add(tableComponent);
|
||||
|
||||
return panelComponent.render(graphics);
|
||||
}
|
||||
}
|
||||
@@ -1,56 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Jacky <liangj97@gmail.com>
|
||||
* 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.plugins.inferno;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Font;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import java.awt.Rectangle;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Perspective;
|
||||
import net.runelite.api.Point;
|
||||
import net.runelite.api.Prayer;
|
||||
import net.runelite.api.coords.LocalPoint;
|
||||
import net.runelite.api.coords.WorldPoint;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.plugins.inferno.displaymodes.InfernoPrayerDisplayMode;
|
||||
import net.runelite.client.plugins.inferno.displaymodes.InfernoSafespotDisplayMode;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.OverlayPriority;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
|
||||
@Singleton
|
||||
public class InfernoOverlay extends Overlay
|
||||
{
|
||||
private final Client client;
|
||||
private static final int TICK_PIXEL_SIZE = 60;
|
||||
private static final int BOX_WIDTH = 10;
|
||||
private static final int BOX_HEIGHT = 5;
|
||||
|
||||
private final InfernoPlugin plugin;
|
||||
private final Client client;
|
||||
|
||||
@Inject
|
||||
public InfernoOverlay(final Client client, final InfernoPlugin plugin)
|
||||
private InfernoOverlay(final Client client, final InfernoPlugin plugin)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_SCENE);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
setPriority(OverlayPriority.HIGHEST);
|
||||
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
@@ -58,55 +48,426 @@ public class InfernoOverlay extends Overlay
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (!client.isInInstancedRegion() || client.getMapRegions()[0] != 9043)
|
||||
final Widget meleePrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MELEE);
|
||||
final Widget rangePrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
|
||||
final Widget magicPrayerWidget = client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC);
|
||||
|
||||
if (plugin.isIndicateObstacles())
|
||||
{
|
||||
return null;
|
||||
renderObstacles(graphics);
|
||||
}
|
||||
|
||||
for (InfernoNPC monster : plugin.getMonsters().values())
|
||||
if (plugin.getSafespotDisplayMode() == InfernoSafespotDisplayMode.AREA)
|
||||
{
|
||||
NPC npc = monster.getNpc();
|
||||
//if (npc == null || !config.showPrayer()) return;
|
||||
LocalPoint lp = npc.getLocalLocation();
|
||||
if (lp != null)
|
||||
renderAreaSafepots(graphics);
|
||||
}
|
||||
else if (plugin.getSafespotDisplayMode() == InfernoSafespotDisplayMode.INDIVIDUAL_TILES)
|
||||
{
|
||||
renderIndividualTilesSafespots(graphics);
|
||||
}
|
||||
|
||||
for (InfernoNPC infernoNPC : plugin.getInfernoNpcs())
|
||||
{
|
||||
if (infernoNPC.getNpc().getConvexHull() != null)
|
||||
{
|
||||
Point point = Perspective.localToCanvas(client, lp, client.getPlane(), npc.getLogicalHeight());
|
||||
if (point != null)
|
||||
if (plugin.isIndicateNonSafespotted() && plugin.isNormalSafespots(infernoNPC)
|
||||
&& infernoNPC.canAttack(client, client.getLocalPlayer().getWorldLocation()))
|
||||
{
|
||||
if (monster.getTicksTillAttack() == 1 || (monster.getName().equals("blob") && monster.getTicksTillAttack() <= 3))
|
||||
{
|
||||
renderTextLocation(graphics, monster, monster.info(), Color.GREEN);
|
||||
}
|
||||
else
|
||||
{
|
||||
renderTextLocation(graphics, monster, monster.info(), Color.RED);
|
||||
}
|
||||
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.RED);
|
||||
}
|
||||
if (plugin.isIndicateTemporarySafespotted() && plugin.isNormalSafespots(infernoNPC)
|
||||
&& infernoNPC.canMoveToAttack(client, client.getLocalPlayer().getWorldLocation(), plugin.getObstacles()))
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.YELLOW);
|
||||
}
|
||||
if (plugin.isIndicateSafespotted() && plugin.isNormalSafespots(infernoNPC))
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.GREEN);
|
||||
}
|
||||
if (plugin.isIndicateNibblers() && infernoNPC.getType() == InfernoNPC.Type.NIBBLER
|
||||
&& (!plugin.isIndicateCentralNibbler() || plugin.getCentralNibbler() != infernoNPC))
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
|
||||
}
|
||||
if (plugin.isIndicateCentralNibbler() && infernoNPC.getType() == InfernoNPC.Type.NIBBLER
|
||||
&& plugin.getCentralNibbler() == infernoNPC)
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.BLUE);
|
||||
}
|
||||
if (plugin.isIndicateActiveHealersJad() && infernoNPC.getType() == InfernoNPC.Type.HEALER_JAD
|
||||
&& infernoNPC.getNpc().getInteracting() != client.getLocalPlayer())
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
|
||||
}
|
||||
if (plugin.isIndicateActiveHealersZuk() && infernoNPC.getType() == InfernoNPC.Type.HEALER_ZUK
|
||||
&& infernoNPC.getNpc().getInteracting() != client.getLocalPlayer())
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, infernoNPC.getNpc().getConvexHull(), Color.CYAN);
|
||||
}
|
||||
}
|
||||
|
||||
if (plugin.isIndicateNpcPosition(infernoNPC))
|
||||
{
|
||||
renderNpcLocation(graphics, infernoNPC);
|
||||
}
|
||||
|
||||
if (plugin.isTicksOnNpc(infernoNPC) && infernoNPC.getTicksTillNextAttack() > 0)
|
||||
{
|
||||
renderTicksOnNpc(graphics, infernoNPC);
|
||||
}
|
||||
}
|
||||
|
||||
if ((plugin.getPrayerDisplayMode() == InfernoPrayerDisplayMode.PRAYER_TAB
|
||||
|| plugin.getPrayerDisplayMode() == InfernoPrayerDisplayMode.BOTH)
|
||||
&& (meleePrayerWidget != null && !meleePrayerWidget.isHidden()
|
||||
&& rangePrayerWidget != null && !rangePrayerWidget.isHidden()
|
||||
&& magicPrayerWidget != null && !magicPrayerWidget.isHidden()))
|
||||
{
|
||||
renderPrayerIconOverlay(graphics);
|
||||
|
||||
if (plugin.isDescendingBoxes())
|
||||
{
|
||||
renderDescendingBoxes(graphics);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// renders text location
|
||||
private static void renderTextLocation(Graphics2D graphics, InfernoNPC actor, String text, Color color)
|
||||
private void renderObstacles(Graphics2D graphics)
|
||||
{
|
||||
graphics.setFont(new Font("Arial", Font.BOLD, 15));
|
||||
Point textLocation = actor.getNpc().getCanvasTextLocation(graphics, text, actor.textLocHeight + 40);
|
||||
if (Strings.isNullOrEmpty(text))
|
||||
for (WorldPoint worldPoint : plugin.getObstacles())
|
||||
{
|
||||
return;
|
||||
}
|
||||
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
|
||||
|
||||
if (textLocation != null)
|
||||
{
|
||||
int x = textLocation.getX();
|
||||
int y = textLocation.getY();
|
||||
if (localPoint == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
graphics.setColor(Color.BLACK);
|
||||
graphics.drawString(text, x + 1, y + 1);
|
||||
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
|
||||
|
||||
graphics.setColor(color);
|
||||
graphics.drawString(text, x, y);
|
||||
if (tilePoly == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OverlayUtil.renderPolygon(graphics, tilePoly, Color.BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderAreaSafepots(Graphics2D graphics)
|
||||
{
|
||||
for (int safeSpotId : plugin.getSafeSpotAreas().keySet())
|
||||
{
|
||||
if (safeSpotId > 6)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Color colorEdge1 = null;
|
||||
Color colorEdge2 = null;
|
||||
Color colorFill = null;
|
||||
|
||||
switch (safeSpotId)
|
||||
{
|
||||
case 0:
|
||||
colorEdge1 = Color.WHITE;
|
||||
colorFill = Color.WHITE;
|
||||
break;
|
||||
case 1:
|
||||
colorEdge1 = Color.RED;
|
||||
colorFill = Color.RED;
|
||||
break;
|
||||
case 2:
|
||||
colorEdge1 = Color.GREEN;
|
||||
colorFill = Color.GREEN;
|
||||
break;
|
||||
case 3:
|
||||
colorEdge1 = Color.BLUE;
|
||||
colorFill = Color.BLUE;
|
||||
break;
|
||||
case 4:
|
||||
colorEdge1 = Color.RED;
|
||||
colorEdge2 = Color.GREEN;
|
||||
colorFill = Color.YELLOW;
|
||||
break;
|
||||
case 5:
|
||||
colorEdge1 = Color.RED;
|
||||
colorEdge2 = Color.BLUE;
|
||||
colorFill = new Color(255, 0, 255);
|
||||
break;
|
||||
case 6:
|
||||
colorEdge1 = Color.GREEN;
|
||||
colorEdge2 = Color.BLUE;
|
||||
colorFill = new Color(0, 255, 255);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
//Add all edges, calculate average edgeSize and indicate tiles
|
||||
final List<int[][]> allEdges = new ArrayList<>();
|
||||
int edgeSizeSquared = 0;
|
||||
|
||||
for (WorldPoint worldPoint : plugin.getSafeSpotAreas().get(safeSpotId))
|
||||
{
|
||||
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
|
||||
|
||||
if (localPoint == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
|
||||
|
||||
if (tilePoly == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OverlayUtil.renderAreaTilePolygon(graphics, tilePoly, colorFill);
|
||||
|
||||
final int[][] edge1 = new int[][]{{tilePoly.xpoints[0], tilePoly.ypoints[0]}, {tilePoly.xpoints[1], tilePoly.ypoints[1]}};
|
||||
edgeSizeSquared += Math.pow(tilePoly.xpoints[0] - tilePoly.xpoints[1], 2) + Math.pow(tilePoly.ypoints[0] - tilePoly.ypoints[1], 2);
|
||||
allEdges.add(edge1);
|
||||
final int[][] edge2 = new int[][]{{tilePoly.xpoints[1], tilePoly.ypoints[1]}, {tilePoly.xpoints[2], tilePoly.ypoints[2]}};
|
||||
edgeSizeSquared += Math.pow(tilePoly.xpoints[1] - tilePoly.xpoints[2], 2) + Math.pow(tilePoly.ypoints[1] - tilePoly.ypoints[2], 2);
|
||||
allEdges.add(edge2);
|
||||
final int[][] edge3 = new int[][]{{tilePoly.xpoints[2], tilePoly.ypoints[2]}, {tilePoly.xpoints[3], tilePoly.ypoints[3]}};
|
||||
edgeSizeSquared += Math.pow(tilePoly.xpoints[2] - tilePoly.xpoints[3], 2) + Math.pow(tilePoly.ypoints[2] - tilePoly.ypoints[3], 2);
|
||||
allEdges.add(edge3);
|
||||
final int[][] edge4 = new int[][]{{tilePoly.xpoints[3], tilePoly.ypoints[3]}, {tilePoly.xpoints[0], tilePoly.ypoints[0]}};
|
||||
edgeSizeSquared += Math.pow(tilePoly.xpoints[3] - tilePoly.xpoints[0], 2) + Math.pow(tilePoly.ypoints[3] - tilePoly.ypoints[0], 2);
|
||||
allEdges.add(edge4);
|
||||
}
|
||||
|
||||
if (allEdges.size() <= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
edgeSizeSquared /= allEdges.size();
|
||||
|
||||
//Find and indicate unique edges
|
||||
final int toleranceSquared = (int) Math.ceil(edgeSizeSquared / 6);
|
||||
|
||||
for (int i = 0; i < allEdges.size(); i++)
|
||||
{
|
||||
int[][] baseEdge = allEdges.get(i);
|
||||
|
||||
boolean duplicate = false;
|
||||
|
||||
for (int j = 0; j < allEdges.size(); j++)
|
||||
{
|
||||
if (i == j)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int[][] checkEdge = allEdges.get(j);
|
||||
|
||||
if (edgeEqualsEdge(baseEdge, checkEdge, toleranceSquared))
|
||||
{
|
||||
duplicate = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!duplicate)
|
||||
{
|
||||
OverlayUtil.renderFullLine(graphics, baseEdge, colorEdge1);
|
||||
|
||||
if (colorEdge2 != null)
|
||||
{
|
||||
OverlayUtil.renderDashedLine(graphics, baseEdge, colorEdge2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private void renderIndividualTilesSafespots(Graphics2D graphics)
|
||||
{
|
||||
for (WorldPoint worldPoint : plugin.getSafeSpotMap().keySet())
|
||||
{
|
||||
final int safeSpotId = plugin.getSafeSpotMap().get(worldPoint);
|
||||
|
||||
if (safeSpotId > 6)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final LocalPoint localPoint = LocalPoint.fromWorld(client, worldPoint);
|
||||
|
||||
if (localPoint == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final Polygon tilePoly = Perspective.getCanvasTilePoly(client, localPoint);
|
||||
|
||||
if (tilePoly == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Color color;
|
||||
switch (safeSpotId)
|
||||
{
|
||||
case 0:
|
||||
color = Color.WHITE;
|
||||
break;
|
||||
case 1:
|
||||
color = Color.RED;
|
||||
break;
|
||||
case 2:
|
||||
color = Color.GREEN;
|
||||
break;
|
||||
case 3:
|
||||
color = Color.BLUE;
|
||||
break;
|
||||
case 4:
|
||||
color = new Color(255, 255, 0);
|
||||
break;
|
||||
case 5:
|
||||
color = new Color(255, 0, 255);
|
||||
break;
|
||||
case 6:
|
||||
color = new Color(0, 255, 255);
|
||||
break;
|
||||
default:
|
||||
continue;
|
||||
}
|
||||
|
||||
OverlayUtil.renderPolygon(graphics, tilePoly, color);
|
||||
}
|
||||
}
|
||||
|
||||
private void renderTicksOnNpc(Graphics2D graphics, InfernoNPC infernoNPC)
|
||||
{
|
||||
final Color color = (infernoNPC.getTicksTillNextAttack() == 1
|
||||
|| (infernoNPC.getType() == InfernoNPC.Type.BLOB && infernoNPC.getTicksTillNextAttack() == 4))
|
||||
? infernoNPC.getNextAttack().getCriticalColor() : infernoNPC.getNextAttack().getNormalColor();
|
||||
final Point canvasPoint = infernoNPC.getNpc().getCanvasTextLocation(
|
||||
graphics, String.valueOf(infernoNPC.getTicksTillNextAttack()), 0);
|
||||
OverlayUtil.renderTextLocation(graphics, String.valueOf(infernoNPC.getTicksTillNextAttack()),
|
||||
plugin.getTextSize(), plugin.getFontStyle().getFont(), color, canvasPoint, false, 0);
|
||||
}
|
||||
|
||||
private void renderNpcLocation(Graphics2D graphics, InfernoNPC infernoNPC)
|
||||
{
|
||||
final LocalPoint localPoint = LocalPoint.fromWorld(client, infernoNPC.getNpc().getWorldLocation());
|
||||
|
||||
if (localPoint != null)
|
||||
{
|
||||
final Polygon tilePolygon = Perspective.getCanvasTilePoly(client, localPoint);
|
||||
|
||||
if (tilePolygon != null)
|
||||
{
|
||||
OverlayUtil.renderPolygon(graphics, tilePolygon, Color.BLUE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderDescendingBoxes(Graphics2D graphics)
|
||||
{
|
||||
for (Integer tick : plugin.getUpcomingAttacks().keySet())
|
||||
{
|
||||
final Map<InfernoNPC.Attack, Integer> attackPriority = plugin.getUpcomingAttacks().get(tick);
|
||||
int bestPriority = 999;
|
||||
InfernoNPC.Attack bestAttack = null;
|
||||
|
||||
for (Map.Entry<InfernoNPC.Attack, Integer> attackEntry : attackPriority.entrySet())
|
||||
{
|
||||
if (attackEntry.getValue() < bestPriority)
|
||||
{
|
||||
bestAttack = attackEntry.getKey();
|
||||
bestPriority = attackEntry.getValue();
|
||||
}
|
||||
}
|
||||
|
||||
for (InfernoNPC.Attack currentAttack : attackPriority.keySet())
|
||||
{
|
||||
//TODO: Config values for these colors
|
||||
final Color color = (tick == 1 && currentAttack == bestAttack) ? Color.RED : Color.ORANGE;
|
||||
final Widget prayerWidget = client.getWidget(currentAttack.getPrayer().getWidgetInfo());
|
||||
|
||||
int baseX = (int) prayerWidget.getBounds().getX();
|
||||
baseX += prayerWidget.getBounds().getWidth() / 2;
|
||||
baseX -= BOX_WIDTH / 2;
|
||||
|
||||
int baseY = (int) prayerWidget.getBounds().getY() - tick * TICK_PIXEL_SIZE - BOX_HEIGHT;
|
||||
baseY += TICK_PIXEL_SIZE - ((plugin.getLastTick() + 600 - System.currentTimeMillis()) / 600.0 * TICK_PIXEL_SIZE);
|
||||
|
||||
final Rectangle boxRectangle = new Rectangle(BOX_WIDTH, BOX_HEIGHT);
|
||||
boxRectangle.translate(baseX, baseY);
|
||||
|
||||
if (currentAttack == bestAttack)
|
||||
{
|
||||
OverlayUtil.renderFilledPolygon(graphics, boxRectangle, color);
|
||||
}
|
||||
else if (plugin.isIndicateNonPriorityDescendingBoxes())
|
||||
{
|
||||
OverlayUtil.renderOutlinePolygon(graphics, boxRectangle, color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderPrayerIconOverlay(Graphics2D graphics)
|
||||
{
|
||||
if (plugin.getClosestAttack() != null)
|
||||
{
|
||||
// Prayer indicator in prayer tab
|
||||
InfernoNPC.Attack prayerForAttack = null;
|
||||
if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
|
||||
{
|
||||
prayerForAttack = InfernoNPC.Attack.MAGIC;
|
||||
}
|
||||
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
|
||||
{
|
||||
prayerForAttack = InfernoNPC.Attack.RANGED;
|
||||
}
|
||||
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MELEE))
|
||||
{
|
||||
prayerForAttack = InfernoNPC.Attack.MELEE;
|
||||
}
|
||||
|
||||
if (plugin.getClosestAttack() != prayerForAttack || plugin.isIndicateWhenPrayingCorrectly())
|
||||
{
|
||||
final Widget prayerWidget = client.getWidget(plugin.getClosestAttack().getPrayer().getWidgetInfo());
|
||||
final Rectangle prayerRectangle = new Rectangle((int) prayerWidget.getBounds().getWidth(),
|
||||
(int) prayerWidget.getBounds().getHeight());
|
||||
prayerRectangle.translate((int) prayerWidget.getBounds().getX(), (int) prayerWidget.getBounds().getY());
|
||||
|
||||
//TODO: Config values for these colors
|
||||
Color prayerColor;
|
||||
if (plugin.getClosestAttack() == prayerForAttack)
|
||||
{
|
||||
prayerColor = Color.GREEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
prayerColor = Color.RED;
|
||||
}
|
||||
|
||||
OverlayUtil.renderOutlinePolygon(graphics, prayerRectangle, prayerColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean edgeEqualsEdge(int[][] edge1, int[][] edge2, int toleranceSquared)
|
||||
{
|
||||
return (pointEqualsPoint(edge1[0], edge2[0], toleranceSquared) && pointEqualsPoint(edge1[1], edge2[1], toleranceSquared))
|
||||
|| (pointEqualsPoint(edge1[0], edge2[1], toleranceSquared) && pointEqualsPoint(edge1[1], edge2[0], toleranceSquared));
|
||||
}
|
||||
|
||||
private boolean pointEqualsPoint(int[] point1, int[] point2, int toleranceSquared)
|
||||
{
|
||||
double distanceSquared = Math.pow(point1[0] - point2[0], 2) + Math.pow(point1[1] - point2[1], 2);
|
||||
|
||||
return distanceSquared <= toleranceSquared;
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,138 +0,0 @@
|
||||
package net.runelite.client.plugins.inferno;
|
||||
|
||||
import java.awt.Color;
|
||||
import java.awt.Dimension;
|
||||
import java.awt.Graphics2D;
|
||||
import java.awt.Polygon;
|
||||
import javax.inject.Inject;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Prayer;
|
||||
import net.runelite.api.widgets.Widget;
|
||||
import net.runelite.api.widgets.WidgetInfo;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayLayer;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.OverlayPriority;
|
||||
import net.runelite.client.ui.overlay.OverlayUtil;
|
||||
|
||||
public class InfernoPrayerOverlay extends Overlay
|
||||
{
|
||||
private static final int TICK_PIXEL_SIZE = 60;
|
||||
private static final int BLOB_WIDTH = 10;
|
||||
private static final int BLOB_HEIGHT = 5;
|
||||
|
||||
private final InfernoPlugin plugin;
|
||||
private final Client client;
|
||||
|
||||
@Inject
|
||||
private InfernoPrayerOverlay(final Client client, final InfernoPlugin plugin)
|
||||
{
|
||||
setPosition(OverlayPosition.DYNAMIC);
|
||||
setLayer(OverlayLayer.ABOVE_WIDGETS);
|
||||
setPriority(OverlayPriority.HIGHEST);
|
||||
|
||||
this.client = client;
|
||||
this.plugin = plugin;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension render(Graphics2D graphics)
|
||||
{
|
||||
if (client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC).isHidden()
|
||||
|| client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES).isHidden())
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
InfernoJad.Attack prayerForAttack = null;
|
||||
|
||||
if (client.isPrayerActive(Prayer.PROTECT_FROM_MAGIC))
|
||||
{
|
||||
prayerForAttack = InfernoJad.Attack.MAGIC;
|
||||
}
|
||||
else if (client.isPrayerActive(Prayer.PROTECT_FROM_MISSILES))
|
||||
{
|
||||
prayerForAttack = InfernoJad.Attack.RANGE;
|
||||
}
|
||||
|
||||
InfernoJad.Attack closestAttack = null;
|
||||
int leastTicks = 999;
|
||||
|
||||
for (InfernoJad jad : plugin.getJads())
|
||||
{
|
||||
if (jad.getNextAttack() == null || jad.getTicksTillNextAttack() < 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (jad.getTicksTillNextAttack() < leastTicks)
|
||||
{
|
||||
leastTicks = jad.getTicksTillNextAttack();
|
||||
closestAttack = jad.getNextAttack();
|
||||
}
|
||||
|
||||
if (!plugin.isDescendingBoxes() || !plugin.isShowPrayerHelp()
|
||||
|| (plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.PRAYER_TAB
|
||||
&& plugin.getPrayerOverlayMode() != InfernoPrayerOverlayMode.BOTH))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final Widget prayerWidget = jad.getNextAttack() == InfernoJad.Attack.MAGIC
|
||||
? client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC) : client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
|
||||
int baseX = (int) prayerWidget.getBounds().getX();
|
||||
baseX += prayerWidget.getBounds().getWidth() / 2;
|
||||
baseX -= BLOB_WIDTH / 2;
|
||||
|
||||
int baseY = (int) prayerWidget.getBounds().getY() - jad.getTicksTillNextAttack() * TICK_PIXEL_SIZE - BLOB_HEIGHT;
|
||||
baseY += TICK_PIXEL_SIZE - ((plugin.getLastTick() + 600 - System.currentTimeMillis()) / 600.0 * TICK_PIXEL_SIZE);
|
||||
|
||||
final Polygon blob = new Polygon(new int[]{0, BLOB_WIDTH, BLOB_WIDTH, 0}, new int[]{0, 0, BLOB_HEIGHT, BLOB_HEIGHT}, 4);
|
||||
blob.translate(baseX, baseY);
|
||||
|
||||
OverlayUtil.renderPolygon(graphics, blob, Color.ORANGE);
|
||||
}
|
||||
|
||||
if (plugin.isShowPrayerHelp() && closestAttack != null
|
||||
&& (closestAttack != prayerForAttack || plugin.isIndicateWhenPrayingCorrectly())
|
||||
&& (plugin.getPrayerOverlayMode() == InfernoPrayerOverlayMode.PRAYER_TAB
|
||||
|| plugin.getPrayerOverlayMode() == InfernoPrayerOverlayMode.BOTH))
|
||||
{
|
||||
final Widget prayerWidget = closestAttack == InfernoJad.Attack.MAGIC
|
||||
? client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MAGIC) : client.getWidget(WidgetInfo.PRAYER_PROTECT_FROM_MISSILES);
|
||||
final Polygon prayer = new Polygon(
|
||||
new int[]{0, (int) prayerWidget.getBounds().getWidth(), (int) prayerWidget.getBounds().getWidth(), 0},
|
||||
new int[]{0, 0, (int) prayerWidget.getBounds().getHeight(), (int) prayerWidget.getBounds().getHeight()},
|
||||
4);
|
||||
prayer.translate((int) prayerWidget.getBounds().getX(), (int) prayerWidget.getBounds().getY());
|
||||
|
||||
Color prayerColor;
|
||||
if (closestAttack == prayerForAttack)
|
||||
{
|
||||
prayerColor = Color.GREEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
prayerColor = Color.RED;
|
||||
}
|
||||
|
||||
OverlayUtil.renderPolygon(graphics, prayer, prayerColor);
|
||||
}
|
||||
|
||||
if (plugin.isIndicateActiveHealers())
|
||||
{
|
||||
for (NPC healer : plugin.getActiveHealers())
|
||||
{
|
||||
if (healer.getConvexHull() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
OverlayUtil.renderPolygon(graphics, healer.getConvexHull(), Color.CYAN);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
package net.runelite.client.plugins.inferno;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@RequiredArgsConstructor
|
||||
public enum InfernoPrayerOverlayMode
|
||||
{
|
||||
PRAYER_TAB("Prayer Tab"),
|
||||
BOTTOM_RIGHT("Bottom Right"),
|
||||
BOTH("Both");
|
||||
|
||||
private final String name;
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
/*
|
||||
* Copyright (c) 2019, Kyleeld <https://github.com/kyleeld>
|
||||
* Copyright (c) 2019, openosrs <https://openosrs.com>
|
||||
* Copyright (c) 2019, RuneLitePlus <https://runelitepl.us>
|
||||
*
|
||||
* All rights reserved.
|
||||
*
|
||||
@@ -28,18 +28,21 @@ package net.runelite.client.plugins.inferno;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import java.awt.Color;
|
||||
import java.util.Map;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import net.runelite.client.plugins.inferno.displaymodes.InfernoNamingDisplayMode;
|
||||
import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
|
||||
class InfernoWaveMappings
|
||||
{
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private static final ImmutableMap<Integer, int[]> waveMapping;
|
||||
|
||||
private static final Map<Integer, int[]> waveMapping;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private static final ImmutableMap<Integer, String> npcNameMapping;
|
||||
private static final Map<Integer, String> npcNameMappingComplex;
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private static final Map<Integer, String> npcNameMappingSimple;
|
||||
|
||||
static
|
||||
{
|
||||
@@ -108,30 +111,43 @@ class InfernoWaveMappings
|
||||
waveMapBuilder.put(61, new int[]{32, 32, 32, 85, 165, 240, 370, 490});
|
||||
waveMapBuilder.put(62, new int[]{32, 32, 32, 85, 85, 165, 240, 370, 490});
|
||||
waveMapBuilder.put(63, new int[]{32, 32, 32, 165, 165, 240, 370, 490});
|
||||
waveMapBuilder.put(64, new int[]{32, 32, 32, 85, 240, 240, 370, 490});
|
||||
waveMapBuilder.put(65, new int[]{32, 32, 32, 85, 370, 370, 490});
|
||||
waveMapBuilder.put(66, new int[]{32, 32, 32, 85, 490, 490});
|
||||
waveMapBuilder.put(64, new int[]{32, 32, 32, 240, 240, 370, 490});
|
||||
waveMapBuilder.put(65, new int[]{32, 32, 32, 370, 370, 490});
|
||||
waveMapBuilder.put(66, new int[]{32, 32, 32, 490, 490});
|
||||
waveMapBuilder.put(67, new int[]{900});
|
||||
waveMapBuilder.put(68, new int[]{900, 900, 900});
|
||||
waveMapBuilder.put(69, new int[]{1400});
|
||||
|
||||
waveMapping = waveMapBuilder.build();
|
||||
|
||||
ImmutableMap.Builder<Integer, String> nameMapBuilder = new ImmutableMap.Builder<>();
|
||||
ImmutableMap.Builder<Integer, String> nameMapBuilderSimple = new ImmutableMap.Builder<>();
|
||||
|
||||
nameMapBuilder.put(32, "Jal-Nib - Level 32");
|
||||
nameMapBuilder.put(85, "Jal-MejRah - Level 85");
|
||||
nameMapBuilder.put(165, "Jal-Ak - Level 165");
|
||||
nameMapBuilder.put(240, "Jal-ImKot - Level 240");
|
||||
nameMapBuilder.put(370, "Jal-Xil - Level 370");
|
||||
nameMapBuilder.put(490, "Jal-Zek - Level 490");
|
||||
nameMapBuilder.put(900, "JalTok-Jad - Level 900");
|
||||
nameMapBuilder.put(1400, "TzKal-Zuk - Level 1400");
|
||||
nameMapBuilderSimple.put(32, "Nibbler");
|
||||
nameMapBuilderSimple.put(85, "Bat");
|
||||
nameMapBuilderSimple.put(165, "Blob");
|
||||
nameMapBuilderSimple.put(240, "Meleer");
|
||||
nameMapBuilderSimple.put(370, "Ranger");
|
||||
nameMapBuilderSimple.put(490, "Mage");
|
||||
nameMapBuilderSimple.put(900, "Jad");
|
||||
nameMapBuilderSimple.put(1400, "Zuk");
|
||||
|
||||
npcNameMapping = nameMapBuilder.build();
|
||||
npcNameMappingSimple = nameMapBuilderSimple.build();
|
||||
|
||||
ImmutableMap.Builder<Integer, String> nameMapBuilderComplex = new ImmutableMap.Builder<>();
|
||||
|
||||
nameMapBuilderComplex.put(32, "Jal-Nib");
|
||||
nameMapBuilderComplex.put(85, "Jal-MejRah");
|
||||
nameMapBuilderComplex.put(165, "Jal-Ak");
|
||||
nameMapBuilderComplex.put(240, "Jal-ImKot");
|
||||
nameMapBuilderComplex.put(370, "Jal-Xil");
|
||||
nameMapBuilderComplex.put(490, "Jal-Zek");
|
||||
nameMapBuilderComplex.put(900, "JalTok-Jad");
|
||||
nameMapBuilderComplex.put(1400, "TzKal-Zuk");
|
||||
|
||||
npcNameMappingComplex = nameMapBuilderComplex.build();
|
||||
}
|
||||
|
||||
static void addWaveComponent(PanelComponent panelComponent, String header, int wave, Color titleColor, Color color)
|
||||
static void addWaveComponent(InfernoPlugin plugin, PanelComponent panelComponent, String header, int wave, Color titleColor, Color color)
|
||||
{
|
||||
int[] monsters = waveMapping.get(wave);
|
||||
|
||||
@@ -160,7 +176,23 @@ class InfernoWaveMappings
|
||||
|
||||
TitleComponent.TitleComponentBuilder builder = TitleComponent.builder();
|
||||
|
||||
builder.text(count + "x " + npcNameMapping.get(monsterType));
|
||||
String npcNameText = "";
|
||||
|
||||
if (plugin.getNpcNaming() == InfernoNamingDisplayMode.SIMPLE)
|
||||
{
|
||||
npcNameText += npcNameMappingSimple.get(monsterType);
|
||||
}
|
||||
else
|
||||
{
|
||||
npcNameText += npcNameMappingComplex.get(monsterType);
|
||||
}
|
||||
|
||||
if (plugin.isNpcLevels())
|
||||
{
|
||||
npcNameText += " (" + monsterType + ")";
|
||||
}
|
||||
|
||||
builder.text(count + "x " + npcNameText);
|
||||
builder.color(color);
|
||||
|
||||
panelComponent.getChildren().add(builder.build());
|
||||
|
||||
@@ -8,6 +8,7 @@ import java.awt.Graphics2D;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Setter;
|
||||
import static net.runelite.client.plugins.inferno.InfernoWaveMappings.addWaveComponent;
|
||||
import net.runelite.client.plugins.inferno.displaymodes.InfernoWaveDisplayMode;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import net.runelite.client.ui.overlay.OverlayPosition;
|
||||
import net.runelite.client.ui.overlay.OverlayPriority;
|
||||
@@ -47,6 +48,7 @@ public class InfernoWaveOverlay extends Overlay
|
||||
displayMode == InfernoWaveDisplayMode.BOTH)
|
||||
{
|
||||
addWaveComponent(
|
||||
plugin,
|
||||
panelComponent,
|
||||
"Current Wave (Wave " + plugin.getCurrentWaveNumber() + ")",
|
||||
plugin.getCurrentWaveNumber(),
|
||||
@@ -59,6 +61,7 @@ public class InfernoWaveOverlay extends Overlay
|
||||
displayMode == InfernoWaveDisplayMode.BOTH)
|
||||
{
|
||||
addWaveComponent(
|
||||
plugin,
|
||||
panelComponent,
|
||||
"Next Wave (Wave " + plugin.getNextWaveNumber() + ")",
|
||||
plugin.getNextWaveNumber(),
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package net.runelite.client.plugins.inferno.displaymodes;
|
||||
|
||||
public enum InfernoNamingDisplayMode
|
||||
{
|
||||
SIMPLE,
|
||||
COMPLEX
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package net.runelite.client.plugins.inferno.displaymodes;
|
||||
|
||||
public enum InfernoPrayerDisplayMode
|
||||
{
|
||||
PRAYER_TAB,
|
||||
BOTTOM_RIGHT,
|
||||
BOTH
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.runelite.client.plugins.inferno.displaymodes;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
public enum InfernoSafespotDisplayMode
|
||||
{
|
||||
OFF("Off"),
|
||||
INDIVIDUAL_TILES("Individual tiles"),
|
||||
AREA("Area (lower fps)");
|
||||
|
||||
final private String name;
|
||||
|
||||
InfernoSafespotDisplayMode(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@
|
||||
* (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.plugins.inferno;
|
||||
package net.runelite.client.plugins.inferno.displaymodes;
|
||||
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
package net.runelite.client.plugins.inferno.displaymodes;
|
||||
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
public enum InfernoZukShieldDisplayMode
|
||||
{
|
||||
OFF("Off"),
|
||||
LIVE("Live (follow shield)"),
|
||||
PREDICT("Predict (NOT WORKING YET)");
|
||||
|
||||
final private String name;
|
||||
|
||||
InfernoZukShieldDisplayMode(String name)
|
||||
{
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,7 @@
|
||||
*/
|
||||
package net.runelite.client.plugins.opponentinfo;
|
||||
|
||||
import java.awt.Color;
|
||||
import net.runelite.client.config.Config;
|
||||
import net.runelite.client.config.ConfigGroup;
|
||||
import net.runelite.client.config.ConfigItem;
|
||||
@@ -65,12 +66,47 @@ public interface OpponentInfoConfig extends Config
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showOpponentsInMenu",
|
||||
name = "Show opponents in menu",
|
||||
description = "Marks opponents names in the menu which you are attacking or are attacking you (NPC only)",
|
||||
position = 3
|
||||
keyName = "showAttackersMenu",
|
||||
name = "Show attackers in menu",
|
||||
description = "Marks attackers' names in menus with a *<br>",
|
||||
position = 3,
|
||||
warning = "NOTE: This'll also mark people who are following you/interacting with you in any other way. Don't blindly trust this in pvp!"
|
||||
)
|
||||
default boolean showOpponentsInMenu()
|
||||
default boolean showAttackersMenu()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showAttackingMenu",
|
||||
name = "Green main target",
|
||||
description = "Display main target's name colored in menus (Players and NPCs)",
|
||||
position = 4,
|
||||
warning = "NOTE: This'll also show green when following/interacting in any other way. Don't blindly trust this in pvp!"
|
||||
)
|
||||
default boolean showAttackingMenu()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "attackingColor",
|
||||
name = "Target color",
|
||||
description = "The color your target will be highlighted with",
|
||||
position = 5
|
||||
)
|
||||
default Color attackingColor()
|
||||
{
|
||||
return Color.GREEN;
|
||||
}
|
||||
|
||||
@ConfigItem(
|
||||
keyName = "showHitpointsMenu",
|
||||
name = "Show NPC hp in menu",
|
||||
description = "Show NPC hp in menu. Useful when barraging",
|
||||
position = 6
|
||||
)
|
||||
default boolean showHitpointsMenu()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -36,11 +36,7 @@ import javax.inject.Singleton;
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.Client;
|
||||
import static net.runelite.api.MenuOpcode.RUNELITE_OVERLAY_CONFIG;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.Varbits;
|
||||
import net.runelite.client.game.HiscoreManager;
|
||||
import net.runelite.client.game.NPCManager;
|
||||
import net.runelite.client.ui.overlay.Overlay;
|
||||
import static net.runelite.client.ui.overlay.OverlayManager.OPTION_CONFIGURE;
|
||||
import net.runelite.client.ui.overlay.OverlayMenuEntry;
|
||||
@@ -51,7 +47,6 @@ import net.runelite.client.ui.overlay.components.PanelComponent;
|
||||
import net.runelite.client.ui.overlay.components.ProgressBarComponent;
|
||||
import net.runelite.client.ui.overlay.components.TitleComponent;
|
||||
import net.runelite.api.util.Text;
|
||||
import net.runelite.http.api.hiscore.HiscoreResult;
|
||||
|
||||
@Singleton
|
||||
class OpponentInfoOverlay extends Overlay
|
||||
@@ -61,8 +56,6 @@ class OpponentInfoOverlay extends Overlay
|
||||
|
||||
private final Client client;
|
||||
private final OpponentInfoPlugin opponentInfoPlugin;
|
||||
private final HiscoreManager hiscoreManager;
|
||||
private final NPCManager npcManager;
|
||||
|
||||
private final PanelComponent panelComponent = new PanelComponent();
|
||||
|
||||
@@ -75,15 +68,11 @@ class OpponentInfoOverlay extends Overlay
|
||||
@Inject
|
||||
private OpponentInfoOverlay(
|
||||
final Client client,
|
||||
final OpponentInfoPlugin opponentInfoPlugin,
|
||||
final HiscoreManager hiscoreManager,
|
||||
final NPCManager npcManager)
|
||||
final OpponentInfoPlugin opponentInfoPlugin)
|
||||
{
|
||||
super(opponentInfoPlugin);
|
||||
this.client = client;
|
||||
this.opponentInfoPlugin = opponentInfoPlugin;
|
||||
this.hiscoreManager = hiscoreManager;
|
||||
this.npcManager = npcManager;
|
||||
|
||||
setPosition(OverlayPosition.TOP_LEFT);
|
||||
setPriority(OverlayPriority.HIGH);
|
||||
@@ -110,23 +99,7 @@ class OpponentInfoOverlay extends Overlay
|
||||
lastHealthScale = opponent.getHealth();
|
||||
opponentName = Text.removeTags(opponent.getName());
|
||||
|
||||
lastMaxHealth = -1;
|
||||
if (opponent instanceof NPC)
|
||||
{
|
||||
lastMaxHealth = npcManager.getHealth(((NPC) opponent).getId());
|
||||
}
|
||||
else if (opponent instanceof Player)
|
||||
{
|
||||
final HiscoreResult hiscoreResult = hiscoreManager.lookupAsync(opponentName, opponentInfoPlugin.getHiscoreEndpoint());
|
||||
if (hiscoreResult != null)
|
||||
{
|
||||
final int hp = hiscoreResult.getHitpoints().getLevel();
|
||||
if (hp > 0)
|
||||
{
|
||||
lastMaxHealth = hp;
|
||||
}
|
||||
}
|
||||
}
|
||||
lastMaxHealth = opponentInfoPlugin.getMaxHp(opponent);
|
||||
|
||||
final Actor opponentsOpponent = opponent.getInteracting();
|
||||
if (opponentsOpponent != null
|
||||
@@ -168,37 +141,7 @@ class OpponentInfoOverlay extends Overlay
|
||||
if ((displayStyle == HitpointsDisplayStyle.HITPOINTS || displayStyle == HitpointsDisplayStyle.BOTH)
|
||||
&& lastMaxHealth != -1)
|
||||
{
|
||||
// This is the reverse of the calculation of healthRatio done by the server
|
||||
// which is: healthRatio = 1 + (healthScale - 1) * health / maxHealth (if health > 0, 0 otherwise)
|
||||
// It's able to recover the exact health if maxHealth <= healthScale.
|
||||
int health = 0;
|
||||
if (lastRatio > 0)
|
||||
{
|
||||
int minHealth = 1;
|
||||
int maxHealth;
|
||||
if (lastHealthScale > 1)
|
||||
{
|
||||
if (lastRatio > 1)
|
||||
{
|
||||
// This doesn't apply if healthRatio = 1, because of the special case in the server calculation that
|
||||
// health = 0 forces healthRatio = 0 instead of the expected healthRatio = 1
|
||||
minHealth = (lastMaxHealth * (lastRatio - 1) + lastHealthScale - 2) / (lastHealthScale - 1);
|
||||
}
|
||||
maxHealth = (lastMaxHealth * lastRatio - 1) / (lastHealthScale - 1);
|
||||
if (maxHealth > lastMaxHealth)
|
||||
{
|
||||
maxHealth = lastMaxHealth;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If healthScale is 1, healthRatio will always be 1 unless health = 0
|
||||
// so we know nothing about the upper limit except that it can't be higher than maxHealth
|
||||
maxHealth = lastMaxHealth;
|
||||
}
|
||||
// Take the average of min and max possible healths
|
||||
health = (minHealth + maxHealth + 1) / 2;
|
||||
}
|
||||
int health = getExactHp(lastRatio, lastHealthScale, lastMaxHealth);
|
||||
|
||||
// Show both the hitpoint and percentage values if enabled in the config
|
||||
final ProgressBarComponent.LabelDisplayMode progressBarDisplayMode = displayStyle == HitpointsDisplayStyle.BOTH ?
|
||||
@@ -229,4 +172,47 @@ class OpponentInfoOverlay extends Overlay
|
||||
|
||||
return panelComponent.render(graphics);
|
||||
}
|
||||
|
||||
static int getExactHp(int ratio, int health, int maxHp)
|
||||
{
|
||||
if (ratio < 0 || health <= 0 || maxHp == -1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int exactHealth = 0;
|
||||
|
||||
// This is the reverse of the calculation of healthRatio done by the server
|
||||
// which is: healthRatio = 1 + (healthScale - 1) * health / maxHealth (if health > 0, 0 otherwise)
|
||||
// It's able to recover the exact health if maxHealth <= healthScale.
|
||||
if (ratio > 0)
|
||||
{
|
||||
int minHealth = 1;
|
||||
int maxHealth;
|
||||
if (health > 1)
|
||||
{
|
||||
if (ratio > 1)
|
||||
{
|
||||
// This doesn't apply if healthRatio = 1, because of the special case in the server calculation that
|
||||
// health = 0 forces healthRatio = 0 instead of the expected healthRatio = 1
|
||||
minHealth = (maxHp * (ratio - 1) + health - 2) / (health - 1);
|
||||
}
|
||||
maxHealth = (maxHp * ratio - 1) / (health - 1);
|
||||
if (maxHealth > maxHp)
|
||||
{
|
||||
maxHealth = maxHp;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// If healthScale is 1, healthRatio will always be 1 unless health = 0
|
||||
// so we know nothing about the upper limit except that it can't be higher than maxHealth
|
||||
maxHealth = maxHp;
|
||||
}
|
||||
// Take the average of min and max possible healths
|
||||
exactHealth = (minHealth + maxHealth + 1) / 2;
|
||||
}
|
||||
|
||||
return exactHealth;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,24 +33,33 @@ import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import lombok.AccessLevel;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import net.runelite.api.Actor;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.GameState;
|
||||
import net.runelite.api.MenuEntry;
|
||||
import net.runelite.api.MenuOpcode;
|
||||
import net.runelite.api.NPC;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.WorldType;
|
||||
import net.runelite.api.events.BeforeRender;
|
||||
import net.runelite.api.events.ConfigChanged;
|
||||
import net.runelite.api.events.GameStateChanged;
|
||||
import net.runelite.api.events.GameTick;
|
||||
import net.runelite.api.events.InteractingChanged;
|
||||
import net.runelite.api.events.MenuEntryAdded;
|
||||
import net.runelite.api.events.MenuOpened;
|
||||
import net.runelite.api.util.Text;
|
||||
import net.runelite.client.config.ConfigManager;
|
||||
import net.runelite.client.eventbus.EventBus;
|
||||
import net.runelite.client.game.HiscoreManager;
|
||||
import net.runelite.client.game.NPCManager;
|
||||
import net.runelite.client.menus.MenuManager;
|
||||
import net.runelite.client.plugins.Plugin;
|
||||
import net.runelite.client.plugins.PluginDescriptor;
|
||||
import net.runelite.client.ui.overlay.OverlayManager;
|
||||
import net.runelite.client.util.ColorUtil;
|
||||
import net.runelite.http.api.hiscore.HiscoreEndpoint;
|
||||
import net.runelite.http.api.hiscore.HiscoreResult;
|
||||
|
||||
@PluginDescriptor(
|
||||
name = "Opponent Information",
|
||||
@@ -58,9 +67,12 @@ import net.runelite.http.api.hiscore.HiscoreEndpoint;
|
||||
tags = {"combat", "health", "hitpoints", "npcs", "overlay"}
|
||||
)
|
||||
@Singleton
|
||||
@Slf4j
|
||||
public class OpponentInfoPlugin extends Plugin
|
||||
{
|
||||
private static final Duration WAIT = Duration.ofSeconds(5);
|
||||
private static final Object MENU = new Object();
|
||||
private static final int COLOR_TAG_LENGTH = 12;
|
||||
|
||||
@Inject
|
||||
private Client client;
|
||||
@@ -80,6 +92,15 @@ public class OpponentInfoPlugin extends Plugin
|
||||
@Inject
|
||||
private EventBus eventBus;
|
||||
|
||||
@Inject
|
||||
private HiscoreManager hiscoreManager;
|
||||
|
||||
@Inject
|
||||
private MenuManager menuManager;
|
||||
|
||||
@Inject
|
||||
private NPCManager npcManager;
|
||||
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private HiscoreEndpoint hiscoreEndpoint = HiscoreEndpoint.NORMAL;
|
||||
|
||||
@@ -95,6 +116,11 @@ public class OpponentInfoPlugin extends Plugin
|
||||
@Getter(AccessLevel.PACKAGE)
|
||||
private boolean showOpponentsOpponent;
|
||||
|
||||
private String attackingColTag;
|
||||
private boolean showAttackers;
|
||||
private boolean showAttacking;
|
||||
private boolean showHitpoints;
|
||||
|
||||
@Provides
|
||||
OpponentInfoConfig provideConfig(ConfigManager configManager)
|
||||
{
|
||||
@@ -104,6 +130,11 @@ public class OpponentInfoPlugin extends Plugin
|
||||
@Override
|
||||
protected void startUp() throws Exception
|
||||
{
|
||||
this.attackingColTag = ColorUtil.colorTag(config.attackingColor());
|
||||
this.showAttackers = config.showAttackersMenu();
|
||||
this.showAttacking = config.showAttackingMenu();
|
||||
this.showHitpoints = config.showHitpointsMenu();
|
||||
|
||||
updateConfig();
|
||||
addSubscriptions();
|
||||
|
||||
@@ -115,6 +146,7 @@ public class OpponentInfoPlugin extends Plugin
|
||||
protected void shutDown() throws Exception
|
||||
{
|
||||
eventBus.unregister(this);
|
||||
eventBus.unregister(MENU);
|
||||
|
||||
lastOpponent = null;
|
||||
lastTime = null;
|
||||
@@ -128,7 +160,20 @@ public class OpponentInfoPlugin extends Plugin
|
||||
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
|
||||
eventBus.subscribe(InteractingChanged.class, this, this::onInteractingChanged);
|
||||
eventBus.subscribe(GameTick.class, this, this::onGameTick);
|
||||
eventBus.subscribe(MenuEntryAdded.class, this, this::onMenuEntryAdded);
|
||||
updateMenuSubs();
|
||||
}
|
||||
|
||||
private void updateMenuSubs()
|
||||
{
|
||||
if (showAttackers || showAttacking || showHitpoints)
|
||||
{
|
||||
eventBus.subscribe(BeforeRender.class, MENU, this::onBeforeRender);
|
||||
eventBus.subscribe(MenuOpened.class, MENU, this::onMenuOpened);
|
||||
}
|
||||
else
|
||||
{
|
||||
eventBus.unregister(MENU);
|
||||
}
|
||||
}
|
||||
|
||||
private void onGameStateChanged(GameStateChanged gameStateChanged)
|
||||
@@ -194,6 +239,25 @@ public class OpponentInfoPlugin extends Plugin
|
||||
return;
|
||||
}
|
||||
|
||||
switch (event.getKey())
|
||||
{
|
||||
case "showAttackersMenu":
|
||||
this.showAttackers = config.showAttackersMenu();
|
||||
updateMenuSubs();
|
||||
break;
|
||||
case "showAttackingMenu":
|
||||
this.showAttacking = config.showAttackingMenu();
|
||||
updateMenuSubs();
|
||||
break;
|
||||
case "showHitpointsMenu":
|
||||
this.showHitpoints = config.showHitpointsMenu();
|
||||
updateMenuSubs();
|
||||
break;
|
||||
case "attackingColor":
|
||||
attackingColTag = ColorUtil.colorTag(config.attackingColor());
|
||||
break;
|
||||
}
|
||||
|
||||
updateConfig();
|
||||
}
|
||||
|
||||
@@ -204,27 +268,253 @@ public class OpponentInfoPlugin extends Plugin
|
||||
this.showOpponentsOpponent = config.showOpponentsOpponent();
|
||||
}
|
||||
|
||||
private void onMenuEntryAdded(MenuEntryAdded menuEntryAdded)
|
||||
private void onBeforeRender(BeforeRender event)
|
||||
{
|
||||
if (menuEntryAdded.getOpcode() != MenuOpcode.NPC_SECOND_OPTION.getId()
|
||||
|| !menuEntryAdded.getOption().equals("Attack")
|
||||
|| !config.showOpponentsInMenu())
|
||||
if (client.getMenuOptionCount() <= 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int npcIndex = menuEntryAdded.getIdentifier();
|
||||
NPC npc = client.getCachedNPCs()[npcIndex];
|
||||
if (npc == null)
|
||||
if (client.isMenuOpen())
|
||||
{
|
||||
boolean changed = false;
|
||||
final MenuEntry[] entries = client.getMenuEntries();
|
||||
for (final MenuEntry entry : entries)
|
||||
{
|
||||
changed |= fixup(entry);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
client.setMenuEntries(entries);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc.getInteracting() == client.getLocalPlayer() || lastOpponent == npc)
|
||||
final MenuEntry entry = client.getLeftClickMenuEntry();
|
||||
if (modify(entry))
|
||||
{
|
||||
MenuEntry[] menuEntries = client.getMenuEntries();
|
||||
menuEntries[menuEntries.length - 1].setTarget("*" + menuEntryAdded.getTarget());
|
||||
client.setMenuEntries(menuEntries);
|
||||
client.setLeftClickMenuEntry(entry);
|
||||
}
|
||||
}
|
||||
|
||||
private void onMenuOpened(MenuOpened event)
|
||||
{
|
||||
boolean changed = false;
|
||||
for (MenuEntry entry : event.getMenuEntries())
|
||||
{
|
||||
changed |= modify(entry);
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
event.setModified();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean modify(MenuEntry entry)
|
||||
{
|
||||
if (isNotAttackEntry(entry))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean changed = false;
|
||||
|
||||
int index = entry.getIdentifier();
|
||||
Actor actor = getActorFromIndex(index);
|
||||
|
||||
if (actor == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor instanceof Player)
|
||||
{
|
||||
index -= 32768;
|
||||
}
|
||||
|
||||
String target = entry.getTarget();
|
||||
|
||||
if (showAttacking &&
|
||||
client.getLocalPlayer().getRSInteracting() == index)
|
||||
{
|
||||
target = attackingColTag + target.substring(COLOR_TAG_LENGTH);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (showAttackers &&
|
||||
actor.getRSInteracting() - 32768 == client.getLocalPlayerIndex())
|
||||
{
|
||||
target = '*' + target;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (showHitpoints &&
|
||||
actor.getHealth() > 0)
|
||||
{
|
||||
int lvlIndex = target.lastIndexOf("(level-");
|
||||
if (lvlIndex != -1)
|
||||
{
|
||||
String levelReplacement = getHpString(actor, true);
|
||||
|
||||
target = target.substring(0, lvlIndex) + levelReplacement;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
entry.setTarget(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean fixup(MenuEntry entry)
|
||||
{
|
||||
if (isNotAttackEntry(entry))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int index = entry.getIdentifier();
|
||||
|
||||
Actor actor = getActorFromIndex(index);
|
||||
|
||||
if (actor == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (actor instanceof Player)
|
||||
{
|
||||
index -= 32768;
|
||||
}
|
||||
|
||||
String target = entry.getTarget();
|
||||
|
||||
boolean hasAggro = actor.getRSInteracting() - 32768 == client.getLocalPlayerIndex();
|
||||
boolean hadAggro = target.charAt(0) == '*';
|
||||
boolean isTarget = client.getLocalPlayer().getRSInteracting() == index;
|
||||
boolean hasHp = showHitpoints && actor instanceof NPC && actor.getHealth() > 0;
|
||||
|
||||
boolean aggroChanged = showAttackers && hasAggro != hadAggro;
|
||||
boolean targetChanged = showAttacking && isTarget != target.startsWith(attackingColTag, aggroChanged ? 1 : 0);
|
||||
boolean hpChanged = showHitpoints && hasHp == target.contains("(level-");
|
||||
|
||||
if (!aggroChanged &&
|
||||
!targetChanged &&
|
||||
!hasHp &&
|
||||
!hpChanged)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetChanged)
|
||||
{
|
||||
boolean player = actor instanceof Player;
|
||||
final int start = hadAggro ? 1 + COLOR_TAG_LENGTH : COLOR_TAG_LENGTH;
|
||||
target =
|
||||
(hasAggro ? '*' : "") +
|
||||
(isTarget ? attackingColTag :
|
||||
player ? ColorUtil.colorStartTag(0xffffff) : ColorUtil.colorStartTag(0xffff00)) +
|
||||
target.substring(start);
|
||||
}
|
||||
else if (aggroChanged)
|
||||
{
|
||||
if (hasAggro)
|
||||
{
|
||||
target = '*' + target;
|
||||
}
|
||||
else
|
||||
{
|
||||
target = target.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (hpChanged || hasHp)
|
||||
{
|
||||
final int braceIdx;
|
||||
|
||||
if (!hasHp)
|
||||
{
|
||||
braceIdx = target.lastIndexOf("<col=ff0000>(");
|
||||
if (braceIdx != -1)
|
||||
{
|
||||
target = target.substring(0, braceIdx - 1) + "(level-" + actor.getCombatLevel() + ")";
|
||||
}
|
||||
}
|
||||
else if ((braceIdx = target.lastIndexOf('(')) != -1)
|
||||
{
|
||||
final String hpString = getHpString(actor, hpChanged);
|
||||
|
||||
target = target.substring(0, braceIdx) + hpString;
|
||||
}
|
||||
}
|
||||
|
||||
entry.setTarget(target);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isNotAttackEntry(MenuEntry entry)
|
||||
{
|
||||
return entry.getOpcode() != MenuOpcode.NPC_SECOND_OPTION.getId() &&
|
||||
entry.getOpcode() != menuManager.getPlayerAttackOpcode()
|
||||
|| !entry.getOption().equals("Attack");
|
||||
}
|
||||
|
||||
private String getHpString(Actor actor, boolean withColorTag)
|
||||
{
|
||||
int maxHp = getMaxHp(actor);
|
||||
int health = actor.getHealth();
|
||||
int ratio = actor.getHealthRatio();
|
||||
|
||||
final String result;
|
||||
if (maxHp != -1)
|
||||
{
|
||||
final int exactHealth = OpponentInfoOverlay.getExactHp(ratio, health, maxHp);
|
||||
result = "(" + exactHealth + "/" + maxHp + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
result = "(" + (100 * ratio) / health + "%)";
|
||||
}
|
||||
|
||||
return withColorTag ? ColorUtil.colorStartTag(0xff0000) + result : result;
|
||||
}
|
||||
|
||||
int getMaxHp(Actor actor)
|
||||
{
|
||||
if (actor instanceof NPC)
|
||||
{
|
||||
return npcManager.getHealth(((NPC) actor).getId());
|
||||
}
|
||||
else
|
||||
{
|
||||
final HiscoreResult hiscoreResult = hiscoreManager.lookupAsync(Text.removeTags(actor.getName()), getHiscoreEndpoint());
|
||||
if (hiscoreResult != null)
|
||||
{
|
||||
final int hp = hiscoreResult.getHitpoints().getLevel();
|
||||
if (hp > 0)
|
||||
{
|
||||
return hp;
|
||||
}
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private Actor getActorFromIndex(int index)
|
||||
{
|
||||
if (index < 32768)
|
||||
{
|
||||
return client.getCachedNPCs()[index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return client.getCachedPlayers()[index - 32768];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,49 @@ public class OverlayUtil
|
||||
graphics.setStroke(originalStroke);
|
||||
}
|
||||
|
||||
public static void renderOutlinePolygon(Graphics2D graphics, Shape poly, Color color)
|
||||
{
|
||||
graphics.setColor(color);
|
||||
final Stroke originalStroke = graphics.getStroke();
|
||||
graphics.setStroke(new BasicStroke(2));
|
||||
graphics.draw(poly);
|
||||
graphics.setStroke(originalStroke);
|
||||
}
|
||||
|
||||
public static void renderFilledPolygon(Graphics2D graphics, Shape poly, Color color)
|
||||
{
|
||||
graphics.setColor(color);
|
||||
final Stroke originalStroke = graphics.getStroke();
|
||||
graphics.setStroke(new BasicStroke(2));
|
||||
graphics.draw(poly);
|
||||
graphics.fill(poly);
|
||||
graphics.setStroke(originalStroke);
|
||||
}
|
||||
|
||||
public static void renderAreaTilePolygon(Graphics2D graphics, Shape poly, Color color)
|
||||
{
|
||||
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), 10));
|
||||
graphics.fill(poly);
|
||||
}
|
||||
|
||||
public static void renderFullLine(Graphics2D graphics, int[][] line, Color color)
|
||||
{
|
||||
graphics.setColor(color);
|
||||
final Stroke originalStroke = graphics.getStroke();
|
||||
graphics.setStroke(new BasicStroke(2));
|
||||
graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]);
|
||||
graphics.setStroke(originalStroke);
|
||||
}
|
||||
|
||||
public static void renderDashedLine(Graphics2D graphics, int[][] line, Color color)
|
||||
{
|
||||
graphics.setColor(color);
|
||||
final Stroke originalStroke = graphics.getStroke();
|
||||
graphics.setStroke(new BasicStroke(2, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL, 0, new float[]{9}, 0));
|
||||
graphics.drawLine(line[0][0], line[0][1], line[1][0], line[1][1]);
|
||||
graphics.setStroke(originalStroke);
|
||||
}
|
||||
|
||||
public static void renderPolygonThin(Graphics2D graphics, Polygon poly, Color color)
|
||||
{
|
||||
graphics.setColor(color);
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package net.runelite.client.util;
|
||||
|
||||
import java.awt.Polygon;
|
||||
import java.time.Duration;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import net.runelite.api.Client;
|
||||
import net.runelite.api.Player;
|
||||
import net.runelite.api.WorldType;
|
||||
@@ -16,6 +18,17 @@ public class MiscUtils
|
||||
private static final Polygon abovePoly = new Polygon(abovePointsX, abovePointsY, abovePointsX.length);
|
||||
private static final Polygon belowPoly = new Polygon(belowPointsX, belowPointsY, belowPointsX.length);
|
||||
|
||||
private static final ChronoUnit[] ORDERED_CHRONOS = new ChronoUnit[]
|
||||
{
|
||||
ChronoUnit.YEARS,
|
||||
ChronoUnit.MONTHS,
|
||||
ChronoUnit.WEEKS,
|
||||
ChronoUnit.DAYS,
|
||||
ChronoUnit.HOURS,
|
||||
ChronoUnit.MINUTES,
|
||||
ChronoUnit.SECONDS
|
||||
};
|
||||
|
||||
//test replacement so private for now
|
||||
private static boolean inWildy(WorldPoint point)
|
||||
{
|
||||
@@ -86,4 +99,75 @@ public class MiscUtils
|
||||
|
||||
//return getWildernessLevelFrom(client, localPlayer.getWorldLocation()) > 0;
|
||||
}
|
||||
|
||||
public static String formatTimeAgo(Duration dur)
|
||||
{
|
||||
long dA = 0, dB = 0, rm;
|
||||
ChronoUnit cA = null, cB = null;
|
||||
for (int i = 0; i < ORDERED_CHRONOS.length; i++)
|
||||
{
|
||||
cA = ORDERED_CHRONOS[i];
|
||||
dA = dur.getSeconds() / cA.getDuration().getSeconds();
|
||||
rm = dur.getSeconds() % cA.getDuration().getSeconds();
|
||||
if (dA <= 0)
|
||||
{
|
||||
cA = null;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (i + 1 < ORDERED_CHRONOS.length)
|
||||
{
|
||||
cB = ORDERED_CHRONOS[i + 1];
|
||||
dB = rm / cB.getDuration().getSeconds();
|
||||
|
||||
if (dB <= 0)
|
||||
{
|
||||
cB = null;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (cA == null)
|
||||
{
|
||||
return "just now.";
|
||||
}
|
||||
|
||||
String str = formatUnit(cA, dA);
|
||||
|
||||
if (cB != null)
|
||||
{
|
||||
str += " and " + formatUnit(cB, dB);
|
||||
}
|
||||
|
||||
return str + " ago.";
|
||||
}
|
||||
|
||||
private static String formatUnit(ChronoUnit chrono, long val)
|
||||
{
|
||||
boolean multiple = val != 1;
|
||||
String str;
|
||||
if (multiple)
|
||||
{
|
||||
str = val + " ";
|
||||
}
|
||||
else
|
||||
{
|
||||
str = "a" + (chrono == ChronoUnit.HOURS ? "n " : " ");
|
||||
}
|
||||
str += chrono.name().toLowerCase();
|
||||
if (!multiple)
|
||||
{
|
||||
if (str.charAt(str.length() - 1) == 's')
|
||||
{
|
||||
str = str.substring(0, str.length() - 1);
|
||||
}
|
||||
}
|
||||
else if (str.charAt(str.length() - 1) != 's')
|
||||
{
|
||||
str += "s";
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user