Overhaul of the Tick Timers plugin. (#757)

* Overhaul of the Tick Timers plugin.
Added new OverlayUtils that are commonly found.
Added Attack Speed to npcManager. (Thanks wiki <3)
Updated Prayer API to contain widget info as well.

* Remove unnecessary code.

* Add Dag King animations.

* Update NPCManager to use int instead of Integer.

* Add Waterbith Region check for DKs

* Make valid region check happen only during game state change event.

* Renaming and other misc.

* Renaming and other misc.
This commit is contained in:
Ganom
2019-06-26 23:08:21 -04:00
committed by James
parent 24a6357baf
commit 7a7db495d4
14 changed files with 3433 additions and 459 deletions

View File

@@ -59,6 +59,7 @@ public class NPCManager
/**
* Returns the {@link NPCStats} for target NPC id
*
* @param npcId NPC id
* @return the {@link NPCStats} or null if unknown
*/
@@ -70,23 +71,41 @@ public class NPCManager
/**
* Returns health for target NPC ID
*
* @param npcId NPC id
* @return health or null if unknown
*/
@Nullable
public Integer getHealth(final int npcId)
public int getHealth(final int npcId)
{
final NPCStats s = statsMap.get(npcId);
if (s == null || s.getHitpoints() == -1)
{
return null;
return -1;
}
return s.getHitpoints();
}
/**
* Returns the attack speed for target NPC ID.
*
* @param npcId NPC id
* @return attack speed in game ticks for NPC ID.
*/
public int getAttackSpeed(final int npcId)
{
final NPCStats s = statsMap.get(npcId);
if (s == null || s.getAttackSpeed() == -1)
{
return -1;
}
return s.getAttackSpeed();
}
/**
* Returns the exp modifier for target NPC ID based on its stats.
*
* @param npcId NPC id
* @return npcs exp modifier. Assumes default xp rate if npc stats are unknown (returns 1)
*/

View File

@@ -34,6 +34,7 @@ public class NPCStats
private final int hitpoints;
private final int combatLevel;
private final int slayerLevel;
private final int attackSpeed;
private final int attackLevel;
private final int strengthLevel;

View File

@@ -65,7 +65,7 @@ class OpponentInfoOverlay extends Overlay
private final PanelComponent panelComponent = new PanelComponent();
private Integer lastMaxHealth;
private int lastMaxHealth;
private int lastRatio = 0;
private int lastHealthScale = 0;
private String opponentName;
@@ -111,7 +111,7 @@ class OpponentInfoOverlay extends Overlay
lastHealthScale = opponent.getHealth();
opponentName = Text.removeTags(opponent.getName());
lastMaxHealth = null;
lastMaxHealth = -1;
if (opponent instanceof NPC)
{
lastMaxHealth = npcManager.getHealth(((NPC) opponent).getId());
@@ -167,7 +167,7 @@ class OpponentInfoOverlay extends Overlay
final HitpointsDisplayStyle displayStyle = opponentInfoConfig.hitpointsDisplayStyle();
if ((displayStyle == HitpointsDisplayStyle.HITPOINTS || displayStyle == HitpointsDisplayStyle.BOTH)
&& lastMaxHealth != null)
&& lastMaxHealth != -1)
{
// This is the reverse of the calculation of healthRatio done by the server
// which is: healthRatio = 1 + (healthScale - 1) * health / maxHealth (if health > 0, 0 otherwise)

View File

@@ -98,9 +98,9 @@ class TargetWeaknessOverlay extends Overlay
final int healthScale = target.getHealth();
final int healthRatio = target.getHealthRatio();
final Integer maxHealth = npcManager.getHealth(target.getId());
final int maxHealth = npcManager.getHealth(target.getId());
if (healthRatio < 0 || healthScale <= 0 || maxHealth == null)
if (healthRatio < 0 || healthScale <= 0 || maxHealth == -1)
{
return -1;
}

View File

@@ -1,46 +0,0 @@
/*
* Copyright (c) 2019, ganom <https://github.com/Ganom>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package net.runelite.client.plugins.ticktimers;
import java.awt.Font;
import lombok.AllArgsConstructor;
import lombok.Getter;
@Getter
@AllArgsConstructor
public enum FontStyle
{
BOLD("Bold", Font.BOLD),
ITALIC("Italic", Font.ITALIC),
PLAIN("Plain", Font.PLAIN);
private String name;
private int font;
@Override
public String toString()
{
return getName();
}
}

View File

@@ -1,6 +1,6 @@
/*
* Copyright (c) 2018, Woox <https://github.com/wooxsolo>
* Copyright (c) 2019, Ganom <https://github.com/Ganom>
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -25,65 +25,125 @@
*/
package net.runelite.client.plugins.ticktimers;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import java.awt.Color;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import net.runelite.api.Actor;
import net.runelite.api.AnimationID;
import net.runelite.api.NPC;
import net.runelite.api.NPCDefinition;
import net.runelite.api.NpcID;
import net.runelite.api.Prayer;
@Getter
class NPCContainer
{
@Getter
private NPC npc;
@Getter
private int npcIndex;
@Getter
private String npcName;
@Getter
private int npcSize;
private ImmutableSet<Integer> animations;
private int attackSpeed;
@Setter
@Getter
private int TicksUntilAttack;
private int ticksUntilAttack;
@Setter
@Getter
private int npcSpeed;
@Setter
@Getter
private Actor npcInteracting;
@Setter
@Getter
private Attackstyle attackStyle;
private AttackStyle attackStyle;
NPCContainer(NPC npc)
NPCContainer(NPC npc, int attackSpeed)
{
this.npc = npc;
this.npcName = npc.getName();
this.npcIndex = npc.getIndex();
this.npcInteracting = npc.getInteracting();
this.npcSpeed = 0;
this.attackStyle = Attackstyle.UNKNOWN;
this.TicksUntilAttack = 0;
this.attackStyle = AttackStyle.UNKNOWN;
this.attackSpeed = attackSpeed;
this.ticksUntilAttack = -1;
final NPCDefinition composition = npc.getTransformedDefinition();
BossMonsters monster = BossMonsters.of(npc.getId());
if (monster == null)
{
throw new IllegalStateException();
}
this.animations = monster.animations;
this.attackStyle = monster.attackStyle;
if (composition != null)
{
this.npcSize = composition.getSize();
}
}
@RequiredArgsConstructor
enum BossMonsters
{
SERGEANT_STRONGSTACK(NpcID.SERGEANT_STRONGSTACK, AttackStyle.MELEE, ImmutableSet.of(AnimationID.MINION_AUTO1, AnimationID.MINION_AUTO2, AnimationID.MINION_AUTO3)),
SERGEANT_STEELWILL(NpcID.SERGEANT_STEELWILL, AttackStyle.MAGE, ImmutableSet.of(AnimationID.MINION_AUTO1, AnimationID.MINION_AUTO2, AnimationID.MINION_AUTO3)),
SERGEANT_GRIMSPIKE(NpcID.SERGEANT_GRIMSPIKE, AttackStyle.RANGE, ImmutableSet.of(AnimationID.MINION_AUTO1, AnimationID.MINION_AUTO2, AnimationID.MINION_AUTO4)),
GENERAL_GRAARDOR(NpcID.GENERAL_GRAARDOR, AttackStyle.MELEE, ImmutableSet.of(AnimationID.GENERAL_AUTO1, AnimationID.GENERAL_AUTO2, AnimationID.GENERAL_AUTO3)),
TSTANON_KARLAK(NpcID.TSTANON_KARLAK, AttackStyle.MELEE, ImmutableSet.of(AnimationID.ZAMMY_GENERIC_AUTO)),
BALFRUG_KREEYATH(NpcID.BALFRUG_KREEYATH, AttackStyle.MAGE, ImmutableSet.of(AnimationID.ZAMMY_GENERIC_AUTO, AnimationID.BALFRUG_AUTO)),
ZAKLN_GRITCH(NpcID.ZAKLN_GRITCH, AttackStyle.RANGE, ImmutableSet.of(AnimationID.ZAMMY_GENERIC_AUTO, AnimationID.ZAKL_AUTO)),
KRIL_TSUTSAROTH(NpcID.KRIL_TSUTSAROTH, AttackStyle.UNKNOWN, ImmutableSet.of(AnimationID.KRIL_SPEC, AnimationID.KRIL_AUTO)),
STARLIGHT(NpcID.STARLIGHT, AttackStyle.MELEE, ImmutableSet.of(AnimationID.STARLIGHT_AUTO)),
GROWLER(NpcID.GROWLER, AttackStyle.MAGE, ImmutableSet.of(AnimationID.GROWLER_AUTO)),
BREE(NpcID.BREE, AttackStyle.RANGE, ImmutableSet.of(AnimationID.BREE_AUTO)),
COMMANDER_ZILYANA(NpcID.COMMANDER_ZILYANA, AttackStyle.UNKNOWN, ImmutableSet.of(AnimationID.ZILYANA_AUTO, AnimationID.ZILYANA_MELEE_AUTO, AnimationID.ZILYANA_SPEC)),
FLIGHT_KILISA(NpcID.FLIGHT_KILISA, AttackStyle.MELEE, ImmutableSet.of(AnimationID.KILISA_AUTO)),
FLOCKLEADER_GEERIN(NpcID.FLOCKLEADER_GEERIN, AttackStyle.MAGE, ImmutableSet.of(AnimationID.GEERIN_AUTO, AnimationID.GEERIN_FLINCH)),
WINGMAN_SKREE(NpcID.WINGMAN_SKREE, AttackStyle.RANGE, ImmutableSet.of(AnimationID.SKREE_AUTO)),
KREEARRA(NpcID.KREEARRA, AttackStyle.RANGE, ImmutableSet.of(AnimationID.KREE_RANGED)),
DAGANNOTH_REX(NpcID.DAGANNOTH_REX, AttackStyle.MAGE, ImmutableSet.of(AnimationID.DAG_REX)),
DAGANNOTH_SUPREME(NpcID.DAGANNOTH_SUPREME, AttackStyle.RANGE, ImmutableSet.of(AnimationID.DAG_SUPREME)),
DAGANNOTH_PRIME(NpcID.DAGANNOTH_PRIME, AttackStyle.MAGE, ImmutableSet.of(AnimationID.DAG_PRIME));
private static ImmutableMap<Integer, BossMonsters> idMap;
static
{
ImmutableMap.Builder<Integer, BossMonsters> builder = ImmutableMap.builder();
for (BossMonsters monster : values())
{
builder.put(monster.npcID, monster);
}
idMap = builder.build();
}
private final int npcID;
private final AttackStyle attackStyle;
private final ImmutableSet<Integer> animations;
static BossMonsters of(int npcID)
{
return idMap.get(npcID);
}
}
@AllArgsConstructor
@Getter
public enum Attackstyle
public enum AttackStyle
{
MAGE("Mage", Color.CYAN),
RANGE("Range", Color.GREEN),
MELEE("Melee", Color.RED),
UNKNOWN("Unknown", Color.WHITE);
MAGE("Mage", Color.CYAN, Prayer.PROTECT_FROM_MAGIC),
RANGE("Range", Color.GREEN, Prayer.PROTECT_FROM_MISSILES),
MELEE("Melee", Color.RED, Prayer.PROTECT_FROM_MELEE),
UNKNOWN("Unknown", Color.WHITE, null);
private String name = "";
private String name;
private Color color;
private Prayer prayer;
}
}

View File

@@ -23,6 +23,9 @@
*/
package net.runelite.client.plugins.ticktimers;
import java.awt.Font;
import lombok.AllArgsConstructor;
import lombok.Getter;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@@ -34,6 +37,54 @@ public interface TickTimersConfig extends Config
{
@ConfigItem(
position = 0,
keyName = "mainConfig",
name = "Main Config",
description = ""
)
default Stub mainConfig()
{
return new Stub();
}
@ConfigItem(
position = 1,
keyName = "prayerWidgetHelper",
name = "Prayer Widget Helper",
description = "Shows you which prayer to click and the time until click.",
parent = "mainConfig"
)
default boolean showPrayerWidgetHelper()
{
return false;
}
@ConfigItem(
position = 2,
keyName = "showHitSquares",
name = "Show Hit Squares",
description = "Shows you where the melee bosses can hit you from.",
parent = "mainConfig"
)
default boolean showHitSquares()
{
return false;
}
@ConfigItem(
position = 3,
keyName = "changeTickColor",
name = "Change Tick Color",
description = "If this is enabled, it will change the tick color to white" +
"<br> at 1 tick remaining, signaling you to swap.",
parent = "mainConfig"
)
default boolean changeTickColor()
{
return false;
}
@ConfigItem(
position = 4,
keyName = "bosses",
name = "Bosses",
description = ""
@@ -44,19 +95,31 @@ public interface TickTimersConfig extends Config
}
@ConfigItem(
position = 1,
keyName = "graardor",
name = "General Graardor",
description = "Show tick timers for General Graardor",
position = 5,
keyName = "gwd",
name = "God Wars Dungeon",
description = "Show tick timers for GWD Bosses. This must be enabled before you zone in.",
parent = "bosses"
)
default boolean graardor()
default boolean gwd()
{
return true;
}
@ConfigItem(
position = 22,
position = 6,
keyName = "dks",
name = "Dagannoth Kings",
description = "Show tick timers for Dagannoth Kings. This must be enabled before you zone in.",
parent = "bosses"
)
default boolean dks()
{
return true;
}
@ConfigItem(
position = 7,
keyName = "text",
name = "Text",
description = ""
@@ -67,7 +130,7 @@ public interface TickTimersConfig extends Config
}
@ConfigItem(
position = 23,
position = 8,
keyName = "fontStyle",
name = "Font Style",
description = "Plain | Bold | Italics",
@@ -75,7 +138,7 @@ public interface TickTimersConfig extends Config
)
default FontStyle fontStyle()
{
return FontStyle.PLAIN;
return FontStyle.BOLD;
}
@Range(
@@ -83,7 +146,7 @@ public interface TickTimersConfig extends Config
max = 40
)
@ConfigItem(
position = 24,
position = 9,
keyName = "textSize",
name = "Text Size",
description = "Text Size for Timers.",
@@ -95,7 +158,7 @@ public interface TickTimersConfig extends Config
}
@ConfigItem(
position = 25,
position = 10,
keyName = "shadows",
name = "Shadows",
description = "Adds Shadows to text.",
@@ -105,4 +168,22 @@ public interface TickTimersConfig extends Config
{
return false;
}
@Getter
@AllArgsConstructor
enum FontStyle
{
BOLD("Bold", Font.BOLD),
ITALIC("Italic", Font.ITALIC),
PLAIN("Plain", Font.PLAIN);
private String name;
private int font;
@Override
public String toString()
{
return getName();
}
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright (c) 2019, ganom <https://github.com/Ganom>
* Copyright (c) 2019, Ganom <https://github.com/Ganom>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -24,13 +24,13 @@
package net.runelite.client.plugins.ticktimers;
import com.google.inject.Provides;
import java.util.HashMap;
import java.util.Map;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.inject.Inject;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.NPC;
@@ -41,11 +41,11 @@ import net.runelite.api.events.NpcDespawned;
import net.runelite.api.events.NpcSpawned;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.game.NPCManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.overlay.OverlayManager;
import org.apache.commons.lang3.ArrayUtils;
@PluginDescriptor(
name = "Boss Tick Timers",
@@ -59,21 +59,24 @@ import org.apache.commons.lang3.ArrayUtils;
public class TickTimersPlugin extends Plugin
{
private static final int GENERAL_REGION = 11347;
private static final int ARMA_REGION = 11346;
private static final int SARA_REGION = 11601;
private static final int ZAMMY_REGION = 11603;
private static final int WATERBITH_REGION = 11589;
@Inject
private Client client;
@Inject
private OverlayManager overlayManager;
@Inject
private TimersOverlay timersOverlay;
@Inject
private TickTimersConfig config;
@Inject
private NPCManager npcManager;
@Getter(AccessLevel.PACKAGE)
private Map<NPC, NPCContainer> npcContainer = new HashMap<>();
private Set<NPCContainer> npcContainer = new HashSet<>();
private boolean validRegion;
@Provides
TickTimersConfig getConfig(ConfigManager configManager)
@@ -91,41 +94,71 @@ public class TickTimersPlugin extends Plugin
public void shutDown()
{
npcContainer.clear();
overlayManager.remove(timersOverlay);
validRegion = false;
}
@Subscribe
public void onGameStateChanged(GameStateChanged gameStateChanged)
{
if (gameStateChanged.getGameState() == GameState.LOGGED_IN)
{
if (isInGeneralRegion())
{
overlayManager.add(timersOverlay);
}
else
{
overlayManager.remove(timersOverlay);
}
}
if (gameStateChanged.getGameState() != GameState.LOGGED_IN)
{
return;
}
if (regionCheck())
{
validRegion = true;
overlayManager.add(timersOverlay);
}
else
{
validRegion = false;
overlayManager.remove(timersOverlay);
}
npcContainer.clear();
}
@Subscribe
public void onNpcSpawned(NpcSpawned event)
{
if (!validRegion)
{
return;
}
NPC npc = event.getNpc();
switch (npc.getId())
{
case NpcID.SERGEANT_STRONGSTACK:
case NpcID.SERGEANT_STEELWILL:
case NpcID.SERGEANT_GRIMSPIKE:
case NpcID.GENERAL_GRAARDOR:
case NpcID.GENERAL_GRAARDOR_6494:
npcContainer.put(npc, new NPCContainer(npc));
case NpcID.TSTANON_KARLAK:
case NpcID.BALFRUG_KREEYATH:
case NpcID.ZAKLN_GRITCH:
case NpcID.KRIL_TSUTSAROTH:
case NpcID.STARLIGHT:
case NpcID.BREE:
case NpcID.GROWLER:
case NpcID.COMMANDER_ZILYANA:
case NpcID.FLIGHT_KILISA:
case NpcID.FLOCKLEADER_GEERIN:
case NpcID.WINGMAN_SKREE:
case NpcID.KREEARRA:
if (config.gwd())
{
npcContainer.add(new NPCContainer(npc, npcManager.getAttackSpeed(npc.getId())));
}
break;
case NpcID.DAGANNOTH_REX:
case NpcID.DAGANNOTH_SUPREME:
case NpcID.DAGANNOTH_PRIME:
if (config.dks())
{
npcContainer.add(new NPCContainer(npc, npcManager.getAttackSpeed(npc.getId())));
}
break;
}
}
@@ -133,92 +166,76 @@ public class TickTimersPlugin extends Plugin
@Subscribe
public void onNpcDespawned(NpcDespawned event)
{
if (npcContainer.remove(event.getNpc()) != null && !npcContainer.isEmpty())
if (!validRegion)
{
npcContainer.remove(event.getNpc());
return;
}
NPC npc = event.getNpc();
switch (npc.getId())
{
case NpcID.SERGEANT_STRONGSTACK:
case NpcID.SERGEANT_STEELWILL:
case NpcID.SERGEANT_GRIMSPIKE:
case NpcID.GENERAL_GRAARDOR:
case NpcID.TSTANON_KARLAK:
case NpcID.BALFRUG_KREEYATH:
case NpcID.ZAKLN_GRITCH:
case NpcID.KRIL_TSUTSAROTH:
case NpcID.STARLIGHT:
case NpcID.BREE:
case NpcID.GROWLER:
case NpcID.COMMANDER_ZILYANA:
case NpcID.FLIGHT_KILISA:
case NpcID.FLOCKLEADER_GEERIN:
case NpcID.WINGMAN_SKREE:
case NpcID.KREEARRA:
case NpcID.DAGANNOTH_REX:
case NpcID.DAGANNOTH_SUPREME:
case NpcID.DAGANNOTH_PRIME:
npcContainer.removeIf(c -> c.getNpc() == npc);
break;
}
}
@Subscribe
public void onGameTick(GameTick Event)
{
if (config.graardor())
if (!validRegion)
{
graardorHandler();
return;
}
handleBosses();
}
private void graardorHandler()
private void handleBosses()
{
for (NPCContainer npcs : getNpcContainer().values())
for (NPCContainer npcs : getNpcContainer())
{
switch (npcs.getNpc().getId())
if (npcs.getTicksUntilAttack() >= 0)
{
case NpcID.SERGEANT_STRONGSTACK:
npcs.setTicksUntilAttack(npcs.getTicksUntilAttack() - 1);
npcs.setAttackStyle(NPCContainer.Attackstyle.MELEE);
switch (npcs.getNpc().getAnimation())
npcs.setTicksUntilAttack(npcs.getTicksUntilAttack() - 1);
}
for (int anims : npcs.getAnimations())
{
if (anims == npcs.getNpc().getAnimation())
{
if (npcs.getTicksUntilAttack() < 1)
{
case AnimationID.MINION_AUTO1:
case AnimationID.MINION_AUTO2:
if (npcs.getTicksUntilAttack() < 1)
{
npcs.setTicksUntilAttack(5);
}
break;
npcs.setTicksUntilAttack(npcs.getAttackSpeed());
}
break;
case NpcID.SERGEANT_STEELWILL:
npcs.setTicksUntilAttack(npcs.getTicksUntilAttack() - 1);
npcs.setAttackStyle(NPCContainer.Attackstyle.MAGE);
switch (npcs.getNpc().getAnimation())
{
case AnimationID.MINION_AUTO1:
case AnimationID.MINION_AUTO2:
case AnimationID.MINION_AUTO3:
if (npcs.getTicksUntilAttack() < 1)
{
npcs.setTicksUntilAttack(5);
}
break;
}
case NpcID.SERGEANT_GRIMSPIKE:
npcs.setTicksUntilAttack(npcs.getTicksUntilAttack() - 1);
npcs.setAttackStyle(NPCContainer.Attackstyle.RANGE);
switch (npcs.getNpc().getAnimation())
{
case AnimationID.MINION_AUTO1:
case AnimationID.MINION_AUTO2:
case AnimationID.MINION_AUTO4:
if (npcs.getTicksUntilAttack() < 1)
{
npcs.setTicksUntilAttack(5);
}
break;
}
break;
case NpcID.GENERAL_GRAARDOR:
case NpcID.GENERAL_GRAARDOR_6494:
npcs.setTicksUntilAttack(npcs.getTicksUntilAttack() - 1);
npcs.setAttackStyle(NPCContainer.Attackstyle.MELEE);
switch (npcs.getNpc().getAnimation())
{
case AnimationID.GENERAL_AUTO1:
case AnimationID.GENERAL_AUTO2:
case AnimationID.GENERAL_AUTO3:
if (npcs.getTicksUntilAttack() < 1)
{
npcs.setTicksUntilAttack(6);
}
break;
}
break;
}
}
}
}
private boolean isInGeneralRegion()
private boolean regionCheck()
{
return ArrayUtils.contains(client.getMapRegions(), GENERAL_REGION);
return Arrays.stream(client.getMapRegions()).anyMatch(
x -> x == ARMA_REGION || x == GENERAL_REGION || x == ZAMMY_REGION || x == SARA_REGION || x == WATERBITH_REGION
);
}
}
}

View File

@@ -24,19 +24,17 @@
*/
package net.runelite.client.plugins.ticktimers;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.util.List;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.NPC;
import net.runelite.api.NPCDefinition;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldArea;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
@@ -63,67 +61,91 @@ public class TimersOverlay extends Overlay
@Override
public Dimension render(Graphics2D graphics)
{
Color tickcolor;
for (NPCContainer npcs : plugin.getNpcContainer().values())
for (NPCContainer npc : plugin.getNpcContainer())
{
renderNpcOverlay(graphics, npcs.getNpc(), npcs.getAttackStyle().getColor(), 100, 10);
final int ticksLeft = npcs.getTicksUntilAttack();
if (ticksLeft > 0)
if (npc.getNpc() == null)
{
if (ticksLeft == 1)
continue;
}
int ticksLeft = npc.getTicksUntilAttack();
final List<WorldPoint> hitSquares = getHitSquares(npc.getNpc().getWorldLocation(), npc.getNpcSize(), 1, false);
final NPCContainer.AttackStyle attackStyle = npc.getAttackStyle();
if (config.showHitSquares() && attackStyle.getName().equals("Melee"))
{
for (WorldPoint p : hitSquares)
{
tickcolor = npcs.getAttackStyle().getColor();
OverlayUtil.drawTile(graphics, client, p, client.getLocalPlayer().getWorldLocation(), attackStyle.getColor(), 0, 0, 50);
}
else
}
if (ticksLeft <= 0)
{
continue;
}
final String ticksLeftStr = String.valueOf(ticksLeft);
final int font = config.fontStyle().getFont();
final boolean shadows = config.shadows();
Color color = (ticksLeft <= 1 ? Color.WHITE : attackStyle.getColor());
if (!config.changeTickColor())
{
color = attackStyle.getColor();
}
final Point canvasPoint = npc.getNpc().getCanvasTextLocation(graphics, Integer.toString(ticksLeft), 0);
OverlayUtil.renderTextLocation(graphics, ticksLeftStr, config.textSize(), font, color, canvasPoint, shadows, 0);
if (config.showPrayerWidgetHelper() && attackStyle.getPrayer() != null)
{
Rectangle bounds = OverlayUtil.renderPrayerOverlay(graphics, client, attackStyle.getPrayer(), color);
if (bounds != null)
{
tickcolor = Color.WHITE;
renderTextLocation(graphics, ticksLeftStr, 16, config.fontStyle().getFont(), color, centerPoint(bounds), shadows);
}
final String ticksLeftStr = String.valueOf(ticksLeft);
Point canvasPoint = npcs.getNpc().getCanvasTextLocation(graphics, ticksLeftStr, 0);
renderTextLocation(graphics, ticksLeftStr, config.textSize(), config.fontStyle().getFont(), tickcolor, canvasPoint);
}
}
return null;
}
private void renderNpcOverlay(Graphics2D graphics, NPC actor, Color color, int outlineAlpha, int fillAlpha)
{
int size = 1;
NPCDefinition composition = actor.getTransformedDefinition();
if (composition != null)
{
size = composition.getSize();
}
LocalPoint lp = actor.getLocalLocation();
Polygon tilePoly = Perspective.getCanvasTileAreaPoly(client, lp, size);
if (tilePoly != null)
{
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), outlineAlpha));
graphics.setStroke(new BasicStroke(2));
graphics.draw(tilePoly);
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), fillAlpha));
graphics.fill(tilePoly);
}
}
private void renderTextLocation(Graphics2D graphics, String txtString, int fontSize, int fontStyle, Color fontColor, Point canvasPoint)
private void renderTextLocation(Graphics2D graphics, String txtString, int fontSize, int fontStyle, Color fontColor, Point canvasPoint, boolean shadows)
{
graphics.setFont(new Font("Arial", fontStyle, fontSize));
if (canvasPoint != null)
{
final Point canvasCenterPoint = new Point(
canvasPoint.getX(),
canvasPoint.getY());
canvasPoint.getX() - 3,
canvasPoint.getY() + 6);
final Point canvasCenterPoint_shadow = new Point(
canvasPoint.getX() + 1,
canvasPoint.getY() + 1);
if (config.shadows())
canvasPoint.getX() - 2,
canvasPoint.getY() + 7);
if (shadows)
{
OverlayUtil.renderTextLocation(graphics, canvasCenterPoint_shadow, txtString, Color.BLACK);
}
OverlayUtil.renderTextLocation(graphics, canvasCenterPoint, txtString, fontColor);
}
}
private List<WorldPoint> getHitSquares(WorldPoint npcLoc, int npcSize, int thickness, boolean includeUnder)
{
List<WorldPoint> little = new WorldArea(npcLoc, npcSize, npcSize).toWorldPointList();
List<WorldPoint> big = new WorldArea(npcLoc.getX() - thickness, npcLoc.getY() - thickness, npcSize + (thickness * 2), npcSize + (thickness * 2), npcLoc.getPlane()).toWorldPointList();
if (!includeUnder)
{
big.removeIf(little::contains);
}
return big;
}
private Point centerPoint(Rectangle rect)
{
int x = (int) (rect.getX() + rect.getWidth() / 2);
int y = (int) (rect.getY() + rect.getHeight() / 2);
return new Point(x, y);
}
}

