From 10cd3aa6ed3448899d000e6ed7c5aeae58cbc92a Mon Sep 17 00:00:00 2001 From: Sacca Date: Sun, 4 Jan 2026 11:11:02 +0100 Subject: [PATCH 1/5] add spot animation scanning --- .../controllers/Fighter.java | 32 +++++++++++++++++-- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java index 08ffc0b..89a5b51 100644 --- a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java +++ b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java @@ -42,10 +42,12 @@ import matsyir.pvpperformancetracker.models.CombatLevels; import matsyir.pvpperformancetracker.models.EquipmentData; import matsyir.pvpperformancetracker.models.FightLogEntry; +import net.runelite.api.ActorSpotAnim; +import net.runelite.api.GraphicID; +import net.runelite.api.IterableHashTable; import net.runelite.api.Player; import net.runelite.api.PlayerComposition; import net.runelite.api.kit.KitType; -import net.runelite.api.GraphicID; @Slf4j @Getter @@ -215,14 +217,14 @@ void addAttack(Player opponent, AnimationData animationData, int offensivePray, // --- Detect dark bow & dragon crossbow specials via GFX --- if (weapon == EquipmentData.DARK_BOW && animationData == AnimationData.RANGED_SHORTBOW) { - boolean spec = opponent.getGraphic() == GFX_TARGET_DBOW_SPEC; + boolean spec = hasTargetSpotAnim(opponent, GFX_TARGET_DBOW_SPEC); animationData = spec ? AnimationData.RANGED_DARK_BOW_SPEC : AnimationData.RANGED_DARK_BOW; } else if (weapon == EquipmentData.DRAGON_CROSSBOW && (animationData == AnimationData.RANGED_CROSSBOW_PVP || animationData == AnimationData.RANGED_RUNE_CROSSBOW)) { - boolean spec = opponent.getGraphic() == GFX_TARGET_DCBOW_SPEC; + boolean spec = hasTargetSpotAnim(opponent, GFX_TARGET_DCBOW_SPEC); if (spec) { @@ -364,6 +366,30 @@ void died() dead = true; } + private static boolean hasTargetSpotAnim(Player opponent, int spotAnimId) + { + if (opponent.getGraphic() == spotAnimId) + { + return true; + } + + IterableHashTable spotAnims = opponent.getSpotAnims(); + if (spotAnims == null) + { + return false; + } + + for (ActorSpotAnim spotAnim : spotAnims) + { + if (spotAnim != null && spotAnim.getId() == spotAnimId) + { + return true; + } + } + + return false; + } + AnimationData getAnimationData() { return AnimationData.fromId(player.getAnimation()); From 7908d9984e51fdfbadd9a9313025a3527770a13b Mon Sep 17 00:00:00 2001 From: Sacca Date: Sun, 4 Jan 2026 11:29:30 +0100 Subject: [PATCH 2/5] elysian proc damage reduction --- .../controllers/Fighter.java | 7 +++ .../controllers/PvpDamageCalc.java | 23 +++++++ .../models/FightLogEntry.java | 5 ++ .../views/FightLogFrame.java | 60 +++++++++++++++---- 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java index 89a5b51..0ed6dec 100644 --- a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java +++ b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java @@ -47,6 +47,7 @@ import net.runelite.api.IterableHashTable; import net.runelite.api.Player; import net.runelite.api.PlayerComposition; +import net.runelite.api.gameval.SpotanimID; import net.runelite.api.kit.KitType; @Slf4j @@ -213,6 +214,7 @@ void addAttack(Player opponent, AnimationData animationData, int offensivePray, // Granite Maul specific handling boolean isGmaulSpec = animationData == AnimationData.MELEE_GRANITE_MAUL_SPEC; + boolean elyProc = hasTargetSpotAnim(opponent, SpotanimID.ELYSIAN_SHIELD_DEFEND_SPOTANIM); // --- Detect dark bow & dragon crossbow specials via GFX --- if (weapon == EquipmentData.DARK_BOW && animationData == AnimationData.RANGED_SHORTBOW) @@ -254,6 +256,10 @@ else if (weapon == EquipmentData.DRAGON_CROSSBOW && } pvpDamageCalc.updateDamageStats(player, opponent, successful, animationData); + if (elyProc) + { + pvpDamageCalc.applyElysianReduction(); + } expectedDamage += pvpDamageCalc.getAverageHit(); if (animationData.attackStyle == AnimationData.AttackStyle.MAGIC) @@ -268,6 +274,7 @@ else if (weapon == EquipmentData.DRAGON_CROSSBOW && } FightLogEntry fightLogEntry = new FightLogEntry(player, opponent, pvpDamageCalc, offensivePray, levels, animationData); + fightLogEntry.setElyProc(elyProc); fightLogEntry.setGmaulSpecial(isGmaulSpec); if (PvpPerformanceTrackerPlugin.CONFIG.fightLogInChat()) { diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java b/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java index 11482bc..51d53b3 100644 --- a/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java +++ b/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java @@ -65,6 +65,7 @@ public class PvpDamageCalc private static final int STANCE_BONUS = 0; // assume they are not in controlled or defensive private static final double UNSUCCESSFUL_PRAY_DMG_MODIFIER = 0.6; // modifier for when you unsuccessfully hit off-pray + private static final double ELYSIAN_DAMAGE_MULTIPLIER = 0.75; // Offensive pray: assume you have valid. Piety for melee, Rigour for range, Augury for mage private static final double PIETY_ATK_PRAYER_MODIFIER = 1.2; @@ -255,6 +256,28 @@ else if (attackStyle == AttackStyle.MAGIC) maxHit = (int)(maxHit * (success ? 1 : UNSUCCESSFUL_PRAY_DMG_MODIFIER)); minHit = (int)(minHit * (success ? 1 : UNSUCCESSFUL_PRAY_DMG_MODIFIER)); + + if (atkLog.isElyProc()) + { + applyElysianReduction(); + } + } + + public void applyElysianReduction() + { + applyDamageMultiplier(ELYSIAN_DAMAGE_MULTIPLIER); + } + + private void applyDamageMultiplier(double multiplier) + { + if (multiplier == 1) + { + return; + } + + averageHit *= multiplier; + minHit = (int) Math.floor(minHit * multiplier); + maxHit = (int) Math.floor(maxHit * multiplier); } private void getAverageHit(boolean success, EquipmentData weapon, boolean usingSpec) diff --git a/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java b/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java index 32afa47..bdc7ccc 100644 --- a/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java +++ b/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java @@ -107,6 +107,10 @@ public class FightLogEntry implements Comparable @Expose @SerializedName("s") private boolean splash; // true if it was a magic attack and it splashed + @Expose + @SerializedName("E") + @Setter + private boolean elyProc = false; @Expose @SerializedName("C") @@ -257,6 +261,7 @@ public FightLogEntry(FightLogEntry e, PvpDamageCalc pvpDamageCalc) this.minHit = pvpDamageCalc.getMinHit(); this.maxHit = pvpDamageCalc.getMaxHit(); this.splash = e.splash; + this.elyProc = e.elyProc; this.attackerLevels = e.attackerLevels; // defender data diff --git a/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java b/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java index 4256461..7123424 100644 --- a/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java +++ b/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java @@ -27,6 +27,7 @@ import java.awt.BorderLayout; import java.awt.Component; +import java.awt.FlowLayout; import java.awt.Point; import java.awt.image.BufferedImage; import java.math.RoundingMode; @@ -52,6 +53,7 @@ import static matsyir.pvpperformancetracker.PvpPerformanceTrackerPlugin.PLUGIN_ICON; import matsyir.pvpperformancetracker.utils.PvpPerformanceTrackerUtils; +import net.runelite.api.ItemID; import net.runelite.api.SpriteID; @Slf4j @@ -201,16 +203,7 @@ private FightLogFrame(FightPerformance fight, ArrayList logEntrie stats[i][11] = ""; } // Offensive Pray (Index 12) - JLabel attPrayLabel = new JLabel(); - if (fightEntry.getAttackerOffensivePray() > 0) - { - PLUGIN.addSpriteToLabelIfValid(attPrayLabel, fightEntry.getAttackerOffensivePray(), this::repaint); - stats[i][12] = attPrayLabel; - } - else - { - stats[i][12] = ""; - } + stats[i][12] = buildOffensivePrayCell(fightEntry); int tickDuration = fightEntry.getTick() - initialTick; int durationMillis = (tickDuration * 600); // (* 0.6) to get duration in secs from ticks, so *600 for ms Duration duration = Duration.ofMillis(durationMillis); @@ -271,7 +264,14 @@ private static class BufferedImageCellRenderer extends DefaultTableCellRenderer public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); - if (value instanceof BufferedImage) + if (value instanceof JPanel) + { + JPanel panel = (JPanel) value; + panel.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground()); + panel.setOpaque(true); + return panel; + } + else if (value instanceof BufferedImage) { setText(""); setIcon(new ImageIcon((BufferedImage) value)); @@ -293,6 +293,44 @@ else if (value instanceof JLabel) } } + private Component buildOffensivePrayCell(FightLogEntry fightEntry) + { + boolean hasPray = fightEntry.getAttackerOffensivePray() > 0; + boolean hasEly = fightEntry.isElyProc(); + + if (!hasPray && !hasEly) + { + return ""; + } + + if (hasPray && hasEly) + { + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)); + panel.setOpaque(false); + + JLabel prayLabel = new JLabel(); + PLUGIN.addSpriteToLabelIfValid(prayLabel, fightEntry.getAttackerOffensivePray(), this::repaint); + panel.add(prayLabel); + + JLabel elyLabel = new JLabel(); + PLUGIN.addItemToLabelIfValid(elyLabel, ItemID.ELYSIAN_SPIRIT_SHIELD, false, this::repaint, "Elysian proc"); + panel.add(elyLabel); + + return panel; + } + + JLabel label = new JLabel(); + if (hasPray) + { + PLUGIN.addSpriteToLabelIfValid(label, fightEntry.getAttackerOffensivePray(), this::repaint); + } + else + { + PLUGIN.addItemToLabelIfValid(label, ItemID.ELYSIAN_SPIRIT_SHIELD, false, this::repaint, "Elysian proc"); + } + return label; + } + // initialize frame using an AnalyzedFight, in order to pass the analyzed fight data // to the detailed frame. private FightLogFrame(AnalyzedFightPerformance fight, JRootPane rootPane) From 5cc1189f892b9df1aadfe482a3bb029d6859fc34 Mon Sep 17 00:00:00 2001 From: Sacca Date: Sun, 4 Jan 2026 12:06:58 +0100 Subject: [PATCH 3/5] staff spec damage reduction --- .../controllers/Fighter.java | 34 ++++++++++++++ .../controllers/PvpDamageCalc.java | 10 ++++ .../models/FightLogEntry.java | 5 ++ .../views/FightLogFrame.java | 46 +++++++++++++------ 4 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java index 0ed6dec..b3593b9 100644 --- a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java +++ b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java @@ -255,11 +255,21 @@ else if (weapon == EquipmentData.DRAGON_CROSSBOW && animationData = animationData.isSpecial ? AnimationData.MELEE_VLS_SPEC : AnimationData.MELEE_SCIM_SLASH; } + boolean staffMeleeReduction = false; + if (animationData.attackStyle.isMelee()) + { + staffMeleeReduction = hasStaffMeleeReduction(opponent); + } + pvpDamageCalc.updateDamageStats(player, opponent, successful, animationData); if (elyProc) { pvpDamageCalc.applyElysianReduction(); } + if (staffMeleeReduction) + { + pvpDamageCalc.applyStaffMeleeReduction(); + } expectedDamage += pvpDamageCalc.getAverageHit(); if (animationData.attackStyle == AnimationData.AttackStyle.MAGIC) @@ -275,6 +285,7 @@ else if (weapon == EquipmentData.DRAGON_CROSSBOW && FightLogEntry fightLogEntry = new FightLogEntry(player, opponent, pvpDamageCalc, offensivePray, levels, animationData); fightLogEntry.setElyProc(elyProc); + fightLogEntry.setStaffMeleeReductionProc(staffMeleeReduction); fightLogEntry.setGmaulSpecial(isGmaulSpec); if (PvpPerformanceTrackerPlugin.CONFIG.fightLogInChat()) { @@ -397,6 +408,29 @@ private static boolean hasTargetSpotAnim(Player opponent, int spotAnimId) return false; } + private static boolean hasStaffMeleeReduction(Player opponent) + { + if (hasTargetSpotAnim(opponent, SpotanimID.SOTD_SPECIAL_START) || + hasTargetSpotAnim(opponent, SpotanimID.SOTD_SPECIAL_EXTRA)) + { + return true; + } + + if (hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_LIGHT_SPECIAL_START) || + hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_LIGHT_SPECIAL_EXTRA)) + { + return true; + } + + if (hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_BALANCE_SPECIAL_START) || + hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_BALANCE_SPECIAL_EXTRA)) + { + return true; + } + + return false; + } + AnimationData getAnimationData() { return AnimationData.fromId(player.getAnimation()); diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java b/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java index 51d53b3..1db59cd 100644 --- a/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java +++ b/src/main/java/matsyir/pvpperformancetracker/controllers/PvpDamageCalc.java @@ -66,6 +66,7 @@ public class PvpDamageCalc private static final int STANCE_BONUS = 0; // assume they are not in controlled or defensive private static final double UNSUCCESSFUL_PRAY_DMG_MODIFIER = 0.6; // modifier for when you unsuccessfully hit off-pray private static final double ELYSIAN_DAMAGE_MULTIPLIER = 0.75; + private static final double STAFF_MELEE_DAMAGE_MULTIPLIER = 0.5; // Offensive pray: assume you have valid. Piety for melee, Rigour for range, Augury for mage private static final double PIETY_ATK_PRAYER_MODIFIER = 1.2; @@ -261,6 +262,10 @@ else if (attackStyle == AttackStyle.MAGIC) { applyElysianReduction(); } + if (atkLog.isStaffMeleeReductionProc() && atkLog.getAnimationData().attackStyle.isMelee()) + { + applyStaffMeleeReduction(); + } } public void applyElysianReduction() @@ -268,6 +273,11 @@ public void applyElysianReduction() applyDamageMultiplier(ELYSIAN_DAMAGE_MULTIPLIER); } + public void applyStaffMeleeReduction() + { + applyDamageMultiplier(STAFF_MELEE_DAMAGE_MULTIPLIER); + } + private void applyDamageMultiplier(double multiplier) { if (multiplier == 1) diff --git a/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java b/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java index bdc7ccc..61ab664 100644 --- a/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java +++ b/src/main/java/matsyir/pvpperformancetracker/models/FightLogEntry.java @@ -111,6 +111,10 @@ public class FightLogEntry implements Comparable @SerializedName("E") @Setter private boolean elyProc = false; + @Expose + @SerializedName("S") + @Setter + private boolean staffMeleeReductionProc = false; @Expose @SerializedName("C") @@ -262,6 +266,7 @@ public FightLogEntry(FightLogEntry e, PvpDamageCalc pvpDamageCalc) this.maxHit = pvpDamageCalc.getMaxHit(); this.splash = e.splash; this.elyProc = e.elyProc; + this.staffMeleeReductionProc = e.staffMeleeReductionProc; this.attackerLevels = e.attackerLevels; // defender data diff --git a/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java b/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java index 7123424..2434301 100644 --- a/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java +++ b/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java @@ -297,38 +297,54 @@ private Component buildOffensivePrayCell(FightLogEntry fightEntry) { boolean hasPray = fightEntry.getAttackerOffensivePray() > 0; boolean hasEly = fightEntry.isElyProc(); + boolean hasStaffReduction = fightEntry.isStaffMeleeReductionProc(); - if (!hasPray && !hasEly) + if (!hasPray && !hasEly && !hasStaffReduction) { - return ""; + return new JLabel(); } - if (hasPray && hasEly) + ArrayList icons = new ArrayList<>(); + if (hasPray) { - JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)); - panel.setOpaque(false); - JLabel prayLabel = new JLabel(); PLUGIN.addSpriteToLabelIfValid(prayLabel, fightEntry.getAttackerOffensivePray(), this::repaint); - panel.add(prayLabel); + icons.add(prayLabel); + } + if (hasEly) + { JLabel elyLabel = new JLabel(); PLUGIN.addItemToLabelIfValid(elyLabel, ItemID.ELYSIAN_SPIRIT_SHIELD, false, this::repaint, "Elysian proc"); - panel.add(elyLabel); + icons.add(elyLabel); + } - return panel; + if (hasStaffReduction) + { + JLabel staffLabel = new JLabel(); + PLUGIN.addItemToLabelIfValid(staffLabel, ItemID.STAFF_OF_THE_DEAD, false, this::repaint, "Staff spec damage reduction"); + icons.add(staffLabel); } - JLabel label = new JLabel(); - if (hasPray) + if (icons.size() == 1) { - PLUGIN.addSpriteToLabelIfValid(label, fightEntry.getAttackerOffensivePray(), this::repaint); + return icons.get(0); } - else + + if (icons.size() > 1) { - PLUGIN.addItemToLabelIfValid(label, ItemID.ELYSIAN_SPIRIT_SHIELD, false, this::repaint, "Elysian proc"); + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)); + panel.setOpaque(false); + + for (JLabel icon : icons) + { + panel.add(icon); + } + + return panel; } - return label; + + return new JLabel(); } // initialize frame using an AnalyzedFight, in order to pass the analyzed fight data From 44af80843878e19b29d668d07cd431163eaf8bb7 Mon Sep 17 00:00:00 2001 From: Sacca Date: Thu, 15 Jan 2026 16:30:11 +0100 Subject: [PATCH 4/5] wrong column fix --- .../views/FightLogFrame.java | 38 +++++++++++-------- 1 file changed, 22 insertions(+), 16 deletions(-) diff --git a/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java b/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java index 2434301..8b8ecf8 100644 --- a/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java +++ b/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java @@ -179,17 +179,7 @@ private FightLogFrame(FightPerformance fight, ArrayList logEntrie // Off-Pray? (Index 9) stats[i][9] = fightEntry.success() ? "✔" : ""; // Def Prayer (Index 10) - int prayIcon = PvpPerformanceTrackerUtils.getSpriteForHeadIcon(fightEntry.getDefenderOverhead()); - if (prayIcon > 0) - { - JLabel defPrayLabel = new JLabel(); - PLUGIN.addSpriteToLabelIfValid(defPrayLabel, prayIcon, this::repaint); - stats[i][10] = defPrayLabel; - } - else - { - stats[i][10] = ""; - } + stats[i][10] = buildDefensivePrayCell(fightEntry); // Splash (Index 11) if (fightEntry.getAnimationData().attackStyle == AnimationData.AttackStyle.MAGIC) { @@ -221,6 +211,7 @@ private FightLogFrame(FightPerformance fight, ArrayList logEntrie table = new JTable(stats, header); table.setRowHeight(30); table.setDefaultEditor(Object.class, null); + table.getColumnModel().getColumn(10).setPreferredWidth(96); // room for def pray + proc icons table.getColumnModel().getColumn(1).setCellRenderer(new BufferedImageCellRenderer()); // Style table.getColumnModel().getColumn(5).setCellRenderer(new BufferedImageCellRenderer()); // Actual Dmg @@ -293,9 +284,10 @@ else if (value instanceof JLabel) } } - private Component buildOffensivePrayCell(FightLogEntry fightEntry) + private Component buildDefensivePrayCell(FightLogEntry fightEntry) { - boolean hasPray = fightEntry.getAttackerOffensivePray() > 0; + int prayIcon = PvpPerformanceTrackerUtils.getSpriteForHeadIcon(fightEntry.getDefenderOverhead()); + boolean hasPray = prayIcon > 0; boolean hasEly = fightEntry.isElyProc(); boolean hasStaffReduction = fightEntry.isStaffMeleeReductionProc(); @@ -307,9 +299,10 @@ private Component buildOffensivePrayCell(FightLogEntry fightEntry) ArrayList icons = new ArrayList<>(); if (hasPray) { - JLabel prayLabel = new JLabel(); - PLUGIN.addSpriteToLabelIfValid(prayLabel, fightEntry.getAttackerOffensivePray(), this::repaint); - icons.add(prayLabel); + JLabel defPrayLabel = new JLabel(); + PLUGIN.addSpriteToLabelIfValid(defPrayLabel, prayIcon, this::repaint); + defPrayLabel.setToolTipText("Defensive Prayer"); + icons.add(defPrayLabel); } if (hasEly) @@ -347,6 +340,19 @@ private Component buildOffensivePrayCell(FightLogEntry fightEntry) return new JLabel(); } + private Component buildOffensivePrayCell(FightLogEntry fightEntry) + { + int offensivePray = fightEntry.getAttackerOffensivePray(); + if (offensivePray <= 0) + { + return new JLabel(); + } + + JLabel prayLabel = new JLabel(); + PLUGIN.addSpriteToLabelIfValid(prayLabel, offensivePray, this::repaint); + return prayLabel; + } + // initialize frame using an AnalyzedFight, in order to pass the analyzed fight data // to the detailed frame. private FightLogFrame(AnalyzedFightPerformance fight, JRootPane rootPane) From fe0aa4c0f42ca05e5629f49fd3b27a9dffdac641 Mon Sep 17 00:00:00 2001 From: Sacca Date: Fri, 16 Jan 2026 12:26:55 +0100 Subject: [PATCH 5/5] fightlog frame structure --- .../controllers/Fighter.java | 25 +--- .../views/FightLogFrame.java | 137 +++++++++--------- 2 files changed, 72 insertions(+), 90 deletions(-) diff --git a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java index b3593b9..e934d01 100644 --- a/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java +++ b/src/main/java/matsyir/pvpperformancetracker/controllers/Fighter.java @@ -410,25 +410,12 @@ private static boolean hasTargetSpotAnim(Player opponent, int spotAnimId) private static boolean hasStaffMeleeReduction(Player opponent) { - if (hasTargetSpotAnim(opponent, SpotanimID.SOTD_SPECIAL_START) || - hasTargetSpotAnim(opponent, SpotanimID.SOTD_SPECIAL_EXTRA)) - { - return true; - } - - if (hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_LIGHT_SPECIAL_START) || - hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_LIGHT_SPECIAL_EXTRA)) - { - return true; - } - - if (hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_BALANCE_SPECIAL_START) || - hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_BALANCE_SPECIAL_EXTRA)) - { - return true; - } - - return false; + return hasTargetSpotAnim(opponent, SpotanimID.SOTD_SPECIAL_START) || + hasTargetSpotAnim(opponent, SpotanimID.SOTD_SPECIAL_EXTRA) || + hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_LIGHT_SPECIAL_START) || + hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_LIGHT_SPECIAL_EXTRA) || + hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_BALANCE_SPECIAL_START) || + hasTargetSpotAnim(opponent, SpotanimID.STAFF_OF_BALANCE_SPECIAL_EXTRA); } AnimationData getAnimationData() diff --git a/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java b/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java index 8b8ecf8..32ec4b1 100644 --- a/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java +++ b/src/main/java/matsyir/pvpperformancetracker/views/FightLogFrame.java @@ -179,7 +179,62 @@ private FightLogFrame(FightPerformance fight, ArrayList logEntrie // Off-Pray? (Index 9) stats[i][9] = fightEntry.success() ? "✔" : ""; // Def Prayer (Index 10) - stats[i][10] = buildDefensivePrayCell(fightEntry); + int prayIcon = PvpPerformanceTrackerUtils.getSpriteForHeadIcon(fightEntry.getDefenderOverhead()); + boolean hasPray = prayIcon > 0; + boolean hasEly = fightEntry.isElyProc(); + boolean hasStaffReduction = fightEntry.isStaffMeleeReductionProc(); + + if (!hasPray && !hasEly && !hasStaffReduction) + { + stats[i][10] = ""; + } + else if ((hasPray ? 1 : 0) + (hasEly ? 1 : 0) + (hasStaffReduction ? 1 : 0) == 1) + { + JLabel label = new JLabel(); + if (hasPray) + { + PLUGIN.addSpriteToLabelIfValid(label, prayIcon, this::repaint); + label.setToolTipText("Defensive Prayer"); + } + else if (hasEly) + { + PLUGIN.addItemToLabelIfValid(label, ItemID.ELYSIAN_SPIRIT_SHIELD, false, this::repaint, "Elysian proc"); + } + else + { + PLUGIN.addItemToLabelIfValid(label, ItemID.STAFF_OF_THE_DEAD, false, this::repaint, "Staff spec damage reduction"); + } + stats[i][10] = label; + } + else + { + JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)); + panel.setOpaque(false); + + if (hasPray) + { + JLabel defPrayLabel = new JLabel(); + PLUGIN.addSpriteToLabelIfValid(defPrayLabel, prayIcon, this::repaint); + defPrayLabel.setToolTipText("Defensive Prayer"); + panel.add(defPrayLabel); + } + + if (hasEly) + { + JLabel elyLabel = new JLabel(); + PLUGIN.addItemToLabelIfValid(elyLabel, ItemID.ELYSIAN_SPIRIT_SHIELD, false, this::repaint, "Elysian proc"); + panel.add(elyLabel); + } + + if (hasStaffReduction) + { + JLabel staffLabel = new JLabel(); + PLUGIN.addItemToLabelIfValid(staffLabel, ItemID.STAFF_OF_THE_DEAD, false, this::repaint, "Staff spec damage reduction"); + panel.add(staffLabel); + } + + stats[i][10] = panel; + } // Splash (Index 11) if (fightEntry.getAnimationData().attackStyle == AnimationData.AttackStyle.MAGIC) { @@ -193,7 +248,16 @@ private FightLogFrame(FightPerformance fight, ArrayList logEntrie stats[i][11] = ""; } // Offensive Pray (Index 12) - stats[i][12] = buildOffensivePrayCell(fightEntry); + JLabel attPrayLabel = new JLabel(); + if (fightEntry.getAttackerOffensivePray() > 0) + { + PLUGIN.addSpriteToLabelIfValid(attPrayLabel, fightEntry.getAttackerOffensivePray(), this::repaint); + stats[i][12] = attPrayLabel; + } + else + { + stats[i][12] = ""; + } int tickDuration = fightEntry.getTick() - initialTick; int durationMillis = (tickDuration * 600); // (* 0.6) to get duration in secs from ticks, so *600 for ms Duration duration = Duration.ofMillis(durationMillis); @@ -284,75 +348,6 @@ else if (value instanceof JLabel) } } - private Component buildDefensivePrayCell(FightLogEntry fightEntry) - { - int prayIcon = PvpPerformanceTrackerUtils.getSpriteForHeadIcon(fightEntry.getDefenderOverhead()); - boolean hasPray = prayIcon > 0; - boolean hasEly = fightEntry.isElyProc(); - boolean hasStaffReduction = fightEntry.isStaffMeleeReductionProc(); - - if (!hasPray && !hasEly && !hasStaffReduction) - { - return new JLabel(); - } - - ArrayList icons = new ArrayList<>(); - if (hasPray) - { - JLabel defPrayLabel = new JLabel(); - PLUGIN.addSpriteToLabelIfValid(defPrayLabel, prayIcon, this::repaint); - defPrayLabel.setToolTipText("Defensive Prayer"); - icons.add(defPrayLabel); - } - - if (hasEly) - { - JLabel elyLabel = new JLabel(); - PLUGIN.addItemToLabelIfValid(elyLabel, ItemID.ELYSIAN_SPIRIT_SHIELD, false, this::repaint, "Elysian proc"); - icons.add(elyLabel); - } - - if (hasStaffReduction) - { - JLabel staffLabel = new JLabel(); - PLUGIN.addItemToLabelIfValid(staffLabel, ItemID.STAFF_OF_THE_DEAD, false, this::repaint, "Staff spec damage reduction"); - icons.add(staffLabel); - } - - if (icons.size() == 1) - { - return icons.get(0); - } - - if (icons.size() > 1) - { - JPanel panel = new JPanel(new FlowLayout(FlowLayout.LEFT, 2, 0)); - panel.setOpaque(false); - - for (JLabel icon : icons) - { - panel.add(icon); - } - - return panel; - } - - return new JLabel(); - } - - private Component buildOffensivePrayCell(FightLogEntry fightEntry) - { - int offensivePray = fightEntry.getAttackerOffensivePray(); - if (offensivePray <= 0) - { - return new JLabel(); - } - - JLabel prayLabel = new JLabel(); - PLUGIN.addSpriteToLabelIfValid(prayLabel, offensivePray, this::repaint); - return prayLabel; - } - // initialize frame using an AnalyzedFight, in order to pass the analyzed fight data // to the detailed frame. private FightLogFrame(AnalyzedFightPerformance fight, JRootPane rootPane)