|
|
|
|
@@ -1,4 +1,6 @@
|
|
|
|
|
/*
|
|
|
|
|
* Copyright (c) 2019, Owain van Brakel <https://github.com/Owain94>
|
|
|
|
|
* Copyright (c) 2019, TheStonedTurtle <https://github.com/TheStonedTurtle>
|
|
|
|
|
* Copyright (c) 2018, Cameron <https://github.com/noremac201>, SoyChai <https://github.com/SoyChai>
|
|
|
|
|
* All rights reserved.
|
|
|
|
|
*
|
|
|
|
|
@@ -25,8 +27,6 @@
|
|
|
|
|
package net.runelite.client.plugins.experiencedrop;
|
|
|
|
|
|
|
|
|
|
import com.google.inject.Provides;
|
|
|
|
|
import java.time.Duration;
|
|
|
|
|
import java.time.Instant;
|
|
|
|
|
import java.util.Arrays;
|
|
|
|
|
import java.util.EnumMap;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
@@ -36,43 +36,29 @@ import lombok.AccessLevel;
|
|
|
|
|
import lombok.Getter;
|
|
|
|
|
import net.runelite.api.Actor;
|
|
|
|
|
import net.runelite.api.Client;
|
|
|
|
|
import net.runelite.api.GameState;
|
|
|
|
|
import net.runelite.api.NPC;
|
|
|
|
|
import net.runelite.api.Player;
|
|
|
|
|
import static net.runelite.api.ScriptID.XPDROP_DISABLED;
|
|
|
|
|
import net.runelite.api.Skill;
|
|
|
|
|
import net.runelite.api.SpriteID;
|
|
|
|
|
import net.runelite.api.VarPlayer;
|
|
|
|
|
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.InteractingChanged;
|
|
|
|
|
import net.runelite.api.events.VarbitChanged;
|
|
|
|
|
import net.runelite.api.events.ScriptCallbackEvent;
|
|
|
|
|
import net.runelite.api.events.WidgetHiddenChanged;
|
|
|
|
|
import net.runelite.api.widgets.Widget;
|
|
|
|
|
import net.runelite.api.widgets.WidgetID;
|
|
|
|
|
import net.runelite.api.widgets.WidgetInfo;
|
|
|
|
|
import net.runelite.client.config.ConfigManager;
|
|
|
|
|
import net.runelite.client.eventbus.Subscribe;
|
|
|
|
|
import net.runelite.client.game.HiscoreManager;
|
|
|
|
|
import net.runelite.client.game.NPCManager;
|
|
|
|
|
import net.runelite.client.plugins.Plugin;
|
|
|
|
|
import net.runelite.client.plugins.PluginDescriptor;
|
|
|
|
|
import net.runelite.client.plugins.attackstyles.AttackStyle;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.ACCURATE;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.AGGRESSIVE;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.CASTING;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.CONTROLLED;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.DEFENSIVE_CASTING;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.LONGRANGE;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.OTHER;
|
|
|
|
|
import static net.runelite.client.plugins.attackstyles.AttackStyle.RANGING;
|
|
|
|
|
import net.runelite.client.plugins.attackstyles.WeaponType;
|
|
|
|
|
import net.runelite.client.ui.overlay.OverlayManager;
|
|
|
|
|
import net.runelite.client.util.Text;
|
|
|
|
|
import net.runelite.http.api.hiscore.HiscoreEndpoint;
|
|
|
|
|
import net.runelite.http.api.hiscore.HiscoreResult;
|
|
|
|
|
import net.runelite.client.util.ColorUtil;
|
|
|
|
|
|
|
|
|
|
@PluginDescriptor(
|
|
|
|
|
name = "XP Drop",
|
|
|
|
|
@@ -82,7 +68,8 @@ import net.runelite.http.api.hiscore.HiscoreResult;
|
|
|
|
|
public class XpDropPlugin extends Plugin
|
|
|
|
|
{
|
|
|
|
|
private static final int XPDROP_PADDING = 2; // space between xp drop icons
|
|
|
|
|
private static final Duration WAIT = Duration.ofSeconds(5);
|
|
|
|
|
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;
|
|
|
|
|
@@ -90,29 +77,8 @@ public class XpDropPlugin extends Plugin
|
|
|
|
|
@Inject
|
|
|
|
|
private XpDropConfig config;
|
|
|
|
|
|
|
|
|
|
private int tickCounter = 0;
|
|
|
|
|
private int previousExpGained;
|
|
|
|
|
private boolean hasHit = false;
|
|
|
|
|
private boolean hasDropped = false;
|
|
|
|
|
private boolean correctPrayer;
|
|
|
|
|
private Skill lastSkill = null;
|
|
|
|
|
private Map<Skill, Integer> previousSkillExpTable = new EnumMap<>(Skill.class);
|
|
|
|
|
private PrayerType currentTickPrayer;
|
|
|
|
|
private AttackStyle attackStyle;
|
|
|
|
|
private int attackStyleVarbit = -1;
|
|
|
|
|
private int equippedWeaponTypeVarbit = -1;
|
|
|
|
|
private int castingModeVarbit = -1;
|
|
|
|
|
private int opponentHealth = -1;
|
|
|
|
|
private int xpGains = 0;
|
|
|
|
|
private AttackStyle[] offensiveStyles = {ACCURATE, AGGRESSIVE, DEFENSIVE, CONTROLLED, RANGING, LONGRANGE, CASTING, DEFENSIVE_CASTING};
|
|
|
|
|
|
|
|
|
|
@Getter(AccessLevel.PACKAGE)
|
|
|
|
|
private int damage = 0;
|
|
|
|
|
|
|
|
|
|
@Getter(AccessLevel.PACKAGE)
|
|
|
|
|
private Actor lastOpponent;
|
|
|
|
|
|
|
|
|
|
private Instant lastTime;
|
|
|
|
|
@Inject
|
|
|
|
|
private NPCManager npcManager;
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
|
private OverlayManager overlayManager;
|
|
|
|
|
@@ -120,11 +86,23 @@ public class XpDropPlugin extends Plugin
|
|
|
|
|
@Inject
|
|
|
|
|
private XpDropOverlay overlay;
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
|
private NPCManager npcManager;
|
|
|
|
|
@Getter(AccessLevel.PACKAGE)
|
|
|
|
|
private int damage = 0;
|
|
|
|
|
|
|
|
|
|
@Inject
|
|
|
|
|
private HiscoreManager hiscoreManager;
|
|
|
|
|
@Getter(AccessLevel.PACKAGE)
|
|
|
|
|
private int tickShow = 0;
|
|
|
|
|
|
|
|
|
|
@Getter(AccessLevel.PACKAGE)
|
|
|
|
|
private Actor lastOpponent;
|
|
|
|
|
|
|
|
|
|
private int tickCounter = 0;
|
|
|
|
|
private int previousExpGained;
|
|
|
|
|
private boolean hasDropped = false;
|
|
|
|
|
private boolean correctPrayer;
|
|
|
|
|
private Skill lastSkill = null;
|
|
|
|
|
private Map<Skill, Integer> previousSkillExpTable = new EnumMap<>(Skill.class);
|
|
|
|
|
private PrayerType currentTickPrayer;
|
|
|
|
|
private XpDropConfig.DamageMode damageMode;
|
|
|
|
|
|
|
|
|
|
@Provides
|
|
|
|
|
XpDropConfig provideConfig(ConfigManager configManager)
|
|
|
|
|
@@ -135,20 +113,56 @@ public class XpDropPlugin extends Plugin
|
|
|
|
|
@Override
|
|
|
|
|
protected void startUp() throws Exception
|
|
|
|
|
{
|
|
|
|
|
lastOpponent = null;
|
|
|
|
|
overlayManager.add(overlay);
|
|
|
|
|
if (client.getGameState() == GameState.LOGGED_IN)
|
|
|
|
|
damageMode = config.showDamage();
|
|
|
|
|
|
|
|
|
|
if (damageMode == XpDropConfig.DamageMode.ABOVE_OPPONENT)
|
|
|
|
|
{
|
|
|
|
|
attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE);
|
|
|
|
|
equippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE);
|
|
|
|
|
castingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE);
|
|
|
|
|
updateAttackStyle(
|
|
|
|
|
equippedWeaponTypeVarbit,
|
|
|
|
|
attackStyleVarbit,
|
|
|
|
|
castingModeVarbit);
|
|
|
|
|
overlayManager.add(overlay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
protected void shutDown() throws Exception
|
|
|
|
|
{
|
|
|
|
|
overlayManager.remove(overlay);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Subscribe
|
|
|
|
|
public void onConfigChanged(ConfigChanged event)
|
|
|
|
|
{
|
|
|
|
|
if (!event.getGroup().equals("xpdrop"))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (damageMode != XpDropConfig.DamageMode.ABOVE_OPPONENT)
|
|
|
|
|
{
|
|
|
|
|
damageMode = config.showDamage();
|
|
|
|
|
|
|
|
|
|
if (damageMode == XpDropConfig.DamageMode.ABOVE_OPPONENT)
|
|
|
|
|
{
|
|
|
|
|
overlayManager.add(overlay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
damageMode = config.showDamage();
|
|
|
|
|
|
|
|
|
|
if (damageMode != XpDropConfig.DamageMode.ABOVE_OPPONENT)
|
|
|
|
|
{
|
|
|
|
|
overlayManager.remove(overlay);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Subscribe
|
|
|
|
|
public void onGameStateChanged(GameStateChanged event)
|
|
|
|
|
{
|
|
|
|
|
damage = 0;
|
|
|
|
|
tickShow = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Subscribe
|
|
|
|
|
public void onWidgetHiddenChanged(WidgetHiddenChanged event)
|
|
|
|
|
{
|
|
|
|
|
@@ -277,40 +291,11 @@ public class XpDropPlugin extends Plugin
|
|
|
|
|
@Subscribe
|
|
|
|
|
public void onGameTick(GameTick tick)
|
|
|
|
|
{
|
|
|
|
|
// Detect hitting a 0
|
|
|
|
|
if (lastOpponent != null)
|
|
|
|
|
{
|
|
|
|
|
int health = calculateHealth(lastOpponent);
|
|
|
|
|
if (health != -1 && opponentHealth != -1 && health == opponentHealth && hasHit)
|
|
|
|
|
{
|
|
|
|
|
damage = 0;
|
|
|
|
|
hasHit = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
lastOpponent = client.getLocalPlayer().getInteracting();
|
|
|
|
|
|
|
|
|
|
// Handle getting XP gains
|
|
|
|
|
if (hasDropped)
|
|
|
|
|
if (tickShow > 0)
|
|
|
|
|
{
|
|
|
|
|
if (xpGains != 0 && attackStyle.getSkills().length > 1 && attackStyle != LONGRANGE)
|
|
|
|
|
{
|
|
|
|
|
damage = (int) (xpGains / (attackStyle.getSkills().length * 1.3));
|
|
|
|
|
}
|
|
|
|
|
else if (xpGains != 0)
|
|
|
|
|
{
|
|
|
|
|
damage = xpGains / 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
xpGains = 0;
|
|
|
|
|
hasDropped = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Clear opponent
|
|
|
|
|
if (lastOpponent != null && lastTime != null && client.getLocalPlayer().getInteracting() == null)
|
|
|
|
|
{
|
|
|
|
|
if (Duration.between(lastTime, Instant.now()).compareTo(WAIT) > 0)
|
|
|
|
|
{
|
|
|
|
|
lastOpponent = null;
|
|
|
|
|
}
|
|
|
|
|
tickShow--;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
currentTickPrayer = getActivePrayerType();
|
|
|
|
|
@@ -350,120 +335,95 @@ public class XpDropPlugin extends Plugin
|
|
|
|
|
Integer previous = previousSkillExpTable.put(skill, xp);
|
|
|
|
|
if (previous != null)
|
|
|
|
|
{
|
|
|
|
|
opponentHealth = calculateHealth(lastOpponent);
|
|
|
|
|
previousExpGained = xp - previous;
|
|
|
|
|
if (skill != Skill.HITPOINTS && Arrays.stream(offensiveStyles).anyMatch(attackStyle::equals))
|
|
|
|
|
{
|
|
|
|
|
xpGains += previousExpGained;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hasDropped = true;
|
|
|
|
|
hasHit = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void updateAttackStyle(int equippedWeaponType, int attackStyleIndex, int castingMode)
|
|
|
|
|
{
|
|
|
|
|
AttackStyle[] attackStyles = WeaponType.getWeaponType(equippedWeaponType).getAttackStyles();
|
|
|
|
|
if (attackStyleIndex < attackStyles.length)
|
|
|
|
|
{
|
|
|
|
|
attackStyle = attackStyles[attackStyleIndex];
|
|
|
|
|
if (attackStyle == null)
|
|
|
|
|
{
|
|
|
|
|
attackStyle = OTHER;
|
|
|
|
|
}
|
|
|
|
|
else if ((attackStyle == CASTING) && (castingMode == 1))
|
|
|
|
|
{
|
|
|
|
|
attackStyle = DEFENSIVE_CASTING;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Subscribe
|
|
|
|
|
public void onInteractingChanged(InteractingChanged event)
|
|
|
|
|
public void onScriptCallbackEvent(ScriptCallbackEvent e)
|
|
|
|
|
{
|
|
|
|
|
if (event.getSource() != client.getLocalPlayer())
|
|
|
|
|
if (config.showDamage() == XpDropConfig.DamageMode.NONE)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Actor opponent = event.getTarget();
|
|
|
|
|
final String eventName = e.getEventName();
|
|
|
|
|
|
|
|
|
|
if (opponent == null)
|
|
|
|
|
// Handles Fake XP drops (Ironman, DMM Cap, 200m xp, etc)
|
|
|
|
|
if (eventName.equals("fakeXpDrop"))
|
|
|
|
|
{
|
|
|
|
|
lastTime = Instant.now();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
else if (opponent.getName().equalsIgnoreCase("fishing spot"))
|
|
|
|
|
{
|
|
|
|
|
lastTime = Instant.now().minus(WAIT);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
final int[] intStack = client.getIntStack();
|
|
|
|
|
final int intStackSize = client.getIntStackSize();
|
|
|
|
|
|
|
|
|
|
damage = 0;
|
|
|
|
|
lastOpponent = opponent;
|
|
|
|
|
opponentHealth = calculateHealth(opponent);
|
|
|
|
|
}
|
|
|
|
|
final int skillId = intStack[intStackSize - 2];
|
|
|
|
|
final Skill skill = Skill.values()[skillId];
|
|
|
|
|
|
|
|
|
|
private int calculateHealth(Actor target)
|
|
|
|
|
{
|
|
|
|
|
if (target == null || target.getName() == null)
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final int healthScale = target.getHealth();
|
|
|
|
|
final int healthRatio = target.getHealthRatio();
|
|
|
|
|
final String targetName = Text.removeTags(target.getName());
|
|
|
|
|
|
|
|
|
|
Integer maxHealth = -1;
|
|
|
|
|
if (target instanceof NPC)
|
|
|
|
|
{
|
|
|
|
|
maxHealth = npcManager.getHealth(targetName, target.getCombatLevel());
|
|
|
|
|
}
|
|
|
|
|
else if (target instanceof Player)
|
|
|
|
|
{
|
|
|
|
|
final HiscoreResult hiscoreResult = hiscoreManager.lookupAsync(targetName, HiscoreEndpoint.NORMAL);
|
|
|
|
|
if (hiscoreResult != null)
|
|
|
|
|
if (skill.equals(Skill.HITPOINTS))
|
|
|
|
|
{
|
|
|
|
|
final int hp = hiscoreResult.getHitpoints().getLevel();
|
|
|
|
|
if (hp > 0)
|
|
|
|
|
{
|
|
|
|
|
maxHealth = hp;
|
|
|
|
|
}
|
|
|
|
|
final int exp = intStack[intStackSize - 1];
|
|
|
|
|
calculateDamageDealt(exp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (healthRatio < 0 || healthScale <= 0 || maxHealth == null)
|
|
|
|
|
client.setIntStackSize(intStackSize - 2);
|
|
|
|
|
}
|
|
|
|
|
else if (eventName.equals("hpXpGained"))
|
|
|
|
|
{
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
final int[] intStack = client.getIntStack();
|
|
|
|
|
final int intStackSize = client.getIntStackSize();
|
|
|
|
|
|
|
|
|
|
return (int) ((maxHealth * healthRatio / healthScale) + 0.5f);
|
|
|
|
|
final int exp = intStack[intStackSize - 1];
|
|
|
|
|
calculateDamageDealt(exp);
|
|
|
|
|
}
|
|
|
|
|
else if (eventName.equals("xpDropAddDamage") &&
|
|
|
|
|
damageMode == XpDropConfig.DamageMode.IN_XP_DROP &&
|
|
|
|
|
damage > 0)
|
|
|
|
|
{
|
|
|
|
|
final String[] stringStack = client.getStringStack();
|
|
|
|
|
final int stringStackSize = client.getStringStackSize();
|
|
|
|
|
|
|
|
|
|
StringBuilder builder = new StringBuilder()
|
|
|
|
|
.append(stringStack[stringStackSize - 1])
|
|
|
|
|
.append(ColorUtil.colorTag(config.getDamageColor()))
|
|
|
|
|
.append(" (").append(damage).append(")");
|
|
|
|
|
|
|
|
|
|
stringStack[stringStackSize - 1] = builder.toString();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Subscribe
|
|
|
|
|
public void onVarbitChanged(VarbitChanged event)
|
|
|
|
|
private void calculateDamageDealt(int diff)
|
|
|
|
|
{
|
|
|
|
|
if (attackStyleVarbit == -1 || attackStyleVarbit != client.getVar(VarPlayer.ATTACK_STYLE))
|
|
|
|
|
double damageDealt = diff / HITPOINT_RATIO;
|
|
|
|
|
// DeadMan mode has an XP modifier
|
|
|
|
|
if (client.getWorldType().contains(WorldType.DEADMAN))
|
|
|
|
|
{
|
|
|
|
|
attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE);
|
|
|
|
|
updateAttackStyle(client.getVar(Varbits.EQUIPPED_WEAPON_TYPE), attackStyleVarbit,
|
|
|
|
|
client.getVar(Varbits.DEFENSIVE_CASTING_MODE));
|
|
|
|
|
damageDealt = damageDealt / DMM_MULTIPLIER_RATIO;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (equippedWeaponTypeVarbit == -1 || equippedWeaponTypeVarbit != client.getVar(Varbits.EQUIPPED_WEAPON_TYPE))
|
|
|
|
|
// Some NPCs have an XP modifier, account for it here.
|
|
|
|
|
Actor a = client.getLocalPlayer().getInteracting();
|
|
|
|
|
if (!(a instanceof NPC) && !(a instanceof Player))
|
|
|
|
|
{
|
|
|
|
|
equippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE);
|
|
|
|
|
updateAttackStyle(equippedWeaponTypeVarbit, client.getVar(VarPlayer.ATTACK_STYLE),
|
|
|
|
|
client.getVar(Varbits.DEFENSIVE_CASTING_MODE));
|
|
|
|
|
// 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);
|
|
|
|
|
tickShow = 3;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
a = lastOpponent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (castingModeVarbit == -1 || castingModeVarbit != client.getVar(Varbits.DEFENSIVE_CASTING_MODE))
|
|
|
|
|
if (a instanceof Player)
|
|
|
|
|
{
|
|
|
|
|
castingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE);
|
|
|
|
|
updateAttackStyle(client.getVar(Varbits.EQUIPPED_WEAPON_TYPE), client.getVar(VarPlayer.ATTACK_STYLE),
|
|
|
|
|
castingModeVarbit);
|
|
|
|
|
damage = (int) Math.rint(damageDealt);
|
|
|
|
|
tickShow = 3;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
NPC target = (NPC) a;
|
|
|
|
|
damage = (int) Math.rint(damageDealt / npcManager.getXpModifier(target.getId()));
|
|
|
|
|
tickShow = 3;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|