Add hunter plugin

This commit is contained in:
Onvo
2017-12-23 19:07:04 -05:00
committed by Adam
parent 93b950c829
commit e4ea7d15e4
6 changed files with 851 additions and 0 deletions

View File

@@ -43,6 +43,12 @@ public final class AnimationID
public static final int COOKING_FIRE = 897;
public static final int COOKING_RANGE = 896;
public static final int FLETCHING_BOW_CUTTING = 1248;
public static final int HUNTER_LAY_BOXTRAP_BIRDSNARE = 5208; //same for laying bird snares and box traps
public static final int HUNTER_LAY_DEADFALLTRAP = 5212; //setting up deadfall trap
public static final int HUNTER_LAY_NETTRAP = 5215; //setting up net trap
public static final int HUNTER_CHECK_BIRD_SNARE = 5207;
public static final int HUNTER_CHECK_BOX_TRAP = 5212;
public static final int HERBLORE_MAKE_TAR = 5249;
public static final int FLETCHING_STRING_NORMAL_SHORTBOW = 6678;
public static final int FLETCHING_STRING_NORMAL_LONGBOW = 6684;
public static final int FLETCHING_STRING_OAK_SHORTBOW = 6679;

View File

@@ -0,0 +1,76 @@
/*
* Copyright (c) 2017, Robin Weymans <Robin.weymans@gmail.com>
* 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.hunter;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.time.Duration;
import java.time.Instant;
import javax.inject.Inject;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.PanelComponent;
/**
* Represents the overlay that shows the catch rate (percentage).
*/
public class CatchrateOverlay extends Overlay
{
/**
* The time after which the catch rate panel disappears, if the player
* stops hunting.
*/
private final Duration catchRatePanelTimeOut = Duration.ofSeconds(30);
private final PanelComponent catchRatePanel = new PanelComponent();
private final HunterPlugin plugin;
private final HunterConfig config;
@Inject
CatchrateOverlay(HunterPlugin plugin, HunterConfig config)
{
setPosition(OverlayPosition.BOTTOM_RIGHT);
this.plugin = plugin;
this.config = config;
}
@Override
public Dimension render(Graphics2D graphics, Point parent)
{
if (config.enabled())
{
if (Duration.between(plugin.getLastActionTime(), Instant.now()).compareTo(catchRatePanelTimeOut) < 0)
{
final String attackStyleString = String.format("%.2f", plugin.getCatchRate() * 100) + " %";
catchRatePanel.setTitle(attackStyleString);
catchRatePanel.setWidth(80);
return catchRatePanel.render(graphics, parent);
}
}
return null;
}
}

View File

