From bed950ba5134905a9861b2ce2cba74f349508ed2 Mon Sep 17 00:00:00 2001
From: mathclassjr <153806286+mathclassjr@users.noreply.github.com>
Date: Wed, 6 Aug 2025 00:59:22 -0700
Subject: [PATCH 1/4] initial
---
.../PvpPerformanceTrackerPlugin.java | 209 +++++++++---------
.../controllers/FightPerformance.java | 78 ++++---
.../controllers/Fighter.java | 36 +--
.../controllers/PvpDamageCalc.java | 194 +++++++++-------
.../models/CombatLevels.java | 53 ++++-
.../models/FightLogEntry.java | 37 ++--
.../models/FightType.java | 10 +-
.../views/FightLogDetailFrame.java | 88 +++++---
8 files changed, 422 insertions(+), 283 deletions(-)
diff --git a/src/main/java/matsyir/pvpperformancetracker/PvpPerformanceTrackerPlugin.java b/src/main/java/matsyir/pvpperformancetracker/PvpPerformanceTrackerPlugin.java
index 4f1baed..73419e3 100644
--- a/src/main/java/matsyir/pvpperformancetracker/PvpPerformanceTrackerPlugin.java
+++ b/src/main/java/matsyir/pvpperformancetracker/PvpPerformanceTrackerPlugin.java
@@ -81,14 +81,8 @@
import net.runelite.api.Prayer;
import net.runelite.api.Skill;
import net.runelite.api.SpriteID;
-import net.runelite.api.events.AnimationChanged;
-import net.runelite.api.events.FakeXpDrop;
-import net.runelite.api.events.GameTick;
-import net.runelite.api.events.GameStateChanged;
-import net.runelite.api.events.HitsplatApplied;
-import net.runelite.api.events.InteractingChanged;
-
-import net.runelite.api.events.StatChanged;
+import net.runelite.api.events.*;
+
import net.runelite.client.RuneLite;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.chat.ChatMessageManager;
@@ -118,7 +112,7 @@
@Slf4j
@PluginDescriptor(
- name = "PvP Performance Tracker"
+ name = "PvP Performance Tracker"
)
public class PvpPerformanceTrackerPlugin extends Plugin
{
@@ -130,6 +124,7 @@ public class PvpPerformanceTrackerPlugin extends Plugin
// "pvp-performance-tracker2": From 1.5.9 update, until present
public static final String DATA_FOLDER = "pvp-performance-tracker2";
public static final String FIGHT_HISTORY_DATA_FNAME = "FightHistoryData.json";
+ private static final String FROZEN_MESSAGE = "
You have been frozen!";
public static final File FIGHT_HISTORY_DATA_DIR;
public static PvpPerformanceTrackerConfig CONFIG;
public static PvpPerformanceTrackerPlugin PLUGIN;
@@ -228,11 +223,11 @@ protected void startUp() throws Exception
fightHistory = new ArrayList<>();
GSON = injectedGson.newBuilder()
- .excludeFieldsWithoutExposeAnnotation()
- .registerTypeAdapter(Double.class, (JsonSerializer) (value, theType, context) ->
- value.isNaN() ? new JsonPrimitive(0) // Convert NaN to zero, otherwise, return as BigDecimal with scale of 3.
- : new JsonPrimitive(BigDecimal.valueOf(value).setScale(3, RoundingMode.HALF_UP))
- ).create();
+ .excludeFieldsWithoutExposeAnnotation()
+ .registerTypeAdapter(Double.class, (JsonSerializer) (value, theType, context) ->
+ value.isNaN() ? new JsonPrimitive(0) // Convert NaN to zero, otherwise, return as BigDecimal with scale of 3.
+ : new JsonPrimitive(BigDecimal.valueOf(value).setScale(3, RoundingMode.HALF_UP))
+ ).create();
if (!config.pluginVersion().equals(PLUGIN_VERSION))
{
@@ -243,17 +238,17 @@ protected void startUp() throws Exception
final BufferedImage icon = ImageUtil.getResourceStreamFromClass(getClass(), "/skull_red.png");
PLUGIN_ICON = new ImageIcon(icon).getImage();
navButton = NavigationButton.builder()
- .tooltip("PvP Fight History")
- .icon(icon)
- .priority(6)
- .panel(panel)
- .build();
+ .tooltip("PvP Fight History")
+ .icon(icon)
+ .priority(6)
+ .panel(panel)
+ .build();
importFightHistoryData();
// add the panel's nav button depending on config
if (config.showFightHistoryPanel() &&
- (!config.restrictToLms() || (client.getGameState() == GameState.LOGGED_IN && isAtLMS())))
+ (!config.restrictToLms() || (client.getGameState() == GameState.LOGGED_IN && isAtLMS())))
{
navButtonShown = true;
clientToolbar.addNavigation(navButton);
@@ -265,13 +260,13 @@ protected void startUp() throws Exception
// prepare default N/A or None symbol for eventual use.
clientThread.invokeLater(() -> DEFAULT_NONE_SYMBOL = itemManager.getImage(20594));
-
+
// Explicitly rebuild panel after all setup and import.
- SwingUtilities.invokeLater(() -> {
- if (panel != null) {
- panel.rebuild();
- }
- });
+ SwingUtilities.invokeLater(() -> {
+ if (panel != null) {
+ panel.rebuild();
+ }
+ });
}
@Override
@@ -295,7 +290,7 @@ public void onConfigChanged(ConfigChanged event)
case "restrictToLms":
boolean isAtLms = isAtLMS();
if (!navButtonShown && config.showFightHistoryPanel() &&
- (!config.restrictToLms() || isAtLms))
+ (!config.restrictToLms() || isAtLms))
{
SwingUtilities.invokeLater(() -> clientToolbar.addNavigation(navButton));
navButtonShown = true;
@@ -340,7 +335,7 @@ else if (navButtonShown && (!config.showFightHistoryPanel() || (config.restrictT
case "robeHitFilter":
recalculateAllRobeHits(true);
break;
- // potential future code for level presets/dynamic config if RL ever supports it.
+ // potential future code for level presets/dynamic config if RL ever supports it.
// case "attackLevel":
// case "strengthLevel":
// case "defenceLevel":
@@ -389,8 +384,8 @@ public void onInteractingChanged(InteractingChanged event)
// if the client player already has a valid opponent AND the fight has started,
// or the event source/target aren't players, skip any processing.
if ((hasOpponent() && currentFight.fightStarted())
- || !(event.getSource() instanceof Player)
- || !(event.getTarget() instanceof Player))
+ || !(event.getSource() instanceof Player)
+ || !(event.getTarget() instanceof Player))
{
return;
}
@@ -414,7 +409,7 @@ else if (event.getTarget().equals(client.getLocalPlayer()))
// start a new fight with the new found opponent, if a new one.
if (!hasOpponent() || !currentFight.getOpponent().getName().equals(opponent.getName()))
{
- currentFight = new FightPerformance(client.getLocalPlayer(), (Player)opponent);
+ currentFight = new FightPerformance(client.getLocalPlayer(), (Player)opponent, hiscoreManager);
overlay.setFight(currentFight);
hitsplatBuffer.clear();
incomingHitsplatsBuffer.clear();
@@ -491,13 +486,13 @@ public void onHitsplatApplied(HitsplatApplied event)
if (amount > 0)
{
if (!(hitType == HitsplatID.DAMAGE_ME
- || hitType == HitsplatID.DAMAGE_ME_ORANGE
- || hitType == HitsplatID.DAMAGE_OTHER_ORANGE
- || hitType == HitsplatID.DAMAGE_OTHER
- || hitType == HitsplatID.DAMAGE_MAX_ME
- || hitType == HitsplatID.DAMAGE_MAX_ME_ORANGE
- || hitType == HitsplatID.POISON
- || hitType == HitsplatID.VENOM))
+ || hitType == HitsplatID.DAMAGE_ME_ORANGE
+ || hitType == HitsplatID.DAMAGE_OTHER_ORANGE
+ || hitType == HitsplatID.DAMAGE_OTHER
+ || hitType == HitsplatID.DAMAGE_MAX_ME
+ || hitType == HitsplatID.DAMAGE_MAX_ME_ORANGE
+ || hitType == HitsplatID.POISON
+ || hitType == HitsplatID.VENOM))
{
return;
}
@@ -508,9 +503,9 @@ public void onHitsplatApplied(HitsplatApplied event)
// Exclude certain hitsplat types (like heal, poison, venom, disease)
// from the buffer used for HP-before-hit calculations.
boolean isExcludedType = hitType == HitsplatID.HEAL ||
- hitType == HitsplatID.POISON ||
- hitType == HitsplatID.VENOM ||
- hitType == HitsplatID.DISEASE;
+ hitType == HitsplatID.POISON ||
+ hitType == HitsplatID.VENOM ||
+ hitType == HitsplatID.DISEASE;
if (isExcludedType)
{
@@ -623,24 +618,24 @@ public void onGameTick(GameTick event)
if (currentFight.getOpponent() != null)
{
totalExpectedAttackHits += currentFight.getOpponent().getPendingAttacks().stream()
- .filter(e -> !e.isKoChanceCalculated() && e.isFullEntry() && !e.isSplash() && (tickToProcess - e.getTick() <= 5)) // Check if attack could land now
- .mapToInt(FightLogEntry::getExpectedHits)
- .sum();
+ .filter(e -> !e.isKoChanceCalculated() && e.isFullEntry() && !e.isSplash() && (tickToProcess - e.getTick() <= 5)) // Check if attack could land now
+ .mapToInt(FightLogEntry::getExpectedHits)
+ .sum();
}
// Sum expected hits from competitor's pending attacks targeting opponent
if (currentFight.getCompetitor() != null)
{
totalExpectedAttackHits += currentFight.getCompetitor().getPendingAttacks().stream()
- .filter(e -> !e.isKoChanceCalculated() && e.isFullEntry() && !e.isSplash() && (tickToProcess - e.getTick() <= 5)) // Check if attack could land now
- .mapToInt(FightLogEntry::getExpectedHits)
- .sum();
+ .filter(e -> !e.isKoChanceCalculated() && e.isFullEntry() && !e.isSplash() && (tickToProcess - e.getTick() <= 5)) // Check if attack could land now
+ .mapToInt(FightLogEntry::getExpectedHits)
+ .sum();
}
// 2. Compare observed vs expected
if (hitsplatsToProcess.size() > totalExpectedAttackHits)
{
log.debug("Tick {}: Observed hits ({}) > Expected attack hits ({}). Checking for special hits...",
- tickToProcess, hitsplatsToProcess.size(), totalExpectedAttackHits);
+ tickToProcess, hitsplatsToProcess.size(), totalExpectedAttackHits);
boolean removedHitInIteration;
int safetyBreakCounter = 0;
@@ -688,7 +683,7 @@ else if (target == opponentActor)
if (hitAmount == expectedVengeance)
{
log.debug("Tick {}: Found potential Vengeance hit ({} damage) on {} based on {} incoming damage on {}",
- tickToProcess, hitAmount, target.getName(), incomingDamage, otherPlayer.getName());
+ tickToProcess, hitAmount, target.getName(), incomingDamage, otherPlayer.getName());
isCandidate = true;
break; // Found a reason, no need to check other incoming hits for this potentialSpecialHit
}
@@ -698,7 +693,7 @@ else if (target == opponentActor)
if (hitAmount == expectedRecoil)
{
log.debug("Tick {}: Found potential Recoil hit ({} damage) on {} based on {} incoming damage on {}",
- tickToProcess, hitAmount, target.getName(), incomingDamage, otherPlayer.getName());
+ tickToProcess, hitAmount, target.getName(), incomingDamage, otherPlayer.getName());
isCandidate = true;
break;
}
@@ -750,7 +745,7 @@ else if (target == opponentActor)
// Group hitsplats by the actor receiving them (remaining hitsplats after special removal)
final List finalHitsplatsToProcess = hitsplatsToProcess; // Create effectively final list
Map> hitsByActor = finalHitsplatsToProcess.stream()
- .collect(Collectors.groupingBy((HitsplatInfo info) -> info.getEvent().getActor()));
+ .collect(Collectors.groupingBy((HitsplatInfo info) -> info.getEvent().getActor()));
List processedEntriesThisTick = new ArrayList<>();
@@ -768,19 +763,19 @@ else if (target == opponentActor)
maxHpToUse = CONFIG.opponentHitpointsLevel();
// Hiscores lookup should only happen if not in LMS
- if (opponent instanceof Player && opponent.getName() != null)
- {
- final HiscoreResult hiscoreResult = hiscoreManager.lookupAsync(opponent.getName(), hiscoreEndpoint);
- if (hiscoreResult != null)
+ if (opponent instanceof Player && opponent.getName() != null)
{
- final int hp = hiscoreResult.getSkill(HiscoreSkill.HITPOINTS).getLevel();
- if (hp > 0)
+ final HiscoreResult hiscoreResult = hiscoreManager.lookupAsync(opponent.getName(), hiscoreEndpoint);
+ if (hiscoreResult != null)
{
- maxHpToUse = hp; // Use Hiscores HP if available
+ final int hp = hiscoreResult.getSkill(HiscoreSkill.HITPOINTS).getLevel();
+ if (hp > 0)
+ {
+ maxHpToUse = hp; // Use Hiscores HP if available
+ }
}
}
}
- }
// Determine attacker
String actorName = ((Player) opponent).getName();
@@ -790,20 +785,20 @@ else if (target == opponentActor)
attacker = currentFight.getCompetitor();
}
else if (actorName.equals(currentFight.getCompetitor().getName()))
- {
+ {
attacker = currentFight.getOpponent();
- }
- else
- {
+ }
+ else
+ {
return;
}
// Get all potentially relevant, unprocessed entries sorted by animation tick
List candidateEntries = attacker.getPendingAttacks().stream()
- .filter(e -> !e.isKoChanceCalculated() && e.isFullEntry() && !e.isSplash())
- .filter(e -> (client.getTickCount() - e.getTick()) <= 5)
- .sorted(Comparator.comparingInt(FightLogEntry::getTick))
- .collect(Collectors.toList());
+ .filter(e -> !e.isKoChanceCalculated() && e.isFullEntry() && !e.isSplash())
+ .filter(e -> (client.getTickCount() - e.getTick()) <= 5)
+ .sorted(Comparator.comparingInt(FightLogEntry::getTick))
+ .collect(Collectors.toList());
List gmaulsMatchedThisTick = new ArrayList<>();
int totalGmaulHitsMatchedThisTick = 0;
@@ -927,14 +922,14 @@ else if (actorName.equals(currentFight.getCompetitor().getName()))
{
// Group processed entries by the tick they landed and the attacker
Map>> groupedByTickAndAttacker = processedEntriesThisTick.stream()
- .filter(e -> e.getHitsplatTick() >= 0)
- .collect(Collectors.groupingBy(
- FightLogEntry::getHitsplatTick,
- Collectors.groupingBy(
- FightLogEntry::getAttackerName,
- Collectors.toList()
- )
- ));
+ .filter(e -> e.getHitsplatTick() >= 0)
+ .collect(Collectors.groupingBy(
+ FightLogEntry::getHitsplatTick,
+ Collectors.groupingBy(
+ FightLogEntry::getAttackerName,
+ Collectors.toList()
+ )
+ ));
groupedByTickAndAttacker.forEach((tick, attackerMap) -> {
attackerMap.forEach((attackerName, entries) -> {
@@ -958,8 +953,8 @@ else if (actorName.equals(currentFight.getCompetitor().getName()))
// Calculate total damage for the sequence
int totalDamageInSequence = entries.stream()
- .mapToInt((FightLogEntry e) -> e.getActualDamageSum() != null ? e.getActualDamageSum() : 0)
- .sum();
+ .mapToInt((FightLogEntry e) -> e.getActualDamageSum() != null ? e.getActualDamageSum() : 0)
+ .sum();
// Calculate HP Before the entire sequence
hpBeforeSequence = hpAfterSequence + totalDamageInSequence;
@@ -983,8 +978,8 @@ else if (actorName.equals(currentFight.getCompetitor().getName()))
entry.setDisplayHpAfter(hpAfterCurrent);
Double koChanceCurrent = (hpBeforeCurrent != null)
- ? PvpPerformanceTrackerUtils.calculateKoChance(entry.getAccuracy(), entry.getMinHit(), entry.getMaxHit(), hpBeforeCurrent)
- : null;
+ ? PvpPerformanceTrackerUtils.calculateKoChance(entry.getAccuracy(), entry.getMinHit(), entry.getMaxHit(), hpBeforeCurrent)
+ : null;
entry.setDisplayKoChance(koChanceCurrent);
entry.setKoChance(koChanceCurrent);
@@ -994,7 +989,7 @@ else if (actorName.equals(currentFight.getCompetitor().getName()))
// Update HP for the next iteration
currentHp = hpAfterCurrent;
- }
+ }
});
});
}
@@ -1093,7 +1088,7 @@ private void updateFrom1_5_5to1_5_6()
// read the old saved fights from the file into an array, and add them as an updated
// fight to the fightHistory list.
Arrays.asList(GSON.fromJson(new FileReader(fightHistoryData), FightPerformance__1_5_5[].class))
- .forEach((oldFight) -> fightHistory.add(new FightPerformance(oldFight)));
+ .forEach((oldFight) -> fightHistory.add(new FightPerformance(oldFight)));
// now that the fights were deserialized and updated to the newest version, simply save them.
// afterwards, they will be re-loaded normally. Bit inefficient but not a big deal
@@ -1155,10 +1150,10 @@ void addToFightHistory(FightPerformance fight)
}
catch (Exception e)
{
- log.warn("Error calculating robe hits for new fight ({} vs {}): {}",
- fight.getCompetitor() != null ? fight.getCompetitor().getName() : "N/A",
- fight.getOpponent() != null ? fight.getOpponent().getName() : "N/A",
- e.getMessage());
+ log.warn("Error calculating robe hits for new fight ({} vs {}): {}",
+ fight.getCompetitor() != null ? fight.getCompetitor().getName() : "N/A",
+ fight.getOpponent() != null ? fight.getOpponent().getName() : "N/A",
+ e.getMessage());
}
// remove fights as necessary to respect the fightHistoryLimit.
@@ -1224,7 +1219,7 @@ void importFightHistoryData()
// read the saved fights from the file
List savedFights = Arrays.asList(
- GSON.fromJson(new FileReader(fightHistoryData), FightPerformance[].class));
+ GSON.fromJson(new FileReader(fightHistoryData), FightPerformance[].class));
fightHistory.clear();
importFights(savedFights);
@@ -1265,15 +1260,15 @@ public void initializeImportedFight(FightPerformance f)
{
// check for nulls in case the data was corrupted and entries are corrupted.
if (f.getCompetitor() == null || f.getOpponent() == null ||
- f.getCompetitor().getFightLogEntries() == null || f.getOpponent().getFightLogEntries() == null)
+ f.getCompetitor().getFightLogEntries() == null || f.getOpponent().getFightLogEntries() == null)
{
return;
}
f.getCompetitor().getFightLogEntries().forEach((FightLogEntry l) ->
- l.attackerName = f.getCompetitor().getName());
+ l.attackerName = f.getCompetitor().getName());
f.getOpponent().getFightLogEntries().forEach((FightLogEntry l) ->
- l.attackerName = f.getOpponent().getName());
+ l.attackerName = f.getOpponent().getName());
}
// process and add a list of deserialized json fights to the currently loaded fights
@@ -1332,15 +1327,29 @@ public boolean isAtLMS()
return false;
}
+ public boolean isAtArena()
+ {
+ boolean atArena = false;
+ int world = client.getWorld();
+ switch(world) {
+ case 570:
+ case 578:
+ case 558:
+ atArena = true;
+ break;
+ }
+ return atArena;
+ }
+
// Send a message to the chat. Send them messages to the trade chat since it is uncommonly
// used while fighting, but game, public, private, and clan chat all have their uses.
public void sendTradeChatMessage(String chatMessage)
{
chatMessageManager
- .queue(QueuedMessage.builder()
- .type(ChatMessageType.TRADE)
- .runeLiteFormattedMessage(chatMessage)
- .build());
+ .queue(QueuedMessage.builder()
+ .type(ChatMessageType.TRADE)
+ .runeLiteFormattedMessage(chatMessage)
+ .build());
}
// create a simple confirmation modal, using a custom dialog so it can be always
@@ -1382,11 +1391,11 @@ public void exportFight(FightPerformance fight)
boolean success = false;
String confirmMessage;
if (fight.getCompetitor() != null && fight.getCompetitor().getName() != null &&
- fight.getOpponent() != null && fight.getOpponent().getName() != null)
+ fight.getOpponent() != null && fight.getOpponent().getName() != null)
{
success = true;
confirmMessage = "Fight data of " + fight.getCompetitor().getName() + " vs " +
- fight.getOpponent().getName() + " was copied to the clipboard.";
+ fight.getOpponent().getName() + " was copied to the clipboard.";
}
else
{
@@ -1401,11 +1410,11 @@ public int currentlyUsedOffensivePray()
{
return client.isPrayerActive(Prayer.PIETY) ? SpriteID.PRAYER_PIETY :
client.isPrayerActive(Prayer.ULTIMATE_STRENGTH) ? SpriteID.PRAYER_ULTIMATE_STRENGTH :
- client.isPrayerActive(Prayer.RIGOUR) ? SpriteID.PRAYER_RIGOUR :
- client.isPrayerActive(Prayer.EAGLE_EYE) ? SpriteID.PRAYER_EAGLE_EYE :
- client.isPrayerActive(Prayer.AUGURY) ? SpriteID.PRAYER_AUGURY :
- client.isPrayerActive(Prayer.MYSTIC_MIGHT) ? SpriteID.PRAYER_MYSTIC_MIGHT :
- 0;
+ client.isPrayerActive(Prayer.RIGOUR) ? SpriteID.PRAYER_RIGOUR :
+ client.isPrayerActive(Prayer.EAGLE_EYE) ? SpriteID.PRAYER_EAGLE_EYE :
+ client.isPrayerActive(Prayer.AUGURY) ? SpriteID.PRAYER_AUGURY :
+ client.isPrayerActive(Prayer.MYSTIC_MIGHT) ? SpriteID.PRAYER_MYSTIC_MIGHT :
+ 0;
}
public void addSpriteToLabelIfValid(JLabel label, int spriteId, Runnable swingCallback)
diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/FightPerformance.java b/src/main/java/matsyir/pvpperformancetracker/controllers/FightPerformance.java
index 8b9f1a9..c89cb3e 100644
--- a/src/main/java/matsyir/pvpperformancetracker/controllers/FightPerformance.java
+++ b/src/main/java/matsyir/pvpperformancetracker/controllers/FightPerformance.java
@@ -34,6 +34,7 @@
import java.util.Arrays;
import java.util.Objects;
import lombok.Getter;
+import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import static matsyir.pvpperformancetracker.PvpPerformanceTrackerPlugin.CONFIG;
@@ -45,10 +46,13 @@
import matsyir.pvpperformancetracker.models.FightType;
import matsyir.pvpperformancetracker.models.oldVersions.FightPerformance__1_5_5;
import net.runelite.api.AnimationID;
+import net.runelite.api.Client;
import net.runelite.api.Player;
import net.runelite.api.Skill;
import matsyir.pvpperformancetracker.PvpPerformanceTrackerConfig;
import net.runelite.api.kit.KitType;
+import net.runelite.client.hiscore.HiscoreManager;
+
import static matsyir.pvpperformancetracker.utils.PvpPerformanceTrackerUtils.fixItemId;
import static matsyir.pvpperformancetracker.controllers.PvpDamageCalc.RANGE_DEF;
@@ -59,9 +63,9 @@
public class FightPerformance implements Comparable
{
private static final int[] DEATH_ANIMATIONS = {
- AnimationID.DEATH, // Default
- 10629, // League IV
- 11902, // League V
+ AnimationID.DEATH, // Default
+ 10629, // League IV
+ 11902, // League V
};
// Delay to assume a fight is over. May seem long, but sometimes people barrage &
// stand under for a while to eat. Fights will automatically end when either competitor dies.
@@ -107,6 +111,13 @@ public class FightPerformance implements Comparable
private transient double competitorSurvivalProb = 1.0;
private transient double opponentSurvivalProb = 1.0;
+ @Getter
+ private CombatLevels playersStats = new CombatLevels(PLUGIN.getClient());
+
+ @Getter
+ @Setter
+ private CombatLevels opponentsStats = CombatLevels.getConfigLevels();
+
// shouldn't be used, just here so we can make a subclass, weird java thing
public FightPerformance()
{
@@ -114,15 +125,24 @@ public FightPerformance()
}
// constructor which initializes a fight from the 2 Players, starting stats at 0. Regular use constructor.
- public FightPerformance(Player competitor, Player opponent)
+ public FightPerformance(Player competitor, Player opponent, HiscoreManager hiscoreManager)
{
int defLvl = PLUGIN.getClient().getBoostedSkillLevel(Skill.DEFENCE);
// determine fight type based on being at LMS areas & use def level to check for LMS builds.
- this.fightType = !PLUGIN.isAtLMS() ? FightType.NORMAL :
- defLvl <= FightType.LMS_1DEF.getCombatLevelsForType().def ? FightType.LMS_1DEF :
- defLvl <= FightType.LMS_ZERK.getCombatLevelsForType().def ? FightType.LMS_ZERK :
- FightType.LMS_MAXMED;
+ boolean fightIsAtLMS = PLUGIN.isAtLMS();
+ boolean fightIsAtArena = PLUGIN.isAtArena();
+ this.fightType = FightType.NORMAL;
+ if(fightIsAtLMS) {
+ this.fightType = defLvl <= FightType.LMS_1DEF.getCombatLevelsForType().def ? FightType.LMS_1DEF :
+ defLvl <= FightType.LMS_ZERK.getCombatLevelsForType().def ? FightType.LMS_ZERK :
+ FightType.LMS_MAXMED;
+ }
+ if(fightIsAtArena) {
+ this.fightType = defLvl <= FightType.ARENA_1DEF.getCombatLevelsForType().def ? FightType.ARENA_1DEF :
+ defLvl <= FightType.ARENA_ZERK.getCombatLevelsForType().def ? FightType.ARENA_ZERK :
+ FightType.ARENA_MAXMED;
+ }
// initialize world
this.world = PLUGIN.getClient().getWorld();
@@ -134,6 +154,12 @@ public FightPerformance(Player competitor, Player opponent)
this.competitor = new Fighter(this, competitor);
this.opponent = new Fighter(this, opponent);
+ if(!fightIsAtLMS && !fightIsAtArena) {
+ setOpponentsStats(new CombatLevels(opponent.getName(), hiscoreManager));
+ } else {
+ setOpponentsStats(fightType.getCombatLevelsForType());
+ }
+
this.competitorPrevHp = PLUGIN.getClient().getBoostedSkillLevel(Skill.HITPOINTS);
this.competitor.setLastGhostBarrageCheckedMageXp(PLUGIN.getClient().getSkillExperience(Skill.MAGIC));
}
@@ -150,9 +176,9 @@ public FightPerformance(FightPerformance__1_5_5 old)
{
int defLvl = competitor.getFightLogEntries().get(0).getAttackerLevels().def;
this.fightType =
- defLvl <= FightType.LMS_1DEF.getCombatLevelsForType().def ? FightType.LMS_1DEF :
- defLvl <= FightType.LMS_ZERK.getCombatLevelsForType().def ? FightType.LMS_ZERK :
- FightType.LMS_MAXMED;
+ defLvl <= FightType.LMS_1DEF.getCombatLevelsForType().def ? FightType.LMS_1DEF :
+ defLvl <= FightType.LMS_ZERK.getCombatLevelsForType().def ? FightType.LMS_ZERK :
+ FightType.LMS_MAXMED;
}
else
{
@@ -239,10 +265,10 @@ public void checkForAttackAnimations(Player eventSource, CombatLevels competitor
{
int offensivePray = PLUGIN.currentlyUsedOffensivePray();
competitor.addAttack(
- opponent.getPlayer(),
- animationData,
- offensivePray,
- competitorLevels);
+ opponent.getPlayer(),
+ animationData,
+ offensivePray,
+ competitorLevels, opponentsStats);
lastFightTime = Instant.now().toEpochMilli();
addedAttack = true;
@@ -255,7 +281,7 @@ else if (eName.equals(opponent.getName()) && Objects.equals(interactingName, com
if (animationData != null)
{
// there is no offensive prayer data for the opponent so hardcode 0
- opponent.addAttack(competitor.getPlayer(), animationData, 0);
+ opponent.addAttack(competitor.getPlayer(), animationData, 0, competitorLevels, opponentsStats);
addedAttack = true;
// add a defensive log for the competitor while the opponent is attacking, to be used with the fight analysis/merge
competitor.addDefensiveLogs(competitorLevels, PLUGIN.currentlyUsedOffensivePray());
@@ -296,10 +322,10 @@ public void checkForLocalGhostBarrage(CombatLevels competitorLevels, Player loca
int offensivePray = PLUGIN.currentlyUsedOffensivePray();
competitor.addGhostBarrage(opponent.getPlayer().getOverheadIcon() != animationData.attackStyle.getProtection(),
- opponent.getPlayer(),
- AnimationData.MAGIC_ANCIENT_MULTI_TARGET,
- offensivePray,
- competitorLevels);
+ opponent.getPlayer(),
+ AnimationData.MAGIC_ANCIENT_MULTI_TARGET,
+ offensivePray,
+ competitorLevels);
}
}
@@ -419,9 +445,9 @@ public boolean opponentDmgDealtIsGreater()
public boolean competitorMagicHitsLuckier()
{
double competitorRate = (competitor.getMagicHitCountDeserved() == 0) ? 0 :
- (competitor.getMagicHitCount() / competitor.getMagicHitCountDeserved());
+ (competitor.getMagicHitCount() / competitor.getMagicHitCountDeserved());
double opponentRate = (opponent.getMagicHitCountDeserved() == 0) ? 0 :
- (opponent.getMagicHitCount() / opponent.getMagicHitCountDeserved());
+ (opponent.getMagicHitCount() / opponent.getMagicHitCountDeserved());
return competitorRate > opponentRate;
}
@@ -429,9 +455,9 @@ public boolean competitorMagicHitsLuckier()
public boolean opponentMagicHitsLuckier()
{
double competitorRate = (competitor.getMagicHitCountDeserved() == 0) ? 0 :
- (competitor.getMagicHitCount() / competitor.getMagicHitCountDeserved());
+ (competitor.getMagicHitCount() / competitor.getMagicHitCountDeserved());
double opponentRate = (opponent.getMagicHitCountDeserved() == 0) ? 0 :
- (opponent.getMagicHitCount() / opponent.getMagicHitCountDeserved());
+ (opponent.getMagicHitCount() / opponent.getMagicHitCountDeserved());
return opponentRate > competitorRate;
}
@@ -455,7 +481,7 @@ public int compareTo(FightPerformance o)
// if diff = 0, return 0. Otherwise, divide diff by its absolute value. This will result in
// -1 for negative numbers, and 1 for positive numbers, keeping the sign and a safely small int.
return diff == 0 ? 0 :
- (int)(diff / Math.abs(diff));
+ (int)(diff / Math.abs(diff));
}
/**
@@ -558,7 +584,7 @@ else if (defender == opponent)
}
}
- public void updateKoChanceStats(FightLogEntry entry)
+ public void updateKoChanceStats(FightLogEntry entry)
{
if (entry.getDisplayKoChance() == null) { return; }
diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java
index 7335420..58f1ec8 100644
--- a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java
+++ b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java
@@ -74,7 +74,7 @@ class Fighter
@Expose
@SerializedName("s")
private int offPraySuccessCount; // total number of successful off-pray attacks
- // (when you use a different combat style than your opponent's overhead)
+ // (when you use a different combat style than your opponent's overhead)
@Expose
@SerializedName("d")
private double deservedDamage; // total deserved damage based on gear & opponent's pray
@@ -195,11 +195,11 @@ public Fighter(FightPerformance fight, String name)
// Used for regular, ongoing fights
void addAttack(Player opponent, AnimationData animationData, int offensivePray)
{
- addAttack(opponent, animationData, offensivePray, null);
+ addAttack(opponent, animationData, offensivePray, null, null);
}
// Levels can be null
- void addAttack(Player opponent, AnimationData animationData, int offensivePray, CombatLevels levels)
+ void addAttack(Player opponent, AnimationData animationData, int offensivePray, CombatLevels levels, CombatLevels opponentLevels)
{
int[] attackerItems = player.getPlayerComposition().getEquipmentIds();
@@ -251,7 +251,7 @@ else if (weapon == EquipmentData.DRAGON_CROSSBOW &&
animationData = animationData.isSpecial ? AnimationData.MELEE_VLS_SPEC : AnimationData.MELEE_SCIM_SLASH;
}
- pvpDamageCalc.updateDamageStats(player, opponent, successful, animationData);
+ pvpDamageCalc.updateDamageStats(player, opponent, successful, animationData, levels, opponentLevels);
deservedDamage += pvpDamageCalc.getAverageHit();
if (animationData.attackStyle == AnimationData.AttackStyle.MAGIC)
@@ -265,7 +265,7 @@ else if (weapon == EquipmentData.DRAGON_CROSSBOW &&
}
}
- FightLogEntry fightLogEntry = new FightLogEntry(player, opponent, pvpDamageCalc, offensivePray, levels, animationData);
+ FightLogEntry fightLogEntry = new FightLogEntry(player, opponent, pvpDamageCalc, offensivePray, levels, opponentLevels, animationData);
fightLogEntry.setGmaulSpecial(isGmaulSpec);
if (PvpPerformanceTrackerPlugin.CONFIG.fightLogInChat())
{
@@ -311,7 +311,7 @@ public void addGhostBarrage(boolean successful, Player opponent, AnimationData a
}
lastGhostBarrageCheckedTick = currentTick;
- pvpDamageCalc.updateDamageStats(player, opponent, successful, animationData);
+ pvpDamageCalc.updateDamageStats(player, opponent, successful, animationData, levels, levels);
ghostBarrageCount++;
ghostBarrageDeservedDamage += pvpDamageCalc.getAverageHit();
@@ -382,8 +382,8 @@ public String getOffPrayStats(boolean shortString)
{
nf.setMaximumFractionDigits(1);
return shortString ?
- offPraySuccessCount + "/" + attackCount :
- offPraySuccessCount + "/" + attackCount + " (" + nf.format(calculateOffPraySuccessPercentage()) + "%)";
+ offPraySuccessCount + "/" + attackCount :
+ offPraySuccessCount + "/" + attackCount + " (" + nf.format(calculateOffPraySuccessPercentage()) + "%)";
}
public String getOffPrayStats()
@@ -399,8 +399,8 @@ public String getMagicHitStats()
stats += "/" + nf.format(magicAttackCount);
nf.setMaximumFractionDigits(1);
String luckPercentage = magicHitCountDeserved != 0 ?
- nf.format(((double)magicHitCount / magicHitCountDeserved) * 100.0) :
- "0";
+ nf.format(((double)magicHitCount / magicHitCountDeserved) * 100.0) :
+ "0";
stats += " (" + luckPercentage + "%)";
return stats;
}
@@ -409,8 +409,8 @@ public String getShortMagicHitStats()
{
nf.setMaximumFractionDigits(1);
return magicHitCountDeserved != 0 ?
- nf.format(((double)magicHitCount / magicHitCountDeserved) * 100.0) + "%" :
- "0%";
+ nf.format(((double)magicHitCount / magicHitCountDeserved) * 100.0) + "%" :
+ "0%";
}
public String getDeservedDmgString(Fighter opponent, int precision, boolean onlyDiff)
@@ -418,7 +418,7 @@ public String getDeservedDmgString(Fighter opponent, int precision, boolean only
nf.setMaximumFractionDigits(precision);
double difference = deservedDamage - opponent.deservedDamage;
return onlyDiff ? (difference > 0 ? "+" : "") + nf.format(difference) :
- nf.format(deservedDamage) + " (" + (difference > 0 ? "+" : "") + nf.format(difference) + ")";
+ nf.format(deservedDamage) + " (" + (difference > 0 ? "+" : "") + nf.format(difference) + ")";
}
public String getDeservedDmgString(Fighter opponent)
{
@@ -430,7 +430,7 @@ public String getDmgDealtString(Fighter opponent, boolean onlyDiff)
{
int difference = damageDealt - opponent.damageDealt;
return onlyDiff ? (difference > 0 ? "+" : "") + difference:
- damageDealt + " (" + (difference > 0 ? "+" : "") + difference + ")";
+ damageDealt + " (" + (difference > 0 ? "+" : "") + difference + ")";
}
public String getDmgDealtString(Fighter opponent)
{
@@ -440,13 +440,13 @@ public String getDmgDealtString(Fighter opponent)
public double calculateOffPraySuccessPercentage()
{
return attackCount == 0 ? 0 :
- (double) offPraySuccessCount / attackCount * 100.0;
+ (double) offPraySuccessCount / attackCount * 100.0;
}
public double calculateOffensivePraySuccessPercentage()
{
return attackCount == 0 ? 0 :
- (double) offensivePraySuccessCount / attackCount * 100.0;
+ (double) offensivePraySuccessCount / attackCount * 100.0;
}
public int getMagicAttackCount()
@@ -461,8 +461,8 @@ public String getOffensivePrayStats(boolean shortString)
{
nf.setMaximumFractionDigits(1);
return shortString ?
- offensivePraySuccessCount + "/" + attackCount :
- offensivePraySuccessCount + "/" + attackCount + " (" + nf.format(calculateOffensivePraySuccessPercentage()) + "%)";
+ offensivePraySuccessCount + "/" + attackCount :
+ offensivePraySuccessCount + "/" + attackCount + " (" + nf.format(calculateOffensivePraySuccessPercentage()) + "%)";
}
public String getOffensivePrayStats()
diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java b/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java
index f5a6171..56f9cfe 100644
--- a/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java
+++ b/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java
@@ -41,13 +41,11 @@
import matsyir.pvpperformancetracker.models.CombatLevels;
import matsyir.pvpperformancetracker.models.RangeAmmoData;
import matsyir.pvpperformancetracker.models.RingData;
-import net.runelite.api.PlayerComposition;
-import net.runelite.api.SpriteID;
+import net.runelite.api.*;
import net.runelite.api.kit.KitType;
import net.runelite.client.game.ItemEquipmentStats;
import net.runelite.client.game.ItemStats;
import org.apache.commons.lang3.ArrayUtils;
-import net.runelite.api.Player;
// Pvp damage calculations
// call updateDamageStats(...) with required parameters, and retrieve results by using the field getters
@@ -59,7 +57,7 @@
public class PvpDamageCalc
{
private static final int STAB_ATTACK = 0, SLASH_ATTACK = 1, CRUSH_ATTACK = 2, MAGIC_ATTACK = 3,
- RANGE_ATTACK = 4, STAB_DEF = 5, SLASH_DEF = 6, CRUSH_DEF = 7, MAGIC_DEF = 8;
+ RANGE_ATTACK = 4, STAB_DEF = 5, SLASH_DEF = 6, CRUSH_DEF = 7, MAGIC_DEF = 8;
public static final int RANGE_DEF = 9;
private static final int STRENGTH_BONUS = 10, RANGE_STRENGTH = 11, MAGIC_DAMAGE = 12;
@@ -136,22 +134,39 @@ public class PvpDamageCalc
private RingData ringUsed;
boolean isLmsFight;
+ boolean isArenaFight;
public PvpDamageCalc(FightPerformance relatedFight)
{
isLmsFight = relatedFight.fightType.isLmsFight();
- this.attackerLevels = relatedFight.fightType.getCombatLevelsForType();
- this.defenderLevels = relatedFight.fightType.getCombatLevelsForType();
+ isArenaFight = relatedFight.fightType.isArenaFight();
+ if(isLmsFight || isArenaFight) {
+ this.attackerLevels = relatedFight.fightType.getCombatLevelsForType();
+ this.defenderLevels = relatedFight.fightType.getCombatLevelsForType();
+ } else {
+ this.attackerLevels = relatedFight.getPlayersStats();
+ this.defenderLevels = relatedFight.getOpponentsStats();
+ }
this.ringUsed = isLmsFight ? RingData.BERSERKER_RING : CONFIG.ringChoice();
}
// main function used to update stats during an ongoing fight
- public void updateDamageStats(Player attacker, Player defender, boolean success, AnimationData animationData)
+ public void updateDamageStats(Player attacker, Player defender, boolean success, AnimationData animationData, CombatLevels competitorLevels, CombatLevels opponentLevels)
{
// shouldn't be possible, but just in case
if (attacker == null || defender == null) { return; }
+ //Use appropriate stats based on who is the attacker or defender
+ String competitor = PLUGIN.getClient().getLocalPlayer().getName();
+ if(attacker.getName().equals(competitor)) {
+ this.attackerLevels = competitorLevels;
+ this.defenderLevels = opponentLevels;
+ } else {
+ this.attackerLevels = opponentLevels;
+ this.defenderLevels = competitorLevels;
+ }
+
averageHit = 0;
accuracy = 0;
minHit = 0;
@@ -197,15 +212,16 @@ else if (attackStyle == AttackStyle.MAGIC)
minHit = (int)(minHit * (success ? 1 : UNSUCCESSFUL_PRAY_DMG_MODIFIER));
log.debug("attackStyle: " + attackStyle.toString() + ", avgHit: " + nf.format(averageHit) + ", acc: " + nf.format(accuracy) +
- "\nattacker(" + attacker.getName() + ")stats: " + Arrays.toString(playerStats) +
- "\ndefender(" + defender.getName() + ")stats: " + Arrays.toString(opponentStats));
+ "\nattacker(" + attacker.getName() + ")stats: " + Arrays.toString(playerStats) +
+ "\ndefender(" + defender.getName() + ")stats: " + Arrays.toString(opponentStats));
}
// secondary function used to analyze fights from the fight log (fight analysis/fight merge)
public void updateDamageStats(FightLogEntry atkLog, FightLogEntry defenderLog)
{
this.attackerLevels = atkLog.getAttackerLevels();
- this.defenderLevels = defenderLog.getAttackerLevels();
+ this.defenderLevels = defenderLog.getDefenderLevels();
+
int[] attackerItems = atkLog.getAttackerGear();
int[] defenderItems = atkLog.getDefenderGear();
boolean success = atkLog.success();
@@ -219,8 +235,8 @@ public void updateDamageStats(FightLogEntry atkLog, FightLogEntry defenderLog)
EquipmentData weapon = EquipmentData.fromId(fixItemId(attackerItems[KitType.WEAPON.getIndex()]));
- int[] playerStats = this.calculateBonuses(attackerItems);
- int[] opponentStats = this.calculateBonuses(defenderItems);
+ int[] playerEquipStats = this.calculateBonuses(attackerItems);
+ int[] opponentEquipStats = this.calculateBonuses(defenderItems);
AnimationData.AttackStyle attackStyle = animationData.attackStyle; // basic style: stab/slash/crush/ranged/magic
// Special attack used will be determined based on the currently used weapon, if its special attack has been implemented.
@@ -231,21 +247,21 @@ public void updateDamageStats(FightLogEntry atkLog, FightLogEntry defenderLog)
if (attackStyle.isMelee())
{
- getMeleeMaxHit(playerStats[STRENGTH_BONUS], isSpecial, weapon, voidStyle, successfulOffensive);
- getMeleeAccuracy(playerStats, opponentStats, attackStyle, isSpecial, weapon, voidStyle, successfulOffensive);
+ getMeleeMaxHit(playerEquipStats[STRENGTH_BONUS], isSpecial, weapon, voidStyle, successfulOffensive);
+ getMeleeAccuracy(playerEquipStats, opponentEquipStats, attackStyle, isSpecial, weapon, voidStyle, successfulOffensive);
}
else if (attackStyle == AttackStyle.RANGED)
{
- getRangedMaxHit(playerStats[RANGE_STRENGTH], isSpecial, weapon, voidStyle, successfulOffensive, attackerItems);
- getRangeAccuracy(playerStats[RANGE_ATTACK], opponentStats[RANGE_DEF], isSpecial, weapon, voidStyle, successfulOffensive, attackerItems);
+ getRangedMaxHit(playerEquipStats[RANGE_STRENGTH], isSpecial, weapon, voidStyle, successfulOffensive, attackerItems);
+ getRangeAccuracy(playerEquipStats[RANGE_ATTACK], opponentEquipStats[RANGE_DEF], isSpecial, weapon, voidStyle, successfulOffensive, attackerItems);
}
// this should always be true at this point, but just in case. unknown animation styles won't
// make it here, they should be stopped in FightPerformance::checkForAttackAnimations
else if (attackStyle == AttackStyle.MAGIC)
{
EquipmentData shield = EquipmentData.fromId(fixItemId(attackerItems[KitType.SHIELD.getIndex()]));
- getMagicMaxHit(shield, playerStats[MAGIC_DAMAGE], animationData, weapon, voidStyle, successfulOffensive);
- getMagicAccuracy(playerStats[MAGIC_ATTACK], opponentStats[MAGIC_DEF], weapon, animationData, voidStyle, successfulOffensive, defenderLog.getAttackerOffensivePray() == SpriteID.PRAYER_AUGURY);
+ getMagicMaxHit(shield, playerEquipStats[MAGIC_DAMAGE], animationData, weapon, voidStyle, successfulOffensive);
+ getMagicAccuracy(playerEquipStats[MAGIC_ATTACK], opponentEquipStats[MAGIC_DEF], weapon, animationData, voidStyle, successfulOffensive, defenderLog.getAttackerOffensivePray() == SpriteID.PRAYER_AUGURY);
}
getAverageHit(success, weapon, isSpecial);
@@ -335,10 +351,10 @@ else if (burningClaws && usingSpec)
double avgTotalDmg4 = getAverageBurningClawDamage(minD4, maxD4);
double expectedDamage =
- (acc) * avgTotalDmg1 +
- (miss * acc) * avgTotalDmg2 +
- (miss * miss * acc) * avgTotalDmg3 +
- (miss * miss * miss) * avgTotalDmg4;
+ (acc) * avgTotalDmg1 +
+ (miss * acc) * avgTotalDmg2 +
+ (miss * miss * acc) * avgTotalDmg3 +
+ (miss * miss * miss) * avgTotalDmg4;
this.averageHit = expectedDamage * prayerModifier;
@@ -388,7 +404,7 @@ else if (usingSpec && voidwaker)
if (minHit > 0)
{
log.info("PvpDamageCalc:getAverageHit: Fell into default avg hit calculation with a minHit > 0 (" +
- minHit + "). Shouldn't happen. Weapon: " + weapon.toString());
+ minHit + "). Shouldn't happen. Weapon: " + weapon.toString());
}
}
@@ -439,14 +455,14 @@ private void getMeleeMaxHit(int meleeStrength, boolean usingSpec, EquipmentData
int baseDamage = (int) Math.floor(0.5 + effectiveLevel * (meleeStrength + 64) / 640.0);
double damageModifier = (ags && usingSpec) ? ARMA_GS_SPEC_DMG_MODIFIER :
- (ancientGs && usingSpec) ? ANCIENT_GS_SPEC_DMG_MODIFIER :
- (swh && usingSpec) ? SWH_SPEC_DMG_MODIFIER :
- (dds && usingSpec) ? DDS_SPEC_DMG_MODIFIER :
- (vls && usingSpec) ? VLS_SPEC_DMG_MODIFIER :
- (dwh && usingSpec) ? DWH_SPEC_DMG_MODIFIER :
- (voidwaker && usingSpec) ? VOIDWAKER_SPEC_DMG_MODIFIER :
- (abyssalDagger && usingSpec) ? ABYSSAL_DAGGER_SPEC_DMG_MODIFIER :
- 1;
+ (ancientGs && usingSpec) ? ANCIENT_GS_SPEC_DMG_MODIFIER :
+ (swh && usingSpec) ? SWH_SPEC_DMG_MODIFIER :
+ (dds && usingSpec) ? DDS_SPEC_DMG_MODIFIER :
+ (vls && usingSpec) ? VLS_SPEC_DMG_MODIFIER :
+ (dwh && usingSpec) ? DWH_SPEC_DMG_MODIFIER :
+ (voidwaker && usingSpec) ? VOIDWAKER_SPEC_DMG_MODIFIER :
+ (abyssalDagger && usingSpec) ? ABYSSAL_DAGGER_SPEC_DMG_MODIFIER :
+ 1;
maxHit = (int) (damageModifier * baseDamage);
}
@@ -461,7 +477,7 @@ private void getRangedMaxHit(int rangeStrength, boolean usingSpec, EquipmentData
if (this.isLmsFight)
{
weaponAmmo = weaponAmmo instanceof RangeAmmoData.StrongBoltAmmo ? RangeAmmoData.StrongBoltAmmo.OPAL_DRAGON_BOLTS_E :
- weaponAmmo instanceof RangeAmmoData.BoltAmmo ? RangeAmmoData.BoltAmmo.DIAMOND_BOLTS_E : weaponAmmo;
+ weaponAmmo instanceof RangeAmmoData.BoltAmmo ? RangeAmmoData.BoltAmmo.DIAMOND_BOLTS_E : weaponAmmo;
}
boolean ballista = weapon == EquipmentData.HEAVY_BALLISTA;
@@ -472,6 +488,8 @@ private void getRangedMaxHit(int rangeStrength, boolean usingSpec, EquipmentData
rangeStrength += ammoStrength;
+ System.out.println("getRangedMaxHit Range level: " + attackerLevels.range);
+
double effectiveLevel = Math.floor((attackerLevels.range * (successfulOffensive ? RIGOUR_OFFENSIVE_PRAYER_DMG_MODIFIER : 1)) + 8);
// apply void bonus if applicable
if (voidStyle == VoidStyle.VOID_ELITE_RANGE || voidStyle == VoidStyle.VOID_RANGE)
@@ -505,19 +523,19 @@ private void getRangedMaxHit(int rangeStrength, boolean usingSpec, EquipmentData
}
else // Standard Ranged Max Hit Calc
{
- maxHit = weaponAmmo == null ?
- (int) (modifier * baseDamage) :
- (int) ((modifier * baseDamage) + weaponAmmo.getBonusMaxHit(attackerLevels.range));
+ maxHit = weaponAmmo == null ?
+ (int) (modifier * baseDamage) :
+ (int) ((modifier * baseDamage) + weaponAmmo.getBonusMaxHit(attackerLevels.range));
}
// apply crystal armor bonus if using bow
if ((weapon == EquipmentData.BOW_OF_FAERDHINEN || weapon == EquipmentData.CRYSTAL_BOW || weapon == EquipmentData.CRYSTAL_BOW_I) &&
- (head == EquipmentData.CRYSTAL_HELM || body == EquipmentData.CRYSTAL_BODY || legs == EquipmentData.CRYSTAL_LEGS))
+ (head == EquipmentData.CRYSTAL_HELM || body == EquipmentData.CRYSTAL_BODY || legs == EquipmentData.CRYSTAL_LEGS))
{
double dmgModifier = 1 +
- (head == EquipmentData.CRYSTAL_HELM ? 0.025 : 0) +
- (body == EquipmentData.CRYSTAL_BODY ? 0.075 : 0) +
- (legs == EquipmentData.CRYSTAL_LEGS ? 0.05 : 0);
+ (head == EquipmentData.CRYSTAL_HELM ? 0.025 : 0) +
+ (body == EquipmentData.CRYSTAL_BODY ? 0.075 : 0) +
+ (legs == EquipmentData.CRYSTAL_LEGS ? 0.05 : 0);
maxHit *= dmgModifier;
}
@@ -581,14 +599,17 @@ private void getMeleeAccuracy(int[] playerStats, int[] opponentStats, AttackStyl
double defenderChance;
double accuracyModifier = dds ? DDS_SPEC_ACCURACY_MODIFIER :
- ags ? ARMA_GS_SPEC_ACCURACY_MODIFIER :
- ancientGs ? ANCIENT_GS_SPEC_ACCURACY_MODIFIER :
- fang ? FANG_SPEC_ACCURACY_MODIFIER :
- 1;
+ ags ? ARMA_GS_SPEC_ACCURACY_MODIFIER :
+ ancientGs ? ANCIENT_GS_SPEC_ACCURACY_MODIFIER :
+ fang ? FANG_SPEC_ACCURACY_MODIFIER :
+ 1;
/**
* Attacker Chance
*/
+
+ System.out.println("getMeleeAccuracy Attack Level: " + attackerLevels.atk);
+
effectiveLevelPlayer = Math.floor(((attackerLevels.atk * (successfulOffensive ? PIETY_ATK_PRAYER_MODIFIER : 1)) + STANCE_BONUS) + 8);
// apply void bonus if applicable
if (voidStyle == VoidStyle.VOID_ELITE_MELEE || voidStyle == VoidStyle.VOID_MELEE)
@@ -597,10 +618,10 @@ private void getMeleeAccuracy(int[] playerStats, int[] opponentStats, AttackStyl
}
final double attackBonus = attackStyle == AttackStyle.STAB ? stabBonusPlayer
- : attackStyle == AttackStyle.SLASH ? slashBonusPlayer : crushBonusPlayer;
+ : attackStyle == AttackStyle.SLASH ? slashBonusPlayer : crushBonusPlayer;
final double targetDefenceBonus = attackStyle == AttackStyle.STAB ? stabBonusTarget
- : attackStyle == AttackStyle.SLASH ? slashBonusTarget : crushBonusTarget;
+ : attackStyle == AttackStyle.SLASH ? slashBonusTarget : crushBonusTarget;
baseChance = Math.floor(effectiveLevelPlayer * (attackBonus + 64));
@@ -608,7 +629,7 @@ private void getMeleeAccuracy(int[] playerStats, int[] opponentStats, AttackStyl
{
// Don't apply the generic modifier if it's the Abyssal Dagger (handled separately below)
if (weapon != EquipmentData.ABYSSAL_DAGGER) {
- baseChance = baseChance * accuracyModifier;
+ baseChance = baseChance * accuracyModifier;
}
}
@@ -622,6 +643,8 @@ private void getMeleeAccuracy(int[] playerStats, int[] opponentStats, AttackStyl
/**
* Defender Chance
*/
+
+ System.out.println("getMeleeAccuracy Defender defense Level: " + defenderLevels.def);
effectiveLevelTarget = Math.floor(((defenderLevels.def * PIETY_DEF_PRAYER_MODIFIER) + STANCE_BONUS) + 8);
if (vls && usingSpec)
@@ -651,7 +674,7 @@ private void getRangeAccuracy(int playerRangeAtt, int opponentRangeDef, boolean
RangeAmmoData weaponAmmo = EquipmentData.getWeaponAmmo(weapon);
// if it's an LMS fight and bolts are used, don't use config bolt, just use diamond bolts(e)
if (this.isLmsFight && (weaponAmmo instanceof RangeAmmoData.BoltAmmo ||
- weaponAmmo instanceof RangeAmmoData.StrongBoltAmmo))
+ weaponAmmo instanceof RangeAmmoData.StrongBoltAmmo))
{
weaponAmmo = RangeAmmoData.BoltAmmo.DIAMOND_BOLTS_E;
}
@@ -667,6 +690,7 @@ private void getRangeAccuracy(int playerRangeAtt, int opponentRangeDef, boolean
/**
* Attacker Chance
*/
+ System.out.println("getRangeAccuracy range Level: " + attackerLevels.range);
effectiveLevelPlayer = Math.floor(((attackerLevels.range * (successfulOffensive ? RIGOUR_OFFENSIVE_PRAYER_ATTACK_MODIFIER : 1)) + STANCE_BONUS) + 8);
// apply void bonus if applicable
if (voidStyle == VoidStyle.VOID_ELITE_RANGE || voidStyle == VoidStyle.VOID_RANGE)
@@ -680,12 +704,12 @@ private void getRangeAccuracy(int playerRangeAtt, int opponentRangeDef, boolean
EquipmentData legs = EquipmentData.fromId(fixItemId(attackerComposition[KitType.LEGS.getIndex()]));
if ((weapon == EquipmentData.BOW_OF_FAERDHINEN || weapon == EquipmentData.CRYSTAL_BOW || weapon == EquipmentData.CRYSTAL_BOW_I) &&
- (head == EquipmentData.CRYSTAL_HELM || body == EquipmentData.CRYSTAL_BODY || legs == EquipmentData.CRYSTAL_LEGS))
+ (head == EquipmentData.CRYSTAL_HELM || body == EquipmentData.CRYSTAL_BODY || legs == EquipmentData.CRYSTAL_LEGS))
{
double accuracyModifier = 1 +
- (head == EquipmentData.CRYSTAL_HELM ? 0.05 : 0) +
- (body == EquipmentData.CRYSTAL_BODY ? 0.15 : 0) +
- (legs == EquipmentData.CRYSTAL_LEGS ? 0.1 : 0);
+ (head == EquipmentData.CRYSTAL_HELM ? 0.05 : 0) +
+ (body == EquipmentData.CRYSTAL_BODY ? 0.15 : 0) +
+ (legs == EquipmentData.CRYSTAL_LEGS ? 0.1 : 0);
effectiveLevelPlayer *= accuracyModifier;
}
@@ -697,7 +721,7 @@ private void getRangeAccuracy(int playerRangeAtt, int opponentRangeDef, boolean
boolean ballista = weapon == EquipmentData.HEAVY_BALLISTA;
double specAccuracyModifier = acb ? ACB_SPEC_ACCURACY_MODIFIER :
- ballista ? BALLISTA_SPEC_ACCURACY_MODIFIER : 1;
+ ballista ? BALLISTA_SPEC_ACCURACY_MODIFIER : 1;
attackerChance = Math.floor(rangeModifier * specAccuracyModifier);
}
@@ -709,6 +733,7 @@ private void getRangeAccuracy(int playerRangeAtt, int opponentRangeDef, boolean
/**
* Defender Chance
*/
+ System.out.println("getRangeAccuracy defender Level: " + defenderLevels.def);
effectiveLevelTarget = Math.floor(((defenderLevels.def * RIGOUR_DEF_PRAYER_MODIFIER) + STANCE_BONUS) + 8);
defenderChance = Math.floor(effectiveLevelTarget * ((double) opponentRangeDef + 64));
@@ -752,6 +777,7 @@ private void getMagicAccuracy(int playerMageAtt, int opponentMageDef, EquipmentD
/**
* Attacker Chance
*/
+ System.out.println("getMagicAccuracy magic Level: " + attackerLevels.mage);
effectiveLevelPlayer = Math.floor(((attackerLevels.mage * (successfulOffensive ? AUGURY_OFFENSIVE_PRAYER_MODIFIER : 1))) + 8);
// apply void bonus if applicable
if (voidStyle == VoidStyle.VOID_ELITE_MAGE || voidStyle == VoidStyle.VOID_MAGE)
@@ -765,6 +791,8 @@ private void getMagicAccuracy(int playerMageAtt, int opponentMageDef, EquipmentD
/**
* Defender Chance
*/
+ System.out.println("getMagicAccuracy defender defense Level: " + defenderLevels.def);
+ System.out.println("getMagicAccuracy defender magic Level: " + defenderLevels.mage);
effectiveLevelTarget = Math.floor(((defenderLevels.def * AUGURY_DEF_PRAYER_MODIFIER) + STANCE_BONUS) + 8);
effectiveMagicLevelTarget = Math.floor((defenderLevels.mage * (defensiveAugurySuccess ? AUGURY_MAGEDEF_PRAYER_MODIFIER : 1)) * 0.70);
reducedDefenceLevelTarget = Math.floor(effectiveLevelTarget * 0.30);
@@ -772,8 +800,8 @@ private void getMagicAccuracy(int playerMageAtt, int opponentMageDef, EquipmentD
// 0.975x is a simplified brimstone accuracy formula, where x = mage def
defenderChance = ringUsed == RingData.BRIMSTONE_RING ?
- Math.floor(effectiveMagicDefenceTarget * ((BRIMSTONE_RING_OPPONENT_DEF_MODIFIER * opponentMageDef) + 64)) :
- Math.floor(effectiveMagicDefenceTarget * ((double) opponentMageDef + 64));
+ Math.floor(effectiveMagicDefenceTarget * ((BRIMSTONE_RING_OPPONENT_DEF_MODIFIER * opponentMageDef) + 64)) :
+ Math.floor(effectiveMagicDefenceTarget * ((double) opponentMageDef + 64));
/**
* Calculate Accuracy
@@ -827,19 +855,19 @@ public static int[] getItemStats(int itemId)
return null;
}
return new int[] {
- equipmentStats.getAstab(), // 0
- equipmentStats.getAslash(), // 1
- equipmentStats.getAcrush(), // 2
- equipmentStats.getAmagic(), // 3
- equipmentStats.getArange(), // 4
- equipmentStats.getDstab(), // 5
- equipmentStats.getDslash(), // 6
- equipmentStats.getDcrush(), // 7
- equipmentStats.getDmagic(), // 8
- equipmentStats.getDrange(), // 9
- equipmentStats.getStr(), // 10
- equipmentStats.getRstr(), // 11
- (int)equipmentStats.getMdmg(), // 12
+ equipmentStats.getAstab(), // 0
+ equipmentStats.getAslash(), // 1
+ equipmentStats.getAcrush(), // 2
+ equipmentStats.getAmagic(), // 3
+ equipmentStats.getArange(), // 4
+ equipmentStats.getDstab(), // 5
+ equipmentStats.getDslash(), // 6
+ equipmentStats.getDcrush(), // 7
+ equipmentStats.getDmagic(), // 8
+ equipmentStats.getDrange(), // 9
+ equipmentStats.getStr(), // 10
+ equipmentStats.getRstr(), // 11
+ (int)equipmentStats.getMdmg(), // 12
};
}
@@ -858,8 +886,8 @@ public static int[] calculateBonuses(int[] itemIds)
public static int[] calculateBonuses(int[] itemIds, RingData ringUsed)
{
int[] equipmentBonuses = ringUsed == null || ringUsed == RingData.NONE ?
- new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } :
- getItemStats(ringUsed.getItemId());
+ new int[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } :
+ getItemStats(ringUsed.getItemId());
if (equipmentBonuses == null) // shouldn't happen, but as a failsafe if the ring lookup fails
{
@@ -891,19 +919,19 @@ public static ItemEquipmentStats calculateBonusesToStats(int[] itemIds)
{
int[] bonuses = calculateBonuses(itemIds);
return ItemEquipmentStats.builder()
- .astab(bonuses[STAB_ATTACK]) // 0
- .aslash(bonuses[SLASH_ATTACK]) // 1
- .acrush(bonuses[CRUSH_ATTACK]) // 2
- .amagic(bonuses[MAGIC_ATTACK]) // 3
- .arange(bonuses[RANGE_ATTACK]) // 4
- .dstab(bonuses[STAB_DEF]) // 5
- .dslash(bonuses[SLASH_DEF]) // 6
- .dcrush(bonuses[CRUSH_DEF]) // 7
- .dmagic(bonuses[MAGIC_DEF]) // 8
- .drange(bonuses[RANGE_DEF]) // 9
- .str(bonuses[STRENGTH_BONUS]) // 10
- .rstr(bonuses[RANGE_STRENGTH]) // 11
- .mdmg(bonuses[MAGIC_DAMAGE]) // 12
- .build();
+ .astab(bonuses[STAB_ATTACK]) // 0
+ .aslash(bonuses[SLASH_ATTACK]) // 1
+ .acrush(bonuses[CRUSH_ATTACK]) // 2
+ .amagic(bonuses[MAGIC_ATTACK]) // 3
+ .arange(bonuses[RANGE_ATTACK]) // 4
+ .dstab(bonuses[STAB_DEF]) // 5
+ .dslash(bonuses[SLASH_DEF]) // 6
+ .dcrush(bonuses[CRUSH_DEF]) // 7
+ .dmagic(bonuses[MAGIC_DEF]) // 8
+ .drange(bonuses[RANGE_DEF]) // 9
+ .str(bonuses[STRENGTH_BONUS]) // 10
+ .rstr(bonuses[RANGE_STRENGTH]) // 11
+ .mdmg(bonuses[MAGIC_DAMAGE]) // 12
+ .build();
}
}
diff --git a/src/main/java/matsyir/pvpperformancetracker/models/CombatLevels.java b/src/main/java/matsyir/pvpperformancetracker/models/CombatLevels.java
index 071cd16..c577bd3 100644
--- a/src/main/java/matsyir/pvpperformancetracker/models/CombatLevels.java
+++ b/src/main/java/matsyir/pvpperformancetracker/models/CombatLevels.java
@@ -28,8 +28,13 @@
import com.google.gson.annotations.SerializedName;
import lombok.Getter;
import static matsyir.pvpperformancetracker.PvpPerformanceTrackerPlugin.CONFIG;
+
import net.runelite.api.Client;
import net.runelite.api.Skill;
+import net.runelite.client.hiscore.HiscoreResult;
+import net.runelite.client.hiscore.HiscoreSkill;
+import net.runelite.client.hiscore.HiscoreEndpoint;
+import net.runelite.client.hiscore.HiscoreManager;
// Basic class that will be used to save current combat levels (including boosts/drains)
@Getter
@@ -38,11 +43,11 @@ public class CombatLevels
public static CombatLevels getConfigLevels()
{
return new CombatLevels(CONFIG.attackLevel(),
- CONFIG.strengthLevel(),
- CONFIG.defenceLevel(),
- CONFIG.rangedLevel(),
- CONFIG.magicLevel(),
- 99);
+ CONFIG.strengthLevel(),
+ CONFIG.defenceLevel(),
+ CONFIG.rangedLevel(),
+ CONFIG.magicLevel(),
+ 99);
}
@Expose
@@ -84,6 +89,44 @@ public CombatLevels(Client client)
this.hp = client.getBoostedSkillLevel(Skill.HITPOINTS);
}
+ public CombatLevels(String username, HiscoreManager hiscoreManager) {
+ try {
+ HiscoreResult lookupResults = hiscoreManager.lookupAsync(username.toLowerCase(), HiscoreEndpoint.NORMAL);
+ if(lookupResults == null) {
+ //if highscores fails just pull from config
+ this.atk = CONFIG.attackLevel();
+ this.str = CONFIG.strengthLevel();
+ this.def = CONFIG.defenceLevel();
+ this.range = CONFIG.rangedLevel();
+ this.mage = CONFIG.magicLevel();
+ this.hp = 99;
+ } else {
+ int usernamesAttack = lookupResults.getSkill(HiscoreSkill.ATTACK).getLevel();
+ int usernamesStrength = lookupResults.getSkill(HiscoreSkill.STRENGTH).getLevel();
+ int usernamesDefence = lookupResults.getSkill(HiscoreSkill.DEFENCE).getLevel();
+ int usernamesRange = lookupResults.getSkill(HiscoreSkill.RANGED).getLevel();
+ int usernamesMagic = lookupResults.getSkill(HiscoreSkill.MAGIC).getLevel();
+ int usernamesHitpoints = lookupResults.getSkill(HiscoreSkill.HITPOINTS).getLevel();
+
+ //assume boosted
+ this.atk = (int) Math.floor((usernamesAttack + 5) * 1.15);
+ this.str = (int) Math.floor((usernamesStrength + 5) * 1.15);
+ this.def = (int) Math.floor((usernamesDefence + 2) * 1.20);
+ this.range = (int) Math.floor((usernamesRange + 4) * 1.10);
+ this.mage = usernamesMagic; //nobody uses boosted magic for extended periods of time
+ this.hp = usernamesHitpoints;
+ }
+ } catch (Exception exception) {
+ //if highscores fails just pull from config
+ this.atk = CONFIG.attackLevel();
+ this.str = CONFIG.strengthLevel();
+ this.def = CONFIG.defenceLevel();
+ this.range = CONFIG.rangedLevel();
+ this.mage = CONFIG.magicLevel();
+ this.hp = 99;
+ }
+ }
+
public int getSkill(Skill skill)
{
switch(skill)
diff --git a/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java b/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java
index e1c9acc..6e79511 100644
--- a/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java
+++ b/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java
@@ -112,6 +112,10 @@ public class FightLogEntry implements Comparable
@SerializedName("C")
private CombatLevels attackerLevels; // CAN BE NULL
+ @Expose
+ @SerializedName("D")
+ private CombatLevels defenderLevels; // CAN BE NULL
+
@Getter
@Setter
@Expose
@@ -192,7 +196,7 @@ public class FightLogEntry implements Comparable
@Getter @Setter
private boolean isPartOfTickGroup = false;
- public FightLogEntry(Player attacker, Player defender, PvpDamageCalc pvpDamageCalc, int attackerOffensivePray, CombatLevels levels, AnimationData animationData)
+ public FightLogEntry(Player attacker, Player defender, PvpDamageCalc pvpDamageCalc, int attackerOffensivePray, CombatLevels levels, CombatLevels defLevels, AnimationData animationData)
{
this.isFullEntry = true;
@@ -221,6 +225,7 @@ public FightLogEntry(Player attacker, Player defender, PvpDamageCalc pvpDamageCa
this.expectedHits = PvpPerformanceTrackerUtils.getExpectedHits(animationData);
this.matchedHitsCount = 0;
this.actualDamageSum = 0;
+ this.defenderLevels = defLevels;
}
// create incomplete entry to save competitor's defensive stats which are only client side
@@ -296,20 +301,20 @@ public String toChatMessage()
{
Color darkRed = new Color(127, 0, 0); // same color as default clan chat color
return new ChatMessageBuilder()
- .append(darkRed, attackerName + ": ")
- .append(Color.BLACK, "Style: ")
- .append(darkRed, WordUtils.capitalizeFully(animationData.attackStyle.toString()))
- .append(Color.BLACK, " Hit: ")
- .append(darkRed, getHitRange())
- .append(Color.BLACK, " Acc: ")
- .append(darkRed, nf.format(accuracy))
- .append(Color.BLACK, " AvgHit: ")
- .append(darkRed, nf.format(deservedDamage))
- .append(Color.BLACK, " Spec?: ")
- .append(darkRed, animationData.isSpecial ? "Y" : "N")
- .append(Color.BLACK, " OffP?:")
- .append(darkRed, success() ? "Y" : "N")
- .build();
+ .append(darkRed, attackerName + ": ")
+ .append(Color.BLACK, "Style: ")
+ .append(darkRed, WordUtils.capitalizeFully(animationData.attackStyle.toString()))
+ .append(Color.BLACK, " Hit: ")
+ .append(darkRed, getHitRange())
+ .append(Color.BLACK, " Acc: ")
+ .append(darkRed, nf.format(accuracy))
+ .append(Color.BLACK, " AvgHit: ")
+ .append(darkRed, nf.format(deservedDamage))
+ .append(Color.BLACK, " Spec?: ")
+ .append(darkRed, animationData.isSpecial ? "Y" : "N")
+ .append(Color.BLACK, " OffP?:")
+ .append(darkRed, success() ? "Y" : "N")
+ .build();
}
public String getHitRange()
@@ -326,7 +331,7 @@ public int compareTo(FightLogEntry o)
// if diff = 0, return 0. Otherwise, divide diff by its absolute value. This will result in
// -1 for negative numbers, and 1 for positive numbers, keeping the sign and a safely small int.
return diff == 0 ? 0 :
- (int)(diff / Math.abs(diff));
+ (int)(diff / Math.abs(diff));
}
}
diff --git a/src/main/java/matsyir/pvpperformancetracker/models/FightType.java b/src/main/java/matsyir/pvpperformancetracker/models/FightType.java
index e5a3e1d..597680c 100644
--- a/src/main/java/matsyir/pvpperformancetracker/models/FightType.java
+++ b/src/main/java/matsyir/pvpperformancetracker/models/FightType.java
@@ -29,12 +29,16 @@
public enum FightType
{
+ ARENA_MAXMED(new CombatLevels(91, 118, 86, 112, 99, 99)),
+ ARENA_ZERK(new CombatLevels(74, 118, 56, 112, 99, 99)),
+ ARENA_1DEF(new CombatLevels(74, 118, 2, 112, 99, 99)),
LMS_MAXMED(new CombatLevels(118, 118, 75, 112, 99, 99)),
- LMS_ZERK(new CombatLevels(91, 118, 45, 112, 99, 99)),
+ LMS_ZERK(new CombatLevels(91, 118, 50, 112, 99, 99)),
LMS_1DEF(new CombatLevels(91, 118, 1, 112, 99, 99)),
NORMAL(CombatLevels.getConfigLevels());
private static FightType[] LMS_TYPES = { LMS_MAXMED, LMS_ZERK, LMS_1DEF };
+ private static FightType[] ARENA_TYPES = { ARENA_MAXMED, ARENA_ZERK, ARENA_1DEF };
private CombatLevels combatLevelsForType;
FightType(CombatLevels combatLevelsForType)
@@ -56,4 +60,8 @@ public boolean isLmsFight()
{
return ArrayUtils.contains(LMS_TYPES, this);
}
+ public boolean isArenaFight()
+ {
+ return ArrayUtils.contains(ARENA_TYPES, this);
+ }
}
diff --git a/src/main/java/matsyir/pvpperformancetracker/views/FightLogDetailFrame.java b/src/main/java/matsyir/pvpperformancetracker/views/FightLogDetailFrame.java
index 8b432fd..f5dc408 100644
--- a/src/main/java/matsyir/pvpperformancetracker/views/FightLogDetailFrame.java
+++ b/src/main/java/matsyir/pvpperformancetracker/views/FightLogDetailFrame.java
@@ -87,7 +87,7 @@ class FightLogDetailFrame extends JFrame
FightLogDetailFrame(FightPerformance fight, FightLogEntry log, int rowIdx, Point location)
{
super("Fight Log Details - " + fight.getCompetitor().getName() + " vs " + fight.getOpponent().getName()
- + " on world " + fight.getWorld());
+ + " on world " + fight.getWorld());
this.rowIdx = rowIdx;
@@ -184,32 +184,51 @@ class FightLogDetailFrame extends JFrame
praysUsedLine.add(defenderPrays, BorderLayout.EAST);
- CombatLevels levels = fight.fightType.getCombatLevelsForType();
+ //Pull levels from fight type or if they exist on attacker and defender
+ CombatLevels attackerLevels = fight.fightType.getCombatLevelsForType();
+ CombatLevels defenderLevels = fight.fightType.getCombatLevelsForType();
+ if(isCompetitorLog) {
+ if(log.getAttackerLevels() != null) {
+ attackerLevels = log.getAttackerLevels();
+ }
+ if(log.getDefenderLevels() != null) {
+ defenderLevels = log.getDefenderLevels();
+ }
+ }
+ if(!isCompetitorLog) {
+ if(log.getAttackerLevels() != null) {
+ attackerLevels = log.getDefenderLevels();
+ }
+ if(log.getDefenderLevels() != null) {
+ defenderLevels = fight.competitor.getFightLogEntries().get(rowIdx).getAttackerLevels();
+ }
+ }
+
combatLevelsLine = new JPanel(new BorderLayout());
JPanel attackerCombatLevels = new JPanel(new GridLayout(2, 3));
attackerAtkLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(attackerAtkLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.ATTACK));
- attackerAtkLvl.setText(String.valueOf(levels.atk));
+ attackerAtkLvl.setText(String.valueOf(attackerLevels.atk));
attackerAtkLvl.setToolTipText("Attack Level");
attackerStrLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(attackerStrLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.STRENGTH));
- attackerStrLvl.setText(String.valueOf(levels.str));
+ attackerStrLvl.setText(String.valueOf(attackerLevels.str));
attackerStrLvl.setToolTipText("Strength Level");
attackerDefLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(attackerDefLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.DEFENCE));
- attackerDefLvl.setText(String.valueOf(levels.def));
+ attackerDefLvl.setText(String.valueOf(attackerLevels.def));
attackerDefLvl.setToolTipText("Defence Level");
attackerRangeLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(attackerRangeLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.RANGED));
- attackerRangeLvl.setText(String.valueOf(levels.range));
+ attackerRangeLvl.setText(String.valueOf(attackerLevels.range));
attackerRangeLvl.setToolTipText("Ranged Level");
attackerMageLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(attackerMageLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.MAGIC));
- attackerMageLvl.setText(String.valueOf(levels.mage));
+ attackerMageLvl.setText(String.valueOf(attackerLevels.mage));
attackerMageLvl.setToolTipText("Magic Level");
attackerHpLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(attackerHpLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.HITPOINTS));
- attackerHpLvl.setText(String.valueOf(levels.hp));
+ attackerHpLvl.setText(String.valueOf(attackerLevels.hp));
attackerHpLvl.setToolTipText("Hitpoints Level");
attackerCombatLevels.add(attackerAtkLvl);
@@ -222,28 +241,29 @@ class FightLogDetailFrame extends JFrame
JPanel defenderCombatLevels = new JPanel(new GridLayout(2, 3));
defenderAtkLvl = new JLabel();
+
PLUGIN.addSpriteToLabelIfValid(defenderAtkLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.ATTACK));
- defenderAtkLvl.setText(String.valueOf(levels.atk));
+ defenderAtkLvl.setText(String.valueOf(defenderLevels.atk));
defenderAtkLvl.setToolTipText("Attack Level");
defenderStrLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(defenderStrLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.STRENGTH));
- defenderStrLvl.setText(String.valueOf(levels.str));
+ defenderStrLvl.setText(String.valueOf(defenderLevels.str));
defenderStrLvl.setToolTipText("Strength Level");
defenderDefLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(defenderDefLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.DEFENCE));
- defenderDefLvl.setText(String.valueOf(levels.def));
+ defenderDefLvl.setText(String.valueOf(defenderLevels.def));
defenderDefLvl.setToolTipText("Defence Level");
defenderRangeLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(defenderRangeLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.RANGED));
- defenderRangeLvl.setText(String.valueOf(levels.range));
+ defenderRangeLvl.setText(String.valueOf(defenderLevels.range));
defenderRangeLvl.setToolTipText("Ranged Level");
defenderMageLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(defenderMageLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.MAGIC));
- defenderMageLvl.setText(String.valueOf(levels.mage));
+ defenderMageLvl.setText(String.valueOf(defenderLevels.mage));
defenderMageLvl.setToolTipText("Magic Level");
defenderHpLvl = new JLabel();
PLUGIN.addSpriteToLabelIfValid(defenderHpLvl, PvpPerformanceTrackerUtils.getSpriteForSkill(Skill.HITPOINTS));
- defenderHpLvl.setText(String.valueOf(levels.hp));
+ defenderHpLvl.setText(String.valueOf(defenderLevels.hp));
defenderHpLvl.setToolTipText("Hitpoints Level");
defenderCombatLevels.add(defenderAtkLvl);
@@ -259,12 +279,12 @@ class FightLogDetailFrame extends JFrame
JPanel equipmentStatsLine = new JPanel(new BorderLayout());
JLabel attackerStatsLabel = new JLabel();
PLUGIN.getClientThread().invokeLater(() ->
- attackerStatsLabel.setText(getItemEquipmentStatsString(log.getAttackerGear())));
+ attackerStatsLabel.setText(getItemEquipmentStatsString(log.getAttackerGear())));
equipmentStatsLine.add(attackerStatsLabel, BorderLayout.WEST);
JLabel defenderStatsLabel = new JLabel();
PLUGIN.getClientThread().invokeLater(() ->
- defenderStatsLabel.setText(getItemEquipmentStatsString(log.getDefenderGear())));
+ defenderStatsLabel.setText(getItemEquipmentStatsString(log.getDefenderGear())));
equipmentStatsLine.add(defenderStatsLabel, BorderLayout.EAST);
JPanel equipmentRenderLine = new JPanel(new BorderLayout());
@@ -278,8 +298,8 @@ class FightLogDetailFrame extends JFrame
JLabel attackerAnimationDetected = new JLabel();
attackerAnimationDetected.setText("Animation Detected: " + log.getAnimationData().toString() + "");
attackerAnimationDetected.setToolTipText("Note that the animation can be misleading, as many animations are re-used, but this is normal.
" +
- "For example, Zammy Hasta and Staff of Fire use the same crush animation.
" +
- "These were not intended to ever be displayed, but why not include them here.");
+ "For example, Zammy Hasta and Staff of Fire use the same crush animation.
" +
+ "These were not intended to ever be displayed, but why not include them here.