View File

@@ -126,9 +126,9 @@ class XpState
* @param npc currently interacted NPC
* @param npcHealth health of currently interacted NPC
*/
void updateNpcExperience(Skill skill, NPC npc, Integer npcHealth)
void updateNpcExperience(Skill skill, NPC npc, int npcHealth)
{
if (npc == null || npc.getCombatLevel() <= 0 || npcHealth == null)
if (npc == null || npc.getCombatLevel() <= 0 || npcHealth == -1)
{
return;
}
@@ -170,11 +170,11 @@ class XpState
* @param npcHealth max health of npc that just died
* @return UPDATED in case new kill was successfully added
*/
XpUpdateResult updateNpcKills(Skill skill, NPC npc, Integer npcHealth)
XpUpdateResult updateNpcKills(Skill skill, NPC npc, int npcHealth)
{
XpStateSingle state = getSkill(skill);
if (state.getXpGained() <= 0 || npcHealth == null || npc != interactedNPC)
if (state.getXpGained() <= 0 || npcHealth == -1 || npc != interactedNPC)
{
return XpUpdateResult.NO_CHANGE;
}

View File

@@ -28,8 +28,10 @@ import com.google.common.base.Strings;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.geom.Area;
@@ -38,8 +40,11 @@ import net.runelite.api.Actor;
import net.runelite.api.Client;
import net.runelite.api.Perspective;
import net.runelite.api.Point;
import net.runelite.api.Prayer;
import net.runelite.api.TileObject;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.api.widgets.Widget;
/**
@@ -259,8 +264,7 @@ public class OverlayUtil
return result;
}
public static void renderActorTextAndImage(Graphics2D graphics, Actor actor, String text, Color color,
BufferedImage image, int yOffset, int xOffset)
public static void renderActorTextAndImage(Graphics2D graphics, Actor actor, String text, Color color, BufferedImage image, int yOffset, int xOffset)
{
Point textLocation = new Point(actor.getConvexHull().getBounds().x + xOffset,
actor.getConvexHull().getBounds().y + yOffset);
@@ -271,4 +275,69 @@ public class OverlayUtil
textLocation = new Point(textLocation.getX() + xOffset, textLocation.getY() + image.getHeight() - yOffset);
renderTextLocation(graphics, textLocation, text, color);
}
public static void renderTextLocation(Graphics2D graphics, String txtString, int fontSize, int fontStyle, Color fontColor, Point canvasPoint, boolean shadows, int yOffset)
{
graphics.setFont(new Font("Arial", fontStyle, fontSize));
if (canvasPoint != null)
{
final Point canvasCenterPoint = new Point(
canvasPoint.getX(),
canvasPoint.getY() + yOffset);
final Point canvasCenterPoint_shadow = new Point(
canvasPoint.getX() + 1,
canvasPoint.getY() + 1);
if (shadows)
{
renderTextLocation(graphics, canvasCenterPoint_shadow, txtString, Color.BLACK);
}
renderTextLocation(graphics, canvasCenterPoint, txtString, fontColor);
}
}
public static void drawTile(Graphics2D graphics, Client client, WorldPoint point, WorldPoint playerPoint, Color color, int strokeWidth, int outlineAlpha, int fillAlpha)
{
if (point.distanceTo(playerPoint) >= 32)
{
return;
}
LocalPoint lp = LocalPoint.fromWorld(client, point);
if (lp == null)
{
return;
}
Polygon poly = Perspective.getCanvasTilePoly(client, lp);
if (poly == null)
{
return;
}
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), outlineAlpha));
graphics.setStroke(new BasicStroke(strokeWidth));
graphics.draw(poly);
graphics.setColor(new Color(color.getRed(), color.getGreen(), color.getBlue(), fillAlpha));
graphics.fill(poly);
}
public static Rectangle renderPrayerOverlay(Graphics2D graphics, Client client, Prayer prayer, Color color)
{
Widget widget = client.getWidget(prayer.getWidgetInfo());
if (widget == null || widget.isHidden())
{
return null;
}
Rectangle bounds = widget.getBounds();
renderPolygon(graphics, rectangleToPolygon(bounds), color);
return bounds;
}
private static Polygon rectangleToPolygon(Rectangle rect)
{
int[] xpoints = {rect.x, rect.x + rect.width, rect.x + rect.width, rect.x};
int[] ypoints = {rect.y, rect.y, rect.y + rect.height, rect.y + rect.height};
return new Polygon(xpoints, ypoints, 4);
}
}