Simplify xp drop damage

This commit is contained in:
Scott Burns
2019-05-23 01:06:25 +02:00
committed by Lucas
parent 39116d5a64
commit 587b89e957
3 changed files with 218 additions and 175 deletions

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2018, TheStonedTurtle <https://github.com/TheStonedTurtle>
* 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.experiencedrop;
import java.util.HashMap;
import java.util.Map;
import lombok.Getter;
import net.runelite.api.NpcID;
/**
* The Experience received from damaging an NPC is actually based on the NPCs stats.
* For stronger NPCs this can cause you to gain more XP for the same damage dealt.
*/
@Getter
enum NpcExpModifier
{
// TODO: Confirm all NPC ids, some are probably incorrect or just useless
// Common
DAGANNOTH_REX(255, 255, 255, 255, 255, 255, 255, 0, 0, NpcID.DAGANNOTH_REX),
DAGANNOTH_PRIME(255, 255, 255, 255, 255, 255, 255, 0, 0, NpcID.DAGANNOTH_PRIME),
DAGANNOTH_SUPREME(225, 255, 128, 255, 10, 10, 10, 0, 0, NpcID.DAGANNOTH_SUPREME),
GIANT_MOLE(200, 200, 200, 200, 60, 80, 100, 0, 0, NpcID.GIANT_MOLE),
KQ(300, 300, 300, 255, 50, 50, 10, 0, 0, NpcID.KALPHITE_QUEEN, NpcID.KALPHITE_QUEEN_963, NpcID.KALPHITE_QUEEN_965),
KQ_2(300, 300, 300, 255, 100, 100, 100, 0, 0, NpcID.KALPHITE_QUEEN_4303, NpcID.KALPHITE_QUEEN_4304),
// Other
VORKATH(560, 308, 214, 750, 26, 108, 108, 16, 0, NpcID.VORKATH, NpcID.VORKATH_8058, NpcID.VORKATH_8059, NpcID.VORKATH_8060, NpcID.VORKATH_8061),
// All Zulrah phase stats are the same for the values we care about
ZULRAH(1, 1, 300, 500, 0, 0, 0, 0, 0, NpcID.ZULRAH, NpcID.ZULRAH_2043, NpcID.ZULRAH_2044),
// Sporadic
OBOR(90, 100, 60, 120, 35, 40, 45, 100, 68, NpcID.OBOR),
BRYOPHYTA(130, 100, 100, 115, 0, 0, 0, 33, 31, NpcID.BRYOPHYTA),
SKOTIZO(240, 250, 200, 450, 80, 80, 80, 160, 31, NpcID.SKOTIZO),
// GWD
KREEARRA(300, 200, 260, 255, 180, 180, 180, 136, 12, NpcID.KREEARRA),
GENERAL_GRAARDOR(280, 350, 250, 255, 90, 90, 90, 120, 43, NpcID.GENERAL_GRAARDOR),
COMMANDER_ZILYANA(280, 196, 300, 255, 100, 100, 100, 195, 20, NpcID.COMMANDER_ZILYANA),
KRIL_TSUTSAROTH(340, 300, 270, 255, 80, 80, 80, 160, 31, NpcID.KRIL_TSUTSAROTH),
// Slayer
DUSK(200, 1400, 100, 450, 0, 0, 0, 0, 0, NpcID.DUSK, NpcID.DUSK_7851, NpcID.DUSK_7854, NpcID.DUSK_7855,
NpcID.DUSK_7882, NpcID.DUSK_7883, NpcID.DUSK_7886, NpcID.DUSK_7887, NpcID.DUSK_7888, NpcID.DUSK_7889),
DAWN(140, 140, 100, 450, 0, 0, 0, 0, 0, NpcID.DAWN, NpcID.DAWN_7852, NpcID.DAWN_7853, NpcID.DAWN_7884, NpcID.DAWN_7885),
ABYSSAL_SIRE(180, 136, 250, 350, 40, 60, 50, 65, 0, NpcID.ABYSSAL_SIRE, NpcID.ABYSSAL_SIRE_5908,
NpcID.ABYSSAL_SIRE_5887, NpcID.ABYSSAL_SIRE_5888, NpcID.ABYSSAL_SIRE_5889, NpcID.ABYSSAL_SIRE_5890, NpcID.ABYSSAL_SIRE_5891),
KRAKEN(1, 1, 1, 255, 0, 0, 0, 0, 0, NpcID.KRAKEN, NpcID.KRAKEN_6640, NpcID.KRAKEN_6656),
CERBERUS(220, 220, 100, 600, 50, 100, 25, 50, 0, NpcID.CERBERUS, NpcID.CERBERUS_5863, NpcID.CERBERUS_5866),
SMOKE_DEVIL(240, 220, 360, 240, 11, 4, 9, 0, 0, NpcID.THERMONUCLEAR_SMOKE_DEVIL),
// Theatre of Blood
MAIDEN(350, 350, 200, 3500, 0, 0, 0, 0, 0,
NpcID.THE_MAIDEN_OF_SUGADINTI, NpcID.THE_MAIDEN_OF_SUGADINTI_8361, NpcID.THE_MAIDEN_OF_SUGADINTI_8362,
NpcID.THE_MAIDEN_OF_SUGADINTI_8363, NpcID.THE_MAIDEN_OF_SUGADINTI_8364, NpcID.THE_MAIDEN_OF_SUGADINTI_8365),
BLOAT(250, 340, 100, 2000, 40, 20, 40, 82, 150, NpcID.PESTILENT_BLOAT),
NYLOCAS(400, 350, 50, 2500, 0, 0, 0, 60, 0,
NpcID.NYLOCAS_VASILIAS, NpcID.NYLOCAS_VASILIAS_8355, NpcID.NYLOCAS_VASILIAS_8356, NpcID.NYLOCAS_VASILIAS_8357),
SOTETSEG(250, 250, 200, 4000, 70, 70, 70, 49, 0, NpcID.SOTETSEG, NpcID.SOTETSEG_8388),
XARPUS(1, 1, 250, 5080, 0, 0, 0, 0, 0, NpcID.XARPUS, NpcID.XARPUS_8339, NpcID.XARPUS_8340, NpcID.XARPUS_8341),
VERZIK(400, 400, 20, 2000, 20, 20, 20, 0, 0, NpcID.VERZIK_VITUR_8370),
VERZIK_2(400, 400, 200, 3250, 100, 60, 100, 0, 0, NpcID.VERZIK_VITUR_8372),
VERZIK_3(400, 400, 150, 3250, 70, 30, 70, 30, 80, NpcID.VERZIK_VITUR_8374);
private final int attack;
private final int strength;
private final int defence;
private final int hp;
private final int stab;
private final int slash;
private final int crush;
private final int bonusStrength;
private final int bonusAttack;
private final int[] npcIDs;
private final static Map<Integer, Double> XP_MODIFIER_MAP = new HashMap<>();
static
{
for (net.runelite.client.plugins.experiencedrop.NpcExpModifier m : values())
{
final double bonus = m.calculateXpModifier();
for (int id : m.getNpcIDs())
{
XP_MODIFIER_MAP.put(id, bonus);
}
}
}
NpcExpModifier(int attack, int strength, int defence, int hp, int stab, int slash, int crush, int bonusStrength, int bonusAttack, int... npcIDs)
{
this.attack = attack;
this.strength = strength;
this.defence = defence;
this.hp = hp;
this.stab = stab;
this.slash = slash;
this.crush = crush;
this.bonusStrength = bonusStrength;
this.bonusAttack = bonusAttack;
this.npcIDs = npcIDs;
}
/**
* Based off the formula found here: http://services.runescape.com/m=forum/c=PLuJ4cy6gtA/forums.ws?317,318,712,65587452,209,337584542#209
* @return bonus XP modifier
*/
private double calculateXpModifier()
{
final double averageLevel = Math.floor((attack + strength + defence + hp) / 4);
final double averageDefBonus = Math.floor((stab + slash + crush) / 3);
return (1 + Math.floor(averageLevel * (averageDefBonus + bonusStrength + bonusAttack) / 5120) / 40);
}
public static double getByNpcId(final int npcID)
{
return XP_MODIFIER_MAP.getOrDefault(npcID, 1.0);
}
}

