This commit is contained in:
James Munson
2019-04-24 17:22:33 -07:00
21 changed files with 612 additions and 212 deletions

View File

@@ -0,0 +1,62 @@
package net.runelite.client.plugins.alchemicalhydra;
import javax.inject.Singleton;
import lombok.Getter;
import lombok.Setter;
import net.runelite.api.Prayer;
import net.runelite.api.ProjectileID;
@Singleton
class Hydra
{
enum AttackStyle
{
MAGIC(ProjectileID.HYDRA_MAGIC, Prayer.PROTECT_FROM_MAGIC),
RANGED(ProjectileID.HYDRA_RANGED, Prayer.PROTECT_FROM_MISSILES);
@Getter
private int projId;
@Getter
private Prayer prayer;
AttackStyle(int projId, Prayer prayer)
{
this.projId = projId;
this.prayer = prayer;
}
}
@Getter
@Setter
private HydraPhase phase;
@Getter
@Setter
private int attackCount;
@Getter
@Setter
private int nextSwitch;
@Getter
@Setter
private int nextSpecial;
@Getter
@Setter
private AttackStyle nextAttack;
@Getter
@Setter
private AttackStyle lastAttack;
Hydra()
{
this.phase = HydraPhase.ONE;
this.nextAttack = AttackStyle.MAGIC;
this.nextSpecial = 3;
this.nextSwitch = phase.getAttacksPerSwitch();
this.attackCount = 0;
}
}

View File

@@ -0,0 +1,132 @@
package net.runelite.client.plugins.alchemicalhydra;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import net.runelite.api.Prayer;
import net.runelite.api.SpriteID;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.components.InfoBoxComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
@Singleton
class HydraOverlay extends Overlay
{
private final HydraPlugin plugin;
private final Client client;
private final SpriteManager spriteManager;
private final PanelComponent panelComponent = new PanelComponent();
private static final Color redBgCol = new Color(156, 0, 0, 156);
private static final Color yelBgCol = new Color(200, 156, 0, 156);
private static final Color grnBgCol = new Color(0, 156, 0, 156);
@Inject
HydraOverlay(HydraPlugin plugin, Client client, SpriteManager spriteManager)
{
this.plugin = plugin;
this.client = client;
this.spriteManager = spriteManager;
setPosition(OverlayPosition.BOTTOM_RIGHT);
panelComponent.setOrientation(PanelComponent.Orientation.VERTICAL);
}
@Override
public Dimension render(Graphics2D graphics2D)
{
Hydra hydra = plugin.getHydra();
panelComponent.getChildren().clear();
if (hydra == null || client == null)
{
return null;
}
//Add spec overlay first, to keep it above pray
HydraPhase phase = hydra.getPhase();
int attackCount = hydra.getAttackCount();
int nextSpec = hydra.getNextSpecial() - attackCount;
if (nextSpec <= 3)
{
InfoBoxComponent specComponent = new InfoBoxComponent();
if (nextSpec == 0)
{
specComponent.setBackgroundColor(redBgCol);
}
else if (nextSpec == 1)
{
specComponent.setBackgroundColor(yelBgCol);
}
Image specImg = scaleImg(spriteManager.getSprite(phase.getSpecImage(), 0));
specComponent.setImage(specImg);
specComponent.setText(" " + (nextSpec)); //hacky way to not have to figure out how to move text
specComponent.setPreferredSize(new Dimension(40, 40));
panelComponent.getChildren().add(specComponent);
}
Prayer nextPrayer = hydra.getNextAttack().getPrayer();
Image prayImg = scaleImg(getPrayerImage(hydra.getNextAttack().getPrayer()));
int nextSwitch = hydra.getNextSwitch();
InfoBoxComponent prayComponent = new InfoBoxComponent();
if (nextSwitch == 1)
{
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? yelBgCol : redBgCol);
}
else
{
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? grnBgCol : redBgCol);
}
prayComponent.setImage(prayImg);
prayComponent.setText(" " + nextSwitch);
prayComponent.setColor(Color.white);
prayComponent.setPreferredSize(new Dimension(40, 40));
panelComponent.getChildren().add(prayComponent);
panelComponent.setPreferredSize(new Dimension(40, 0));
panelComponent.setBorder(new Rectangle(0, 0, 0, 0));
return panelComponent.render(graphics2D);
}
private BufferedImage getPrayerImage(Prayer pray)
{
return pray == Prayer.PROTECT_FROM_MAGIC
? spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MAGIC, 0)
: spriteManager.getSprite(SpriteID.PRAYER_PROTECT_FROM_MISSILES, 0);
}
private Image scaleImg(final Image img)
{
if (img == null)
{
return null;
}
final double width = img.getWidth(null);
final double height = img.getHeight(null);
final double size = 36; // Limit size to 2 as that is minimum size not causing breakage
final double scalex = size / width;
final double scaley = size / height;
final double scale = Math.min(scalex, scaley);
final int newWidth = (int) (width * scale);
final int newHeight = (int) (height * scale);
final BufferedImage scaledImage = new BufferedImage(newWidth, newHeight, BufferedImage.TYPE_INT_ARGB);
final Graphics g = scaledImage.createGraphics();
g.drawImage(img, 0, 0, newWidth, newHeight, null);
g.dispose();
return scaledImage;
}
}

