From 7033fc864be31db1a41ab5c140bdaf39c0a20508 Mon Sep 17 00:00:00 2001 From: Lexer747 Date: Thu, 19 Mar 2026 18:58:53 +0000 Subject: [PATCH 1/3] formatting --- .../attacktimer/AttackTimerBarOverlay.java | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/attacktimer/AttackTimerBarOverlay.java b/src/main/java/com/attacktimer/AttackTimerBarOverlay.java index 2bb1eb2..16b1024 100644 --- a/src/main/java/com/attacktimer/AttackTimerBarOverlay.java +++ b/src/main/java/com/attacktimer/AttackTimerBarOverlay.java @@ -1,7 +1,12 @@ package com.attacktimer; + /* - * Copyright (c) 2018, Tomas Slusny * Copyright (c) 2018, Chdata + * Copyright (c) 2018, Tomas Slusny + * Copyright (c) 2022, Nick Graves + * Copyright (c) 2025-2026, Pedro Alves + * Copyright (c) 2024-2026, Lexer747 + * * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -42,15 +47,16 @@ import net.runelite.client.ui.overlay.OverlayPriority; import net.runelite.client.util.ImageUtil; - @Singleton class AttackTimerBarOverlay extends Overlay { private static final Color BAR_FILL_COLOR = new Color(201, 161, 28); private static final Color BAR_BG_COLOR = Color.black; private static final Dimension ATTACK_BAR_SIZE = new Dimension(30, 5); - private static final BufferedImage HD_FRONT_BAR = ImageUtil.loadImageResource(AttackTimerMetronomePlugin.class, "/front.png"); - private static final BufferedImage HD_BACK_BAR = ImageUtil.loadImageResource(AttackTimerMetronomePlugin.class, "/back.png"); + private static final BufferedImage HD_FRONT_BAR = ImageUtil.loadImageResource(AttackTimerMetronomePlugin.class, + "/front.png"); + private static final BufferedImage HD_BACK_BAR = ImageUtil.loadImageResource(AttackTimerMetronomePlugin.class, + "/back.png"); private final Client client; private final AttackTimerMetronomeConfig config; private final AttackTimerMetronomePlugin plugin; @@ -58,7 +64,8 @@ class AttackTimerBarOverlay extends Overlay private boolean shouldShowBar = false; @Inject - private AttackTimerBarOverlay(final Client client, final AttackTimerMetronomeConfig config, final AttackTimerMetronomePlugin plugin) + private AttackTimerBarOverlay(final Client client, final AttackTimerMetronomeConfig config, + final AttackTimerMetronomePlugin plugin) { this.client = client; this.config = config; @@ -86,15 +93,17 @@ public Dimension render(Graphics2D graphics) int denomMod = (config.barEmpties()) ? 1 : 0; int numerMod = (config.barFills()) ? 1 : 0; float ratio = (float) (plugin.getTicksUntilNextAttack() - numerMod) / (float) (plugin.getWeaponPeriod() - denomMod); - if (!config.barDirection()) { - ratio = (float)Math.max(1.0f - ratio, 0f); + if (!config.barDirection()) + { + ratio = (float) Math.max(1.0f - ratio, 0f); } AttackBarStyle barStyle = config.barStyle(); - boolean useHD = barStyle == AttackBarStyle.HIGH_DETAIL - || (barStyle == AttackBarStyle.AUTO && client.getSpriteOverrides().containsKey(SpriteID.HEALTHBAR_DEFAULT_FRONT_30PX)); + boolean useHD = barStyle == AttackBarStyle.HIGH_DETAIL || (barStyle == AttackBarStyle.AUTO + && client.getSpriteOverrides().containsKey(SpriteID.HEALTHBAR_DEFAULT_FRONT_30PX)); - if (useHD) { + if (useHD) + { final int barWidth = HD_FRONT_BAR.getWidth(); final int barHeight = HD_FRONT_BAR.getHeight(); final int barX = canvasPoint.getX() - barWidth / 2; @@ -104,7 +113,8 @@ public Dimension render(Graphics2D graphics) graphics.drawImage(HD_BACK_BAR, barX, barY, barWidth, barHeight, null); // Use a sub-image to create the same effect the HD Health Bar has - graphics.drawImage(HD_FRONT_BAR.getSubimage(0, 0, progressFill, barHeight), barX, barY, progressFill, barHeight, null); + graphics.drawImage(HD_FRONT_BAR.getSubimage(0, 0, progressFill, barHeight), barX, barY, progressFill, + barHeight, null); return null; } // Draw bar @@ -113,7 +123,8 @@ public Dimension render(Graphics2D graphics) final int barWidth = ATTACK_BAR_SIZE.width; final int barHeight = ATTACK_BAR_SIZE.height; - // Restricted by the width to prevent the bar from being too long while you are boosted above your real prayer level. + // Restricted by the width to prevent the bar from being too long while you are boosted above your + // real prayer level. final int progressFill = (int) Math.ceil(Math.min((barWidth * ratio), barWidth)); graphics.setColor(BAR_BG_COLOR); @@ -123,19 +134,23 @@ public Dimension render(Graphics2D graphics) return null; } + private void onTick() { shouldShowBar = true; - if (!config.enableMetronome()) { + if (!config.enableMetronome()) + { shouldShowBar = false; } - if (!config.showBar()) { + if (!config.showBar()) + { shouldShowBar = false; } - if (!plugin.isAttackCooldownPending()) { + if (!plugin.isAttackCooldownPending()) + { shouldShowBar = false; } } From ceb722e2efbf05ae4d9639ac7513747e67cac011 Mon Sep 17 00:00:00 2001 From: Lexer747 Date: Thu, 19 Mar 2026 20:48:15 +0000 Subject: [PATCH 2/3] Fix the issue --- .../attacktimer/AttackTimerBarOverlay.java | 41 +++++++++++++++---- 1 file changed, 33 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/attacktimer/AttackTimerBarOverlay.java b/src/main/java/com/attacktimer/AttackTimerBarOverlay.java index 16b1024..5609499 100644 --- a/src/main/java/com/attacktimer/AttackTimerBarOverlay.java +++ b/src/main/java/com/attacktimer/AttackTimerBarOverlay.java @@ -36,6 +36,7 @@ import java.awt.image.BufferedImage; import javax.inject.Inject; import javax.inject.Singleton; +import lombok.NonNull; import net.runelite.api.Client; import net.runelite.api.Perspective; import net.runelite.api.Point; @@ -57,7 +58,7 @@ class AttackTimerBarOverlay extends Overlay "/front.png"); private static final BufferedImage HD_BACK_BAR = ImageUtil.loadImageResource(AttackTimerMetronomePlugin.class, "/back.png"); - private final Client client; + private final @NonNull Client client; private final AttackTimerMetronomeConfig config; private final AttackTimerMetronomePlugin plugin; @@ -85,18 +86,30 @@ public Dimension render(Graphics2D graphics) { return null; } + if (client == null) + { + return null; + } final int height = client.getLocalPlayer().getLogicalHeight() + config.heightOffset() + 5; final LocalPoint localLocation = client.getLocalPlayer().getLocalLocation(); + if (localLocation == null) + { + return null; + } final Point canvasPoint = Perspective.localToCanvas(client, localLocation, client.getTopLevelWorldView().getPlane(), height); + if (canvasPoint == null) + { + return null; + } int denomMod = (config.barEmpties()) ? 1 : 0; int numerMod = (config.barFills()) ? 1 : 0; float ratio = (float) (plugin.getTicksUntilNextAttack() - numerMod) / (float) (plugin.getWeaponPeriod() - denomMod); - if (!config.barDirection()) - { - ratio = (float) Math.max(1.0f - ratio, 0f); - } + // barDirection: + // true -> drain -> moves right to left each tick + // false -> fills -> move left to right each tick + ratio = config.barDirection() ? clampBetween0and1(ratio) : clampBetween0and1(1.0f - ratio); AttackBarStyle barStyle = config.barStyle(); boolean useHD = barStyle == AttackBarStyle.HIGH_DETAIL || (barStyle == AttackBarStyle.AUTO @@ -112,9 +125,16 @@ public Dimension render(Graphics2D graphics) final int progressFill = (int) Math.ceil(Math.min((barWidth * ratio), barWidth)); graphics.drawImage(HD_BACK_BAR, barX, barY, barWidth, barHeight, null); - // Use a sub-image to create the same effect the HD Health Bar has - graphics.drawImage(HD_FRONT_BAR.getSubimage(0, 0, progressFill, barHeight), barX, barY, progressFill, - barHeight, null); + // if progress is less than 1 then there's no "sub-bar" to draw as it would either have fractional + // pixels (not possible) or negative pixels (also not possible) fixes + // (https://github.com/ngraves95/attacktimer/issues/92) + if (progressFill <= 1) + { + return null; + } + // else draw a smaller bar as the "filler" image + var subImage = HD_FRONT_BAR.getSubimage(0, 0, progressFill, barHeight); + graphics.drawImage(subImage, barX, barY, progressFill, barHeight, null); return null; } // Draw bar @@ -135,6 +155,11 @@ public Dimension render(Graphics2D graphics) return null; } + private float clampBetween0and1(float x) + { + return Math.max(Math.min(x, 1.0f), 0.0f); + } + private void onTick() { shouldShowBar = true; From e5a29a2eefd3cc6993a553c9f8edde74aa490486 Mon Sep 17 00:00:00 2001 From: Lexer747 Date: Sat, 21 Mar 2026 23:15:52 +0000 Subject: [PATCH 3/3] review --- src/main/java/com/attacktimer/AttackTimerBarOverlay.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/attacktimer/AttackTimerBarOverlay.java b/src/main/java/com/attacktimer/AttackTimerBarOverlay.java index 5609499..e6039fa 100644 --- a/src/main/java/com/attacktimer/AttackTimerBarOverlay.java +++ b/src/main/java/com/attacktimer/AttackTimerBarOverlay.java @@ -107,8 +107,8 @@ public Dimension render(Graphics2D graphics) int numerMod = (config.barFills()) ? 1 : 0; float ratio = (float) (plugin.getTicksUntilNextAttack() - numerMod) / (float) (plugin.getWeaponPeriod() - denomMod); // barDirection: - // true -> drain -> moves right to left each tick - // false -> fills -> move left to right each tick + // true -> drain -> moves right to left each tick + // false -> fills -> move left to right each tick ratio = config.barDirection() ? clampBetween0and1(ratio) : clampBetween0and1(1.0f - ratio); AttackBarStyle barStyle = config.barStyle(); @@ -128,7 +128,7 @@ public Dimension render(Graphics2D graphics) // if progress is less than 1 then there's no "sub-bar" to draw as it would either have fractional // pixels (not possible) or negative pixels (also not possible) fixes // (https://github.com/ngraves95/attacktimer/issues/92) - if (progressFill <= 1) + if (progressFill < 1) { return null; }