View File

@@ -51,7 +51,7 @@ class XpDropOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
if (config.showDamage())
if (config.showDamage() && plugin.getTickShow() > 0)
{
final Actor opponent = plugin.getLastOpponent();
if (opponent != null)

View File

@@ -25,8 +25,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;
@@ -42,37 +40,21 @@ 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.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.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;
@PluginDescriptor(
name = "XP Drop",
@@ -82,7 +64,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 +73,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 +82,25 @@ 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 double hpExp = 0;
private boolean loginTick = false;
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;
@Provides
XpDropConfig provideConfig(ConfigManager configManager)
@@ -135,18 +111,25 @@ public class XpDropPlugin extends Plugin
@Override
protected void startUp() throws Exception
{
lastOpponent = null;
overlayManager.add(overlay);
if (client.getGameState() == GameState.LOGGED_IN)
}
@Override
protected void shutDown() throws Exception
{
overlayManager.remove(overlay);
}
@Subscribe
public void onGameStateChanged(GameStateChanged event)
{
if (event.getGameState() == GameState.LOGIN_SCREEN)
{
attackStyleVarbit = client.getVar(VarPlayer.ATTACK_STYLE);
equippedWeaponTypeVarbit = client.getVar(Varbits.EQUIPPED_WEAPON_TYPE);
castingModeVarbit = client.getVar(Varbits.DEFENSIVE_CASTING_MODE);
updateAttackStyle(
equippedWeaponTypeVarbit,
attackStyleVarbit,
castingModeVarbit);
loginTick = true;
}
damage = 0;
tickShow = 0;
}
@Subscribe
@@ -277,40 +260,16 @@ public class XpDropPlugin extends Plugin
@Subscribe
public void onGameTick(GameTick tick)
{
// Detect hitting a 0
if (lastOpponent != null)
loginTick = false;
lastOpponent = client.getLocalPlayer().getInteracting();
if (tickShow > 0)
{
int health = calculateHealth(lastOpponent);
if (health != -1 && opponentHealth != -1 && health == opponentHealth && hasHit)
{
damage = 0;
hasHit = false;
}
tickShow--;
}
// Handle getting XP gains
if (hasDropped)
else
{
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;
}
damage = 0;
}
currentTickPrayer = getActivePrayerType();
@@ -350,120 +309,67 @@ 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)
{
if (event.getSource() != client.getLocalPlayer())
if (loginTick)
{
return;
}
Actor opponent = event.getTarget();
if (opponent == null)
if (client.getGameState() != GameState.LOGGED_IN)
{
lastTime = Instant.now();
return;
}
else if (opponent.getName().equalsIgnoreCase("fishing spot"))
{
lastTime = Instant.now().minus(WAIT);
damage = 0;
tickShow = 0;
return;
}
damage = 0;
lastOpponent = opponent;
opponentHealth = calculateHealth(opponent);
}
private int calculateHealth(Actor target)
{
if (target == null || target.getName() == null)
if (event.getSkill().equals(Skill.HITPOINTS))
{
return -1;
}
final double oldExp = hpExp;
hpExp = client.getSkillExperience(Skill.HITPOINTS);
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)
final double diff = hpExp - oldExp;
if (diff < 1)
{
final int hp = hiscoreResult.getHitpoints().getLevel();
if (hp > 0)
{
maxHealth = hp;
}
return;
}
}
if (healthRatio < 0 || healthScale <= 0 || maxHealth == null)
{
return -1;
final double damageDealt = calculateDamageDealt(diff);
damage = (int) Math.rint(damageDealt);
tickShow = 3;
}
return (int) ((maxHealth * healthRatio / healthScale) + 0.5f);
}
@Subscribe
public void onVarbitChanged(VarbitChanged event)
private double calculateDamageDealt(double 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))
{
return damageDealt;
}
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);
return damageDealt;
}
NPC target = (NPC) a;
return damageDealt / NpcExpModifier.getByNpcId(target.getId());
}
}