@@ -0,0 +1,93 @@
/*
* Copyright (c) 2017, Robin Weymans <Robin.weymans@gmail.com>
* 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.hunter;
import java.awt.Color;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
@ConfigGroup(
keyName = "hunterplugin",
name = "Hunter plugin",
description = "Configuration for the hunter plugin"
)
public interface HunterConfig extends Config
{
@ConfigItem(
position = 0,
keyName = "enabled",
name = "Enabled",
description = "Configures whether or not the hunter plugin is enabled"
)
default boolean enabled()
{
return true;
}
@ConfigItem(
position = 1,
keyName = "hexColorOpenTrap",
name = "Open trap",
description = "Color of open trap timer"
)
default Color getOpenTrapColor()
{
return Color.YELLOW;
}
@ConfigItem(
position = 2,
keyName = "hexColorFullTrap",
name = "Full trap",
description = "Color of full trap timer"
)
default Color getFullTrapColor()
{
return Color.GREEN;
}
@ConfigItem(
position = 3,
keyName = "hexColorEmptyTrap",
name = "Empty trap",
description = "Color of empty trap timer"
)
default Color getEmptyTrapColor()
{
return Color.RED;
}
@ConfigItem(
position = 4,
keyName = "hexColorTransTrap",
name = "Transitioning trap",
description = "Color of transitioning trap timer"
)
default Color getTransTrapColor()
{
return Color.ORANGE;
}
}

View File

@@ -0,0 +1,348 @@
/*
* Copyright (c) 2017, Robin Weymans <Robin.weymans@gmail.com>
* 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.hunter;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Binder;
import com.google.inject.Provides;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.inject.Inject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.AnimationID;
import net.runelite.api.Client;
import net.runelite.api.GameObject;
import net.runelite.api.GameState;
import net.runelite.api.ObjectID;
import net.runelite.api.Player;
import net.runelite.api.Point;
import net.runelite.api.queries.GameObjectQuery;
import net.runelite.api.queries.PlayerQuery;
import net.runelite.client.RuneLite;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.events.ConfigChanged;
import net.runelite.client.events.GameObjectsChanged;
import net.runelite.client.events.GameStateChanged;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.task.Schedule;
import net.runelite.client.ui.overlay.Overlay;
@Slf4j
@PluginDescriptor(
name = "Hunter plugin"
)
public class HunterPlugin extends Plugin
{
@Inject
private Client client;
@Inject
private RuneLite runelite;
@Inject
private HunterConfig config;
@Inject
private TrapOverlay trapOverlay;
@Inject
private CatchrateOverlay catchrateOverlay;
@Getter
private final Set<HunterTrap> traps = new HashSet<>();
private double catchAtempts = 0;
private double catchSuccess = 0;
@Getter
private Instant lastActionTime = Instant.ofEpochMilli(0);
@Override
public void configure(Binder binder)
{
binder.bind(TrapOverlay.class);
binder.bind(CatchrateOverlay.class);
}
@Provides
HunterConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(HunterConfig.class);
}
@Override
public Collection<Overlay> getOverlays()
{
return Arrays.asList(trapOverlay, catchrateOverlay);
}
@Subscribe
public void onGameObjectsChanged(GameObjectsChanged event)
{
if (!config.enabled())
{
return;
}
GameObject gameObject = event.getGameObject();
HunterTrap myTrap = getTrapFromCollection(gameObject);
Player localPlayer = client.getLocalPlayer();
switch (gameObject.getId())
{
case ObjectID.DEADFALL: //Deadfall trap placed
if (localPlayer.getWorldLocation().distanceTo(gameObject.getWorldLocation()) <= 2
&& localPlayer.getAnimation() == AnimationID.HUNTER_LAY_DEADFALLTRAP)
{
log.debug("Deadfall trap placed by \"{}\" on {}", localPlayer.getName(), gameObject.getWorldLocation());
traps.add(new HunterTrap(gameObject));
lastActionTime = Instant.now();
}
break;
case ObjectID.BOX_TRAP_9380: //Box trap placed
case ObjectID.BIRD_SNARE_9345: //Bird snare placed
case ObjectID.NET_TRAP_9343: //Net trap placed at green sallys
case ObjectID.NET_TRAP: //Net trap placed at orange sallys
case ObjectID.NET_TRAP_8992: //Net trap placed at red sallys
case ObjectID.NET_TRAP_9002: //Net trap placed at black sallys
//Look for players that are on the same tile
PlayerQuery playerQuery = new PlayerQuery().atLocalLocation(gameObject.getLocalLocation());
List<Player> possiblePlayers = Arrays.asList(runelite.runQuery(playerQuery));
/* If the player is on that tile, and it has the correct animation, assume he is the one that placed the trap
* Special case: if you herb+tar, then move and place the trap, it does not detect laying the trap. It does work
* if you herb+tar en then place the trap.
*/
if (possiblePlayers.contains(localPlayer))
{
switch (localPlayer.getAnimation())
{
case AnimationID.HUNTER_LAY_BOXTRAP_BIRDSNARE:
case AnimationID.HUNTER_LAY_NETTRAP:
case AnimationID.HERBLORE_MAKE_TAR: // When 3 ticking
log.debug("Trap placed by \"{}\" on {}", localPlayer.getName(), localPlayer.getWorldLocation());
traps.add(new HunterTrap(gameObject));
lastActionTime = Instant.now();
break;
}
}
break;
case ObjectID.SHAKING_BOX: //Black chinchompa caught
case ObjectID.SHAKING_BOX_9382: // Grey chinchompa caught
case ObjectID.SHAKING_BOX_9383: //Red chinchompa caught
case ObjectID.BOULDER_20648: //Prickly kebbit caught
case ObjectID.BOULDER_20649: //Sabre-tooth kebbit caught
case ObjectID.BOULDER_20650: //Barb-tailed kebbit caught
case ObjectID.BOULDER_20651: //Wild kebbit caught
case ObjectID.BIRD_SNARE_9373: //Crimson swift caught
case ObjectID.BIRD_SNARE_9375: //Cerulean twitch caught
case ObjectID.BIRD_SNARE_9377: //Golden warbler caught
case ObjectID.BIRD_SNARE_9379: //Copper longtail caught
case ObjectID.BIRD_SNARE_9348: //Tropical wagtail caught
case ObjectID.NET_TRAP_9004: //Green sally caught
case ObjectID.NET_TRAP_8986: //Red sally caught
case ObjectID.NET_TRAP_8734: //Orange sally caught
case ObjectID.NET_TRAP_8996: //Black sally caught
if (myTrap != null)
{
log.debug("Yay, you caught something");
myTrap.setState(HunterTrap.State.FULL);
catchAtempts++;
catchSuccess++;
lastActionTime = Instant.now();
}
break;
case ObjectID.BOX_TRAP_9385: //Empty box trap
case ObjectID.BIRD_SNARE: //Empty box trap
if (myTrap != null)
{
log.debug("Your trap didn't catch anything");
myTrap.setState(HunterTrap.State.EMPTY);
myTrap.resetTimer();
catchAtempts++;
lastActionTime = Instant.now();
}
break;
//Black chin shaking box
case ObjectID.BOX_TRAP:
case ObjectID.BOX_TRAP_2026:
case ObjectID.BOX_TRAP_2028:
case ObjectID.BOX_TRAP_2029:
//Red chin shaking box
case ObjectID.BOX_TRAP_9381:
case ObjectID.BOX_TRAP_9390:
case ObjectID.BOX_TRAP_9391:
case ObjectID.BOX_TRAP_9392:
case ObjectID.BOX_TRAP_9393:
//Grey chin shaking box
case ObjectID.BOX_TRAP_9386:
case ObjectID.BOX_TRAP_9387:
case ObjectID.BOX_TRAP_9388:
//Bird traps
case ObjectID.BIRD_SNARE_9346:
case ObjectID.BIRD_SNARE_9347:
case ObjectID.BIRD_SNARE_9349:
case ObjectID.BIRD_SNARE_9374:
case ObjectID.BIRD_SNARE_9376:
case ObjectID.BIRD_SNARE_9378:
//Deadfall trap
case ObjectID.DEADFALL_19218:
case ObjectID.DEADFALL_19851:
case ObjectID.DEADFALL_20128:
case ObjectID.DEADFALL_20129:
case ObjectID.DEADFALL_20130:
case ObjectID.DEADFALL_20131:
//Net trap
case ObjectID.NET_TRAP_9003:
case ObjectID.NET_TRAP_9005:
case ObjectID.NET_TRAP_8972:
case ObjectID.NET_TRAP_8974:
case ObjectID.NET_TRAP_8985:
case ObjectID.NET_TRAP_8987:
case ObjectID.NET_TRAP_8993:
case ObjectID.NET_TRAP_8997:
if (myTrap != null)
{
myTrap.setState(HunterTrap.State.TRANSITION);
}
break;
}
}
@Subscribe
public void onGameStateChange(GameStateChanged event)
{
if (event.getGameState().equals(GameState.LOGIN_SCREEN))
{
trapOverlay.updateConfig();
}
}
@Subscribe
public void onConfigChanged(ConfigChanged event)
{
if (event.getGroup().equals("hunterplugin"))
{
trapOverlay.updateConfig();
}
}
/**
* Iterates over all the traps that were placed by the local player and
* checks if the trap is still there. If the trap is gone, it removes
* the trap from the local players trap collection.
*/
@Schedule(
period = 500,
unit = ChronoUnit.MILLIS
)
public void updateTraps()
{
if (!config.enabled())
{
return;
}
//Check if all traps are still there, and remove the ones that are not.
Iterator<HunterTrap> it = traps.iterator();
while (it.hasNext())
{
HunterTrap trap = it.next();
//Look for gameobjects that are on the same location as the trap
GameObjectQuery goQuery = new GameObjectQuery()
.atWorldLocation(trap.getGameObject().getWorldLocation());
//This is for placeable traps like box traps. There are no gameobjects on that location if the trap collapsed
if (runelite.runQuery(goQuery).length == 0)
{
it.remove();
log.debug("Trap removed from personal trap collection, {} left", traps.size());
}
else //For traps like deadfalls. This is different because when the trap is gone, there is still a GameObject (boulder)
{
goQuery = goQuery
.idEquals(ObjectID.BOULDER_19215); //Deadfalls are the only ones (that i can test) that have this behaviour. I think maniacal monkeys have this too.
if (runelite.runQuery(goQuery).length != 0)
{
it.remove();
log.debug("Special trap removed from personal trap collection, {} left", traps.size());
}
}
}
}
/**
* Looks for a trap in the local players trap collection, on the same
* place as the given GameObject.
*
* @param gameObject
* @return A HunterTrap object if the player has a trap on the same
* location as the GameObject. Otherwise it returns null.
*/
private HunterTrap getTrapFromCollection(GameObject gameObject)
{
Point gameObjectLocation = gameObject.getWorldLocation();
for (HunterTrap trap : traps)
{
if (gameObjectLocation.equals(trap.getGameObject().getWorldLocation()))
{
return trap;
}
}
return null;
}
/**
* Calculates the catch rate, i.e. the attempts to catch something
* compared to the times you succeed.
*
* @return Value between 0 (none) and 1 (all).
*/
public double getCatchRate()
{
return catchAtempts != 0 ? catchSuccess / catchAtempts : 0;
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright (c) 2017, Robin Weymans <Robin.weymans@gmail.com>
* 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.hunter;
import java.time.Duration;
import java.time.Instant;
import java.util.Objects;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.GameObject;
/**
* Wrapper class for a GameObject that represents a hunter trap.
*/
class HunterTrap
{
/**
* A hunter trap stays up 1 minute before collapsing.
*/
private static final Duration TRAP_TIME = Duration.ofMinutes(1);
/**
* The time in milliseconds when the trap was placed.
*/
@Getter
private Instant placedOn;
/**
* The state of the trap.
*/
@Getter
@Setter
private State state;
/**
* The gameobject thats corresponds with this trap.
*/
@Getter
private final GameObject gameObject;
/**
* The states a trap can be in.
*/
enum State
{
/**
* A laid out trap.
*/
OPEN,
/**
* A trap that is empty.
*/
EMPTY,
/**
* A trap that caught something.
*/
FULL,
/**
* A trap that is closing.
*/
TRANSITION
}
/**
* Constructor for a HunterTrap object
*
* @param gameObject The gameobject thats corresponds with this trap.
*/
HunterTrap(GameObject gameObject)
{
this.state = State.OPEN;
this.gameObject = gameObject;
this.placedOn = Instant.now();
}
/**
* Calculates how much time is left before the trap is collapsing.
*
* @return Value between 0 and 1. 0 means the trap was laid moments ago.
* 1 is a trap that's about to collapse.
*/
public double getTrapTimeRelative()
{
Duration duration = Duration.between(placedOn, Instant.now());
return duration.compareTo(TRAP_TIME) < 0 ? (double) duration.toMillis() / TRAP_TIME.toMillis() : 1;
}
/**
* Resets the time value when the trap was placed.
*/
public void resetTimer()
{
placedOn = Instant.now();
}
@Override
public boolean equals(Object o)
{
if (o == this)
{
return true;
}
if (!(o instanceof HunterTrap))
{
return false;
}
HunterTrap t = (HunterTrap) o;
return gameObject.getWorldLocation().equals(t.getGameObject().getWorldLocation());
}
@Override
public int hashCode()
{
int hash = 7;
hash = 97 * hash + Objects.hashCode(this.gameObject);
return hash;
}
}

View File

@@ -0,0 +1,188 @@
/*
* Copyright (c) 2017, Robin Weymans <Robin.weymans@gmail.com>
* 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.hunter;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.geom.Arc2D;
import javax.annotation.Nullable;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.widgets.Widget;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
/**
* Represents the overlay that shows timers on traps that are placed by the
* player.
*/
public class TrapOverlay extends Overlay
{
/**
* Size of the trap timer.
*/
private static final int TIMER_SIZE = 25;
/**
* Width of the border around the trap timer.
*/
private static final int TIMER_BORDER_WIDTH = 1;
/**
* The timer is low when only 25% is left.
*/
private static final double TIMER_LOW = 0.25; // When the timer is under a quarter left, if turns red.
private final Client client;
private final HunterPlugin plugin;
private final HunterConfig config;
private Color colorOpen, colorOpenBorder;
private Color colorEmpty, colorEmptyBorder;
private Color colorFull, colorFullBorder;
private Color colorTrans, colorTransBorder;
@Inject
TrapOverlay(@Nullable Client client, HunterPlugin plugin, HunterConfig config)
{
setPosition(OverlayPosition.DYNAMIC);
this.plugin = plugin;
this.config = config;
this.client = client;
}
@Override
public Dimension render(Graphics2D graphics, Point parent)
{
if (config.enabled())
{
drawTraps(graphics);
}
return null;
}
/**
* Updates the timer colors.
*/
public void updateConfig()
{
colorEmptyBorder = config.getEmptyTrapColor();
colorEmpty = new Color(colorEmptyBorder.getRed(), colorEmptyBorder.getGreen(), colorEmptyBorder.getBlue(), 100);
colorFullBorder = config.getFullTrapColor();
colorFull = new Color(colorFullBorder.getRed(), colorFullBorder.getGreen(), colorFullBorder.getBlue(), 100);
colorOpenBorder = config.getOpenTrapColor();
colorOpen = new Color(colorOpenBorder.getRed(), colorOpenBorder.getGreen(), colorOpenBorder.getBlue(), 100);
colorTransBorder = config.getTransTrapColor();
colorTrans = new Color(colorTransBorder.getRed(), colorTransBorder.getGreen(), colorTransBorder.getBlue(), 100);
}
/**
* Iterates over all the traps that were placed by the local player, and
* draws a circle or a timer on the trap, depending on the trap state.
*
* @param graphics
*/
private void drawTraps(Graphics2D graphics)
{
Widget viewport = client.getViewportWidget();
for (HunterTrap trap : plugin.getTraps())
{
if (viewport != null && viewport.contains(trap.getGameObject().getCanvasLocation()))
{
switch (trap.getState())
{
case OPEN:
drawTimerOnTrap(graphics, trap, colorOpen, colorOpenBorder, colorEmpty, colorOpenBorder);
break;
case EMPTY:
drawTimerOnTrap(graphics, trap, colorEmpty, colorEmptyBorder, colorEmpty, colorEmptyBorder);
break;
case FULL:
drawCircleOnTrap(graphics, trap, colorFull, colorFullBorder);
break;
case TRANSITION:
drawCircleOnTrap(graphics, trap, colorTrans, colorTransBorder);
break;
}
}
}
}
/**
* Draws a timer on a given trap.
*
* @param graphics
* @param trap The trap on which the timer needs to be drawn
* @param fill The fill color of the timer
* @param border The border color of the timer
* @param fillTimeLow The fill color of the timer when it is low
* @param borderTimeLow The border color of the timer when it is low
*/
private void drawTimerOnTrap(Graphics2D graphics, HunterTrap trap, Color fill, Color border, Color fillTimeLow, Color borderTimeLow)
{
net.runelite.api.Point loc = trap.getGameObject().getCanvasLocation();
//Construct the arc
Arc2D.Float arc = new Arc2D.Float(Arc2D.PIE);
arc.setAngleStart(90);
double timeLeft = 1 - trap.getTrapTimeRelative();
arc.setAngleExtent(timeLeft * 360);
arc.setFrame(loc.getX() - TIMER_SIZE / 2, loc.getY() - TIMER_SIZE / 2, TIMER_SIZE, TIMER_SIZE);
//Draw the inside of the arc
graphics.setColor(timeLeft > TIMER_LOW ? fill : fillTimeLow);
graphics.fill(arc);
//Draw the outlines of the arc
graphics.setStroke(new BasicStroke(TIMER_BORDER_WIDTH));
graphics.setColor(timeLeft > TIMER_LOW ? border : borderTimeLow);
graphics.drawOval(loc.getX() - TIMER_SIZE / 2, loc.getY() - TIMER_SIZE / 2, TIMER_SIZE, TIMER_SIZE);
}
/**
* Draws a timer on a given trap.
*
* @param graphics
* @param trap The trap on which the timer needs to be drawn
* @param fill The fill color of the timer
* @param border The border color of the timer
*/
private void drawCircleOnTrap(Graphics2D graphics, HunterTrap trap, Color fill, Color border)
{
net.runelite.api.Point loc = trap.getGameObject().getCanvasLocation();
//Draw the inside of the arc
graphics.setColor(fill);
graphics.fillOval(loc.getX() - TIMER_SIZE / 2, loc.getY() - TIMER_SIZE / 2, TIMER_SIZE, TIMER_SIZE);
//Draw the border of the cirlce
graphics.setColor(border);
graphics.setStroke(new BasicStroke(TIMER_BORDER_WIDTH));
graphics.drawOval(loc.getX() - TIMER_SIZE / 2, loc.getY() - TIMER_SIZE / 2, TIMER_SIZE, TIMER_SIZE);
}
}