View File

@@ -0,0 +1,42 @@
package net.runelite.client.plugins.alchemicalhydra;
import lombok.Getter;
import net.runelite.api.AnimationID;
import net.runelite.api.ProjectileID;
import net.runelite.api.SpriteID;
enum HydraPhase
{
ONE(3, AnimationID.HYDRA_1_1, AnimationID.HYDRA_1_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL),
TWO(3, AnimationID.HYDRA_2_1, AnimationID.HYDRA_2_2, 0, AnimationID.HYDRA_LIGHTNING, SpriteID.BIG_SPEC_TRANSFER),
THREE(3, AnimationID.HYDRA_3_1, AnimationID.HYDRA_3_2, 0, AnimationID.HYDRA_FIRE, SpriteID.BIG_SUPERHEAT),
FOUR(1, AnimationID.HYDRA_4_1, AnimationID.HYDRA_4_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL);
@Getter
private int attacksPerSwitch;
@Getter
private int deathAnim1;
@Getter
private int deathAnim2;
@Getter
private int specProjectileId;
@Getter
private int specAnimationId;
@Getter
private int specImage;
HydraPhase(int attacksPerSwitch, int deathAnim1, int deathAnim2, int specProjectileId, int specAnimationId, int specImage)
{
this.attacksPerSwitch = attacksPerSwitch;
this.deathAnim1 = deathAnim1;
this.deathAnim2 = deathAnim2;
this.specProjectileId = specProjectileId;
this.specAnimationId = specAnimationId;
this.specImage = specImage;
}
}

View File

@@ -0,0 +1,248 @@
package net.runelite.client.plugins.alchemicalhydra;
import java.util.Arrays;
import java.util.HashSet;
import javax.inject.Inject;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Actor;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.NpcID;
import net.runelite.api.Projectile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.ProjectileMoved;
import net.runelite.client.eventbus.Subscribe;
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;
@PluginDescriptor(
name = "Alchemical Hydra",
description = "Show what to pray against hydra",
tags = {"Hydra", "Lazy", "4 headed asshole"},
type = PluginType.PVM
)
@Slf4j
public class HydraPlugin extends Plugin
{
@Getter
private HashSet<LocalPoint> poisonPoints = new HashSet<>();
@Getter
private Hydra hydra;
private boolean inHydraInstance;
private int lastAttackTick;
private int lastPoisonTick;
private static final int[] HYDRA_REGIONS = {
5279, 5280,
5535, 5536
};
@Inject
private Client client;
@Inject
private OverlayManager overlayManager;
@Inject
private HydraOverlay overlay;
@Inject
private HydraPoisonOverlay poisonOverlay;
@Override
protected void startUp()
{
inHydraInstance = checkArea();
lastAttackTick = -1;
poisonPoints.clear();
}
@Override
protected void shutDown()
{
inHydraInstance = false;
hydra = null;
poisonPoints.clear();
removeOverlays();
lastAttackTick = -1;
}
@Subscribe
private void onGameStateChanged(GameStateChanged state)
{
if (state.getGameState() != GameState.LOGGED_IN)
{
return;
}
inHydraInstance = checkArea();
if (inHydraInstance)
{
hydra = new Hydra();
log.debug("Entered hydra instance");
addOverlays();
}
else if (hydra != null)
{
removeOverlays();
hydra = null;
log.debug("Left hydra instance");
}
}
@Subscribe
private void onNpcSpawned(NpcSpawned event)
{
if (!inHydraInstance || event.getNpc().getId() != NpcID.ALCHEMICAL_HYDRA)
{
return;
}
hydra = new Hydra();
log.debug("Hydra spawned");
addOverlays();
}
@Subscribe
public void onAnimationChanged(AnimationChanged animationChanged)
{
Actor actor = animationChanged.getActor();
if (!inHydraInstance || hydra == null || actor == client.getLocalPlayer())
{
return;
}
HydraPhase phase = hydra.getPhase();
// Using the first animation sometimes fucks shit up, so just use 2
if ( /* actor.getAnimation() == phase.getDeathAnim1() || */ actor.getAnimation() == phase.getDeathAnim2())
{
switch (phase)
{
case ONE:
changePhase(HydraPhase.TWO);
log.debug("Hydra phase 2");
return;
case TWO:
changePhase(HydraPhase.THREE);
log.debug("Hydra phase 3");
return;
case THREE:
changePhase(HydraPhase.FOUR);
log.debug("Hydra phase 4");
return;
case FOUR:
hydra = null;
poisonPoints.clear();
log.debug("Hydra dead");
removeOverlays();
return;
default:
log.debug("Tried some weird shit");
break;
}
if (actor.getAnimation() == phase.getDeathAnim1() && phase == HydraPhase.THREE)
{
changePhase(HydraPhase.FOUR);
}
}
else if (actor.getAnimation() == phase.getSpecAnimationId() && phase.getSpecAnimationId() != 0)
{
hydra.setNextSpecial(hydra.getNextSpecial() + 9);
}
if (!poisonPoints.isEmpty() && lastPoisonTick + 10 < client.getTickCount())
{
poisonPoints.clear();
}
}
@Subscribe
public void onProjectileMoved(ProjectileMoved event)
{
if (!inHydraInstance || hydra == null
|| client.getGameCycle() >= event.getProjectile().getStartMovementCycle())
{
return;
}
Projectile projectile = event.getProjectile();
int id = projectile.getId();
if (hydra.getPhase().getSpecProjectileId() != 0 && hydra.getPhase().getSpecProjectileId() == id)
{
poisonPoints.add(event.getPosition());
hydra.setNextSpecial(hydra.getNextSpecial() + 9);
lastPoisonTick = client.getTickCount();
}
else if (client.getTickCount() != lastAttackTick
&& (id == Hydra.AttackStyle.MAGIC.getProjId() || id == Hydra.AttackStyle.RANGED.getProjId()))
{
handleAttack(id);
lastAttackTick = client.getTickCount();
}
}
private boolean checkArea()
{
return Arrays.equals(client.getMapRegions(), HYDRA_REGIONS) && client.isInInstancedRegion();
}
private void addOverlays()
{
overlayManager.add(overlay);
overlayManager.add(poisonOverlay);
}
private void removeOverlays()
{
overlayManager.remove(overlay);
overlayManager.remove(poisonOverlay);
}
private void changePhase(HydraPhase newPhase)
{
hydra.setPhase(newPhase);
hydra.setNextSpecial(3);
hydra.setAttackCount(0);
if (newPhase == HydraPhase.FOUR)
{
switchStyles();
hydra.setNextSwitch(newPhase.getAttacksPerSwitch());
}
}
private void switchStyles()
{
hydra.setNextAttack(hydra.getLastAttack() == Hydra.AttackStyle.MAGIC
? Hydra.AttackStyle.RANGED
: Hydra.AttackStyle.MAGIC);
}
private void handleAttack(int id)
{
hydra.setNextSwitch(hydra.getNextSwitch() - 1);
hydra.setAttackCount(hydra.getAttackCount() + 1);
hydra.setLastAttack(hydra.getNextAttack());
if (id != hydra.getNextAttack().getProjId())
{
switchStyles();
}
else if (hydra.getNextSwitch() <= 0)
{
switchStyles();
hydra.setNextSwitch(hydra.getPhase().getAttacksPerSwitch());
}
}
}

View File

@@ -0,0 +1,61 @@
package net.runelite.client.plugins.alchemicalhydra;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.geom.Area;
import java.util.HashSet;
import javax.inject.Inject;
import javax.inject.Singleton;
import net.runelite.api.Client;
import static net.runelite.api.Perspective.getCanvasTileAreaPoly;
import net.runelite.api.coords.LocalPoint;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
@Singleton
class HydraPoisonOverlay extends Overlay
{
private final HydraPlugin plugin;
private final Client client;
@Inject
public HydraPoisonOverlay(Client client, HydraPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.UNDER_WIDGETS);
this.plugin = plugin;
this.client = client;
}
@Override
public Dimension render(Graphics2D graphics)
{
final HashSet<LocalPoint> initialPoints = plugin.getPoisonPoints();
Area poisonTiles = new Area();
for (LocalPoint point : initialPoints)
{
Polygon poly = getCanvasTileAreaPoly(client, point, 3);
if (poly == null)
{
break;
}
poisonTiles.add(new Area(poly));
}
if (poisonTiles.isEmpty())
{
return null;
}
graphics.setPaintMode();
graphics.setColor(new Color(255, 0, 0, 75));
graphics.draw(poisonTiles);
graphics.setColor(new Color(255, 0, 0, 30));
graphics.fill(poisonTiles);
return null;
}
}

View File

@@ -24,16 +24,16 @@
*/
package net.runelite.client.plugins.antidrag;
import java.awt.Color;
import java.awt.event.KeyEvent;
import net.runelite.client.config.Alpha;
import net.runelite.client.config.Config;
import net.runelite.client.config.ConfigGroup;
import net.runelite.client.config.ConfigItem;
import net.runelite.client.config.Keybind;
import net.runelite.client.config.ModifierlessKeybind;
/*@ConfigGroup(
keyName = "antiDrag",
name = "Anti Drag",
description = "Configuration for the anti drag plugin"
)*/
@ConfigGroup("antidrag")
@ConfigGroup("antiDrag")
public interface AntiDragConfig extends Config
{
@ConfigItem(
@@ -46,4 +46,49 @@ public interface AntiDragConfig extends Config
{
return 600 / 20; // one game tick
}
@ConfigItem(
keyName = "keybind",
name = "keybind",
description = "The keybind you want to use for antidrag",
position = 2
)
default Keybind key()
{
return new ModifierlessKeybind(KeyEvent.VK_SHIFT, 0);
}
@ConfigItem(
keyName = "reqfocus",
name = "Reset on focus loss",
description = "Disable antidrag when losing focus (like alt tabbing)",
position = 3
)
default boolean reqfocus()
{
return false;
}
@ConfigItem(
keyName = "overlay",
name = "Enable overlay",
description = "Do you really need a description?",
position = 4
)
default boolean overlay()
{
return true;
}
@Alpha
@ConfigItem(
keyName = "color",
name = "Overlay color",
description = "Change the overlay color, duh",
position = 5
)
default Color color()
{
return new Color(255, 0, 0, 30);
}
}

View File

@@ -0,0 +1,47 @@
package net.runelite.client.plugins.antidrag;
import com.google.inject.Inject;
import com.google.inject.Singleton;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import net.runelite.api.Client;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
import net.runelite.client.ui.overlay.OverlayPriority;
@Singleton
public class AntiDragOverlay extends Overlay
{
private static final int RADIUS = 20;
private Client client;
private AntiDragConfig config;
@Inject
private AntiDragOverlay(Client client, AntiDragConfig config)
{
this.config = config;
this.client = client;
setPosition(OverlayPosition.TOOLTIP);
setPriority(OverlayPriority.HIGHEST);
setLayer(OverlayLayer.ALWAYS_ON_TOP);
}
@Override
public Dimension render(Graphics2D g)
{
final Color color = config.color();
g.setColor(color);
final net.runelite.api.Point mouseCanvasPosition = client.getMouseCanvasPosition();
final Point mousePosition = new Point(mouseCanvasPosition.getX() - RADIUS, mouseCanvasPosition.getY() - RADIUS);
final Rectangle bounds = new Rectangle(mousePosition.x, mousePosition.y, 2 * RADIUS, 2 * RADIUS);
g.fillOval(bounds.x, bounds.y, bounds.height, bounds.width);
return bounds.getSize();
}
}

View File

@@ -24,26 +24,30 @@
*/
package net.runelite.client.plugins.antidrag;
import com.google.common.eventbus.Subscribe;
import com.google.inject.Provides;
import java.awt.event.KeyEvent;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.events.FocusChanged;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.input.KeyListener;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.input.KeyManager;
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 net.runelite.client.util.HotkeyListener;
@PluginDescriptor(
name = "Anti Drag",
type = PluginType.UTILITY,
enabledByDefault = false)
public class AntiDragPlugin extends Plugin implements KeyListener
name = "Shift Anti Drag",
description = "Prevent dragging an item for a specified delay",
tags = {"antidrag", "delay", "inventory", "items"},
type = PluginType.UTILITY,
enabledByDefault = false
)
public class AntiDragPlugin extends Plugin
{
private static final int DEFAULT_DELAY = 5;
private boolean toggleDrag;
@Inject
private Client client;
@@ -51,6 +55,12 @@ public class AntiDragPlugin extends Plugin implements KeyListener
@Inject
private AntiDragConfig config;
@Inject
private AntiDragOverlay overlay;
@Inject
private OverlayManager overlayManager;
@Inject
private KeyManager keyManager;
@@ -63,57 +73,50 @@ public class AntiDragPlugin extends Plugin implements KeyListener
@Override
protected void startUp() throws Exception
{
client.setInventoryDragDelay(config.dragDelay());
keyManager.registerKeyListener(this);
keyManager.registerKeyListener(hotkeyListener);
toggleDrag = false;
}
@Override
protected void shutDown() throws Exception
{
client.setInventoryDragDelay(DEFAULT_DELAY);
keyManager.unregisterKeyListener(this);
keyManager.unregisterKeyListener(hotkeyListener);
toggleDrag = false;
overlayManager.remove(overlay);
}
@Override
public void keyTyped(KeyEvent e)
private final HotkeyListener hotkeyListener = new HotkeyListener(() -> config.key())
{
}
public boolean toggleDrag = true;
@Override
public void keyPressed(KeyEvent e)
{
/*if (e.getKeyCode() == KeyEvent.VK_SHIFT)
@Override
public void hotkeyPressed()
{
client.setInventoryDragDelay(config.dragDelay());
toggleDrag = !toggleDrag;
if (toggleDrag)
{
if (config.overlay())
{
overlayManager.add(overlay);
}
client.setInventoryDragDelay(config.dragDelay());
}
else
{
overlayManager.remove(overlay);
client.setInventoryDragDelay(DEFAULT_DELAY);
}
}
client.setInventoryDragDelay(config.dragDelay());*/
}
};
@Override
public void keyReleased(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_CONTROL && toggleDrag) {
toggleDrag = false;
client.setInventoryDragDelay(DEFAULT_DELAY);
} else if (e.getKeyCode() == KeyEvent.VK_CONTROL && !toggleDrag) {
toggleDrag = true;
client.setInventoryDragDelay(config.dragDelay());
}
}
/*@Subscribe
@Subscribe
public void onFocusChanged(FocusChanged focusChanged)
{
if (!focusChanged.isFocused())
if (!focusChanged.isFocused() && config.reqfocus())
{
client.setInventoryDragDelay(DEFAULT_DELAY);
overlayManager.remove(overlay);
}
}*/
}
}

View File

@@ -57,6 +57,7 @@ import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.PluginType;
import net.runelite.client.ui.FontManager;
import net.runelite.client.ui.overlay.OverlayManager;
import net.runelite.client.util.ImageUtil;
@@ -64,7 +65,8 @@ import net.runelite.client.util.ImageUtil;
@PluginDescriptor(
name = "Barbarian Assault",
description = "Show a timer to the next call change and game/wave duration in chat.",
tags = {"minigame", "overlay", "timer"}
tags = {"minigame", "overlay", "timer"},
type = PluginType.PVM
)
public class BarbarianAssaultPlugin extends Plugin {
private static final int BA_WAVE_NUM_INDEX = 2;

View File

@@ -1,162 +0,0 @@
package net.runelite.client.plugins.example;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.EnumSet;
import java.util.HashSet;
import javax.inject.Inject;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.ItemID;
import net.runelite.api.Point;
import net.runelite.api.SpriteID;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.callback.ClientThread;
import net.runelite.client.game.ItemManager;
import net.runelite.client.game.SpriteManager;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.plugins.cluescrolls.clues.emote.STASHUnit;
import net.runelite.client.ui.overlay.arrow.ArrowPoint;
import net.runelite.client.ui.overlay.arrow.ArrowPointManager;
import net.runelite.client.ui.overlay.arrow.ArrowType;
@PluginDescriptor(
name = "ArrowTest",
developerPlugin = true
)
public class ExamplePlugin extends Plugin
{
@Inject
private ArrowPointManager arrowPointManager;
@Inject
private SpriteManager spriteManager;
@Inject
private ItemManager itemManager;
@Inject
private Client client;
@Inject
private ClientThread clientThread;
private boolean firstLogin = true;
@Override
public void startUp() throws Exception
{
clientThread.invokeLater(this::loadArrows);
}
@Override
public void shutDown()
{
arrowPointManager.clear();
}
public void loadArrows()
{
if (client.getGameState() != GameState.LOGGED_IN)
{
return;
}
BufferedImage i = spriteManager.getSprite(SpriteID.MINIMAP_GUIDE_ARROW_YELLOW, 0);
BufferedImage i3 = spriteManager.getSprite(SpriteID.RED_GUIDE_ARROW, 0);
AffineTransform at = new AffineTransform();
at.concatenate(AffineTransform.getScaleInstance(1, -1));
at.concatenate(AffineTransform.getTranslateInstance(0, -i3.getHeight()));
BufferedImage i4 = new BufferedImage(i3.getWidth(), i3.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics2D g = i4.createGraphics();
g.transform(at);
g.drawImage(i3, 0, 0, null);
g.dispose();
Point i3Offset = new Point(0, i3.getHeight() / -2);
ArrowPoint one = ArrowPoint.builder()
.worldPoint(new WorldPoint(1554, 3551, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, one);
ArrowPoint two = ArrowPoint.builder()
.worldPoint(new WorldPoint(1544, 3580, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, two);
ArrowPoint three = ArrowPoint.builder()
.worldPoint(new WorldPoint(1571, 3541, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, three);
HashSet<Integer> NPCs = new HashSet<>();
NPCs.add(6910);
ArrowPoint npcone = ArrowPoint.builder()
.worldPoint(new WorldPoint(1545, 3595, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.npcIDs(NPCs)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.NPC))
.build();
//arrowPointManager.add(this, npcone);
HashSet<Integer> NPCs2 = new HashSet<>();
NPCs2.add(6889);
NPCs2.add(6883);
NPCs2.add(6885);
ArrowPoint npctwo = ArrowPoint.builder()
.worldPoint(new WorldPoint(1551, 3561, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.worldImageOffset(new Point(0, -i4.getHeight()))
.npcIDs(NPCs2)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.NPC))
.build();
//arrowPointManager.add(this, npctwo);
HashSet<Integer> OBJs = new HashSet<>();
OBJs.add(STASHUnit.SHAYZIEN_WAR_TENT);
ArrowPoint objone = ArrowPoint.builder()
.worldPoint(new WorldPoint(1550, 3541, 0))
.minimapImage(i4)
.worldImage(i)
.minimapImageOffset(i3Offset)
.worldImageOffset(new Point(0, -i4.getHeight()))
.objectIDs(OBJs)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.OBJECT))
.build();
arrowPointManager.add(this, objone);
BufferedImage i5 = itemManager.getImage(ItemID.BONES);
ArrowPoint four = ArrowPoint.builder()
.worldPoint(new WorldPoint(1517, 3553, 0))
.minimapImage(i5)
.worldImage(i5)
.minimapImagePointToTarget(false)
.types(EnumSet.of(ArrowType.MINIMAP, ArrowType.WORLD_POINT))
.build();
//arrowPointManager.add(this, four);
}
}

View File

@@ -50,11 +50,13 @@ import net.runelite.client.eventbus.EventBus;
import net.runelite.client.eventbus.Subscribe;
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 net.runelite.client.util.Text;
@PluginDescriptor(
name = "Quest Helper",
type = PluginType.UTILITY,
description = "Helps you with your quests"
)
@Slf4j

View File

@@ -0,0 +1,131 @@
/*
* Copyright (c) 2018, Shaun Dreclin <https://github.com/ShaunDreclin>
* 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.tarnslair;
import com.google.common.collect.Sets;
import java.util.Set;
import static net.runelite.api.NullObjectID.NULL_20575;
import static net.runelite.api.ObjectID.*;
class Obstacles
{
static final Set<Integer> STAIRCASE_IDS = Sets.newHashSet(
PASSAGEWAY_15770, /*Wall staircase*/
PASSAGEWAY_15771, /*Wall staircase*/
PASSAGEWAY_15772, /*Wall staircase*/
PASSAGEWAY_15773, /*Wall staircase*/
PASSAGEWAY_15774, /*Wall staircase*/
PASSAGEWAY_16129, /*Wall staircase*/
PASSAGEWAY_16130, /*Wall staircase*/
PASSAGEWAY_16131, /*Wall staircase*/
PASSAGEWAY_16132, /*Wall staircase*/
PASSAGEWAY_16133, /*Wall staircase*/
PASSAGEWAY_16134, /*Wall staircase*/
PASSAGEWAY_18307, /*Wall staircase*/
PASSAGEWAY_18308, /*Wall staircase*/
PASSAGEWAY_18309, /*Wall staircase*/
PASSAGEWAY_18310, /*Wall staircase*/
PASSAGEWAY_18311, /*Wall staircase*/
PASSAGEWAY_20488, /*Wall staircase*/
PASSAGEWAY_20489, /*Wall staircase*/
PASSAGEWAY_20490, /*Wall staircase*/
PASSAGEWAY_20491, /*Wall staircase*/
PASSAGEWAY_20492, /*Wall staircase*/
PASSAGEWAY_20493, /*Wall staircase*/
PASSAGEWAY_20495, /*Wall staircase*/
PASSAGEWAY_20497, /*Wall staircase*/
PASSAGEWAY_20498, /*Wall staircase*/
PASSAGEWAY_20499, /*Wall staircase*/
PASSAGEWAY_20500, /*Wall staircase*/
PASSAGEWAY_20501, /*Wall staircase*/
PASSAGEWAY_20502, /*Wall staircase*/
PASSAGEWAY_20503, /*Wall staircase*/
PASSAGEWAY_20504, /*Wall staircase*/
PASSAGEWAY_20505, /*Wall staircase*/
PASSAGEWAY_20506, /*Wall staircase*/
PASSAGEWAY_20506, /*Wall staircase*/
PASSAGEWAY_20507, /*Wall staircase*/
PASSAGEWAY_20509, /*Wall staircase*/
PASSAGEWAY_20510, /*Wall staircase*/
PASSAGEWAY_20511, /*Wall staircase*/
PASSAGEWAY_20512, /*Wall staircase*/
PASSAGEWAY_20513, /*Wall staircase*/
PASSAGEWAY_20514, /*Wall staircase*/
PASSAGEWAY_20515, /*Wall staircase*/
PASSAGEWAY_20516, /*Wall staircase*/
PASSAGEWAY_20517, /*Wall staircase*/
PASSAGEWAY_20518, /*Wall staircase*/
PASSAGEWAY_20519, /*Wall staircase*/
PASSAGEWAY_20520, /*Wall staircase*/
PASSAGEWAY_20521, /*Wall staircase*/
PASSAGEWAY_20522, /*Wall staircase*/
PASSAGEWAY_20523, /*Wall staircase*/
PASSAGEWAY_20524, /*Wall staircase*/
PASSAGEWAY_20525, /*Wall staircase*/
PASSAGEWAY_20526, /*Wall staircase*/
PASSAGEWAY_20527, /*Wall staircase*/
PASSAGEWAY_20528, /*Wall staircase*/
PASSAGEWAY_20529, /*Wall staircase*/
PASSAGEWAY_20530, /*Wall staircase*/
PASSAGEWAY_20531, /*Wall staircase*/
PASSAGEWAY_20532, /*Wall staircase*/
PASSAGEWAY_20533, /*Wall staircase*/
PASSAGEWAY_20534, /*Wall staircase*/
PASSAGEWAY_20535, /*Wall staircase*/
PASSAGEWAY_20536, /*Wall staircase*/
PASSAGEWAY_20537, /*Wall staircase*/
PASSAGEWAY_20538, /*Wall staircase*/
PASSAGEWAY_20539, /*Wall staircase*/
STAIRS_17098, /*Floor staircase*/
STAIRS_17099, /*Floor staircase*/
STAIRS_18973, /*Floor staircase*/
STAIRS_18974 /*Floor staircase*/
);
static final Set<Integer> WALL_TRAP_IDS = Sets.newHashSet(
WALL_20590, /*Wall spikes*/
WALL_20592, /*Wall spikes*/
WALL_20594, /*Wall spikes*/
WALL_20596, /*Wall spikes*/
WALL_20588, /*Wall spikes*/
WALL_20613, /*Wall pusher*/
WALL_20615, /*Wall pusher*/
WALL_20616, /*Wall pusher*/
WALL_20618, /*Wall pusher*/
HANGING_LOG_20571, /*Hanging log*/
HANGING_LOG_20572, /*Hanging log*/
HANGING_LOG_20573, /*Hanging log*/
HANGING_LOG_20574 /*Hanging log*/
);
static final Set<Integer> FLOOR_TRAP_IDS = Sets.newHashSet(
FLOOR_20583, /*Floor spikes*/
FLOOR_20584, /*Floor spikes*/
NULL_20575, /*Floor spikes (visible)*/
FLOOR_20628, /*Trapdoor*/
FLOOR_20634, /*Floor button*/
FLOOR_20636 /*Floor button*/
);
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2018, Shaun Dreclin <https://github.com/ShaunDreclin>
* 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.tarnslair;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Polygon;
import javax.inject.Inject;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.coords.LocalPoint;
import net.runelite.client.ui.overlay.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition;
@Slf4j
public class TarnsLairOverlay extends Overlay
{
private static final int MAX_DISTANCE = 2350;
private final Client client;
private final TarnsLairPlugin plugin;
@Inject
public TarnsLairOverlay(Client client, TarnsLairPlugin plugin)
{
setPosition(OverlayPosition.DYNAMIC);
setLayer(OverlayLayer.ABOVE_SCENE);
this.client = client;
this.plugin = plugin;
}
@Override
public Dimension render(Graphics2D graphics)
{
if (!plugin.isInLair())
{
return null;
}
LocalPoint playerLocation = client.getLocalPlayer().getLocalLocation();
plugin.getStaircases().forEach((obstacle, tile) ->
{
if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
{
Polygon p = tile.getGameObjects()[0].getConvexHull();
if (p != null)
{
graphics.setColor(Color.GREEN);
graphics.drawPolygon(p);
}
}
});
plugin.getWallTraps().forEach((obstacle, tile) ->
{
if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
{
Polygon p = tile.getGameObjects()[0].getConvexHull();
if (p != null)
{
graphics.setColor(Color.CYAN);
graphics.drawPolygon(p);
}
}
});
plugin.getFloorTraps().forEach((obstacle, tile) ->
{
if (tile.getPlane() == client.getPlane() && obstacle.getLocalLocation().distanceTo(playerLocation) < MAX_DISTANCE)
{
Polygon p = obstacle.getCanvasTilePoly();
if (p != null)
{
graphics.setColor(Color.CYAN);
graphics.drawPolygon(p);
}
}
});
return null;
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright (c) 2018-2019, Shaun Dreclin <https://github.com/ShaunDreclin>
* 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.tarnslair;
import java.util.HashMap;
import javax.inject.Inject;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Client;
import net.runelite.api.GameState;
import net.runelite.api.Tile;
import net.runelite.api.TileObject;
import net.runelite.api.events.GameObjectChanged;
import net.runelite.api.events.GameObjectDespawned;
import net.runelite.api.events.GameObjectSpawned;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.GroundObjectChanged;
import net.runelite.api.events.GroundObjectDespawned;
import net.runelite.api.events.GroundObjectSpawned;
import net.runelite.client.eventbus.Subscribe;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
import net.runelite.client.ui.overlay.OverlayManager;
@PluginDescriptor(
name = "Tarn's Lair",
description = "Mark tiles and clickboxes to help traverse the maze",
tags = {"agility", "maze", "minigame", "overlay"}
)
@Slf4j
public class TarnsLairPlugin extends Plugin
{
private static final int TARNS_LAIR_NORTH_REGION = 12616;
private static final int TARNS_LAIR_SOUTH_REGION = 12615;
@Getter(AccessLevel.PACKAGE)
private final HashMap<TileObject, Tile> staircases = new HashMap<>();
@Getter(AccessLevel.PACKAGE)
private final HashMap<TileObject, Tile> wallTraps = new HashMap<>();
@Getter(AccessLevel.PACKAGE)
private final HashMap<TileObject, Tile> floorTraps = new HashMap<>();
@Getter(AccessLevel.PACKAGE)
private boolean inLair;
@Inject
private Client client;
@Inject
private OverlayManager overlayManager;
@Inject
private TarnsLairOverlay overlay;
@Override
protected void startUp() throws Exception
{
overlayManager.add(overlay);
}
@Override
protected void shutDown() throws Exception
{
overlayManager.remove(overlay);
staircases.clear();
wallTraps.clear();
floorTraps.clear();
inLair = false;
}
@Subscribe
public void onGameTick(GameTick event)
{
int regionID = client.getLocalPlayer().getWorldLocation().getRegionID();
inLair = (regionID == TARNS_LAIR_NORTH_REGION || regionID == TARNS_LAIR_SOUTH_REGION);
}
@Subscribe
public void onGameObjectSpawned(GameObjectSpawned event)
{
onTileObject(event.getTile(), null, event.getGameObject());
}
@Subscribe
public void onGameObjectChanged(GameObjectChanged event)
{
onTileObject(event.getTile(), event.getPrevious(), event.getGameObject());
}
@Subscribe
public void onGameObjectDespawned(GameObjectDespawned event)
{
onTileObject(event.getTile(), event.getGameObject(), null);
}
@Subscribe
public void onGroundObjectSpawned(GroundObjectSpawned event)
{
onTileObject(event.getTile(), null, event.getGroundObject());
}
@Subscribe
public void onGroundObjectChanged(GroundObjectChanged event)
{
onTileObject(event.getTile(), event.getPrevious(), event.getGroundObject());
}
@Subscribe
public void onGroundObjectDespawned(GroundObjectDespawned event)
{
onTileObject(event.getTile(), event.getGroundObject(), null);
}
@Subscribe
public void onGameStateChanged(GameStateChanged event)
{
if (event.getGameState() == GameState.LOADING)
{
staircases.clear();
wallTraps.clear();
floorTraps.clear();
}
}
private void onTileObject(Tile tile, TileObject oldObject, TileObject newObject)
{
staircases.remove(oldObject);
if (newObject != null && Obstacles.STAIRCASE_IDS.contains(newObject.getId()))
{
staircases.put(newObject, tile);
}
wallTraps.remove(oldObject);
if (newObject != null && Obstacles.WALL_TRAP_IDS.contains(newObject.getId()))
{
wallTraps.put(newObject, tile);
}
floorTraps.remove(oldObject);
if (newObject != null && Obstacles.FLOOR_TRAP_IDS.contains(newObject.getId()))
{
floorTraps.put(newObject, tile);
}
}
}

View File

@@ -292,6 +292,13 @@ public class XpGlobesOverlay extends Overlay
.right(xpHrString)
.build());
}
String timeLeft = xpTrackerService.getTimeTillGoal(mouseOverSkill.getSkill());
xpTooltip.getChildren().add(LineComponent.builder()
.left("Time left:")
.leftColor(Color.ORANGE)
.right(timeLeft)
.build());
}
xpTooltip.render(graphics);

View File

@@ -62,4 +62,9 @@ public interface XpTrackerService
* Get the amount of XP left until goal level
*/
int getEndGoalXp(Skill skill);
/**
* Get the amount of time left until goal level
*/
String getTimeTillGoal(Skill skill);
}

View File

@@ -80,4 +80,10 @@ class XpTrackerServiceImpl implements XpTrackerService
{
return plugin.getSkillSnapshot(skill).getEndGoalXp();
}
@Override
public String getTimeTillGoal(Skill skill)
{
return plugin.getSkillSnapshot(skill).getTimeTillGoal();
}
}