hydra: config options and useless stun timer (#1731)

hydra: config options and useless stun timer
This commit is contained in:
Owain van Brakel
2019-10-07 08:47:16 +02:00
committed by GitHub
12 changed files with 1232 additions and 631 deletions

View File

@@ -1819,4 +1819,24 @@ public interface Client extends GameShell
void setSelectedSpellWidget(int widgetID);
void setSelectedSpellChildIndex(int index);
/**
* Scales values from pixels onto canvas
*
* @see net.runelite.client.util.ImageUtil#resizeSprite(Client, Sprite, int, int)
*
* @param canvas the array we're writing to
* @param pixels pixels to draw
* @param color should be 0
* @param pixelX x index
* @param pixelY y index
* @param canvasIdx index in canvas (canvas[canvasIdx])
* @param canvasOffset x offset
* @param newWidth new width
* @param newHeight new height
* @param pixelWidth pretty much horizontal scale
* @param pixelHeight pretty much vertical scale
* @param oldWidth old width
*/
void scaleSprite(int[] canvas, int[] pixels, int color, int pixelX, int pixelY, int canvasIdx, int canvasOffset, int newWidth, int newHeight, int pixelWidth, int pixelHeight, int oldWidth);
}

View File

@@ -92,4 +92,20 @@ public interface Sprite
* @param color target color
*/
void toBufferedOutline(BufferedImage img, int color);
int getMaxWidth();
void setMaxWidth(int maxWidth);
int getMaxHeight();
void setMaxHeight(int maxHeight);
int getOffsetX();
void setOffsetX(int offsetX);
int getOffsetY();
void setOffsetY(int offsetY);
}

View File

@@ -1573,6 +1573,8 @@ public final class SpriteID
/* Unmapped: 1709, 1710 */
public static final int TAB_MAGIC_SPELLBOOK_ARCEUUS = 1711;
public static final int BIG_ASS_GUTHIX_SPELL = 1774;
public static final int BIG_ASS_GREY_ENTANGLE = 1788;
public static final int BIG_ASS_WHITE_ENTANGLE = 1789;
public static final int BIG_SUPERHEAT = 1800;
public static final int BIG_SPEC_TRANSFER = 1959;
/* Unmapped: 1712~2175 */

View File

@@ -53,7 +53,13 @@ public enum VarPlayer
IN_RAID_PARTY(1427),
NMZ_REWARD_POINTS(1060),
/**
* The 11 least significant bits of this var correspond to the player
* you're currently fighting. Value is -1 when not fighting any player.
*
* Client.getVar(ATTACKING_PLAYER) & 2047 == Client.getLocalInteractingIndex();
*/
ATTACKING_PLAYER(1075),
/**

View File

@@ -688,7 +688,9 @@ public enum Varbits
*/
GAUNTLET_ENTERED(9178),
WITHDRAW_X_AMOUNT(3960);
WITHDRAW_X_AMOUNT(3960),
IN_PVP_AREA(8121);
/**
* The raw varbit ID.

View File

@@ -0,0 +1,185 @@
/*
* Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* 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.alchemicalhydra;
import java.awt.Color;
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.ConfigSection;
@ConfigGroup("betterHydra")
public interface HydraConfig extends Config
{
@ConfigSection(
keyName = "features",
name = "Features",
description = "Feathers. Jk, features",
position = 0
)
default boolean features()
{
return true;
}
@ConfigItem(
keyName = "counting",
name = "Prayer helper",
description = "Basically everything this plugin is known for. Also has attacks between specs and poison overlay. Shouldn't NOT use this tbh",
position = 1,
section = "features"
)
default boolean counting()
{
return true;
}
@ConfigItem(
keyName = "fountain",
name = "Fountain helper",
description = "Indicates if hydra is on a fountain",
position = 2,
section = "features"
)
default boolean fountain()
{
return true;
}
@ConfigItem(
keyName = "stun",
name = "Stun timer",
description = "Shows when you can walk in fire phase",
position = 3,
section = "features"
)
default boolean stun()
{
return false;
}
@ConfigSection(
keyName = "colours",
name = "Colours",
description = "colours...",
position = 2
)
default boolean colours()
{
return false;
}
@Alpha
@ConfigItem(
keyName = "safeCol",
name = "Safe colour",
description = "Colour overlay will be when there's >2 attacks left",
position = 1,
section = "colours"
)
default Color safeCol()
{
return new Color(0, 156, 0, 156);
}
@Alpha
@ConfigItem(
keyName = "medCol",
name = "Medium colour",
description = "Colour overlay will be when a input is coming up",
position = 2,
section = "colours"
)
default Color medCol()
{
return new Color(200, 156, 0, 156);
}
@Alpha
@ConfigItem(
keyName = "badCol",
name = "Bad colour",
description = "Colour overlay will be when you have to do something NOW",
position = 3,
section = "colours"
)
default Color badCol()
{
return new Color(156, 0, 0, 156);
}
@Alpha
@ConfigItem(
keyName = "poisonBorderCol",
name = "Poison border colour",
description = "Colour the edges of the area highlighted by poison special will be",
position = 4,
section = "colours"
)
default Color poisonBorderCol()
{
return new Color(255, 0, 0, 100);
}
@Alpha
@ConfigItem(
keyName = "poisonCol",
name = "Poison colour",
description = "Colour the fill of the area highlighted by poison special will be",
position = 5,
section = "colours"
)
default Color poisonCol()
{
return new Color(255, 0, 0, 50);
}
@Alpha
@ConfigItem(
keyName = "fountainColA",
name = "Fountain colour (not on top)",
description = "Fountain colour (not on top)",
position = 6,
section = "colours"
)
default Color fountainColA()
{
return new Color(255, 0, 0, 100);
}
@Alpha
@ConfigItem(
keyName = "fountainColB",
name = "Fountain colour (on top)",
description = "Fountain colour (on top)",
position = 7,
section = "colours"
)
default Color fountainColB()
{
return new Color(0, 255, 0, 100);
}
}

View File

@@ -28,24 +28,27 @@ import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Setter;
import net.runelite.api.Client;
import net.runelite.api.IndexDataBase;
import net.runelite.api.Prayer;
import net.runelite.api.Sprite;
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.ComponentOrientation;
import net.runelite.client.ui.overlay.components.InfoBoxComponent;
import net.runelite.client.ui.overlay.components.PanelComponent;
import net.runelite.client.util.ImageUtil;
@Singleton
class HydraOverlay extends Overlay
{
private static final Color RED_BG_COL = new Color(156, 0, 0, 156);
private static final Color YEL_BG_COL = new Color(200, 156, 0, 156);
private static final Color GRN_BG_COL = new Color(0, 156, 0, 156);
static final int IMGSIZE = 36;
private final HydraPlugin plugin;
@@ -53,50 +56,104 @@ class HydraOverlay extends Overlay
private final SpriteManager spriteManager;
private final PanelComponent panelComponent = new PanelComponent();
private BufferedImage stunImg;
@Setter(AccessLevel.PACKAGE)
private Color safeCol;
@Setter(AccessLevel.PACKAGE)
private Color medCol;
@Setter(AccessLevel.PACKAGE)
private Color badCol;
@Setter(AccessLevel.PACKAGE)
private int stunTicks;
@Inject
HydraOverlay(final HydraPlugin plugin, final Client client, final SpriteManager spriteManager)
{
this.plugin = plugin;
this.client = client;
this.spriteManager = spriteManager;
setPosition(OverlayPosition.BOTTOM_RIGHT);
this.setPosition(OverlayPosition.BOTTOM_RIGHT);
panelComponent.setOrientation(ComponentOrientation.VERTICAL);
}
@Override
public Dimension render(Graphics2D graphics2D)
{
Hydra hydra = plugin.getHydra();
final Hydra hydra = plugin.getHydra();
panelComponent.getChildren().clear();
if (hydra == null || client == null)
if (hydra == null)
{
return null;
}
//Add spec overlay first, to keep it above pray
// First add stunned thing if needed
if (stunTicks > 0)
{
addStunOverlay();
}
if (plugin.isCounting())
{
// Add spec box second, to keep it above pray
addSpecOverlay(hydra);
// Finally add prayer box
addPrayOverlay(hydra);
}
panelComponent.setPreferredSize(new Dimension(40, 0));
panelComponent.setBorder(new Rectangle(0, 0, 0, 0));
return panelComponent.render(graphics2D);
}
private void addStunOverlay()
{
final InfoBoxComponent stunComponent = new InfoBoxComponent();
stunComponent.setBackgroundColor(badCol);
stunComponent.setImage(getStunImg());
stunComponent.setText(" " + stunTicks);
stunComponent.setPreferredSize(new Dimension(40, 40));
panelComponent.getChildren().add(stunComponent);
}
private void addSpecOverlay(final Hydra hydra)
{
final HydraPhase phase = hydra.getPhase();
final int nextSpec = hydra.getNextSpecialRelative();
if (nextSpec <= 3)
if (nextSpec > 3)
{
InfoBoxComponent specComponent = new InfoBoxComponent();
return;
}
final InfoBoxComponent specComponent = new InfoBoxComponent();
if (nextSpec == 0)
{
specComponent.setBackgroundColor(RED_BG_COL);
}
else if (nextSpec == 1)
{
specComponent.setBackgroundColor(YEL_BG_COL);
}
specComponent.setImage(phase.getSpecImage(spriteManager));
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);
if (nextSpec == 0)
{
specComponent.setBackgroundColor(badCol);
}
else if (nextSpec == 1)
{
specComponent.setBackgroundColor(medCol);
}
specComponent.setImage(phase.getSpecImage(spriteManager));
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);
}
private void addPrayOverlay(final Hydra hydra)
{
final Prayer nextPrayer = hydra.getNextAttack().getPrayer();
final int nextSwitch = hydra.getNextSwitch();
@@ -104,21 +161,65 @@ class HydraOverlay extends Overlay
if (nextSwitch == 1)
{
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? YEL_BG_COL : RED_BG_COL);
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? medCol : badCol);
}
else
{
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? GRN_BG_COL : RED_BG_COL);
prayComponent.setBackgroundColor(client.isPrayerActive(nextPrayer) ? safeCol : badCol);
}
prayComponent.setImage(hydra.getNextAttack().getImage(spriteManager));
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);
panelComponent.getChildren().add(prayComponent);
}
boolean onGameTick()
{
return --stunTicks <= 0;
}
private BufferedImage getStunImg()
{
if (stunImg == null)
{
stunImg = createStunImage(client);
}
return stunImg;
}
private static BufferedImage createStunImage(Client client)
{
final Sprite root = getSprite(client, SpriteID.BIG_ASS_GREY_ENTANGLE);
final Sprite mark = getSprite(client, SpriteID.TRADE_EXCLAMATION_MARK_ITEM_REMOVAL_WARNING);
if (mark == null || root == null)
{
return null;
}
final Sprite sprite = ImageUtil.mergeSprites(client, ImageUtil.resizeSprite(client, root, IMGSIZE, IMGSIZE), mark);
return sprite.toBufferedImage();
}
private static Sprite getSprite(Client client, int id)
{
final IndexDataBase spriteDb = client.getIndexSprites();
if (spriteDb == null)
{
return null;
}
final Sprite[] sprites = client.getSprites(spriteDb, id, 0);
if (sprites == null)
{
return null;
}
return sprites[0];
}
}

View File

@@ -24,6 +24,7 @@
*/
package net.runelite.client.plugins.alchemicalhydra;
import com.google.inject.Provides;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
@@ -44,9 +45,12 @@ import net.runelite.api.Projectile;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.ConfigChanged;
import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.GameTick;
import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.ProjectileMoved;
import net.runelite.client.config.ConfigManager;
import net.runelite.client.eventbus.EventBus;
import net.runelite.client.plugins.Plugin;
import net.runelite.client.plugins.PluginDescriptor;
@@ -71,6 +75,15 @@ public class HydraPlugin extends Plugin
@Getter(AccessLevel.PACKAGE)
private Hydra hydra;
@Getter(AccessLevel.PACKAGE)
private boolean counting;
@Getter(AccessLevel.PACKAGE)
private boolean fountain;
@Getter(AccessLevel.PACKAGE)
private boolean stun;
private boolean inHydraInstance;
private int lastAttackTick;
@@ -78,26 +91,40 @@ public class HydraPlugin extends Plugin
5279, 5280,
5535, 5536
};
private static final int STUN_LENGTH = 7;
@Inject
private Client client;
@Inject
private OverlayManager overlayManager;
private EventBus eventBus;
@Inject
private HydraConfig config;
@Inject
private HydraOverlay overlay;
@Inject
private HydraSceneOverlay poisonOverlay;
private HydraSceneOverlay sceneOverlay;
@Inject
private EventBus eventBus;
private OverlayManager overlayManager;
@Provides
HydraConfig provideConfig(ConfigManager configManager)
{
return configManager.getConfig(HydraConfig.class);
}
@Override
protected void startUp()
{
initConfig();
eventBus.subscribe(ConfigChanged.class, this, this::onConfigChanged);
eventBus.subscribe(GameStateChanged.class, this, this::onGameStateChanged);
inHydraInstance = checkArea();
lastAttackTick = -1;
poisonProjectiles.clear();
@@ -117,6 +144,20 @@ public class HydraPlugin extends Plugin
lastAttackTick = -1;
}
private void initConfig()
{
this.counting = config.counting();
this.fountain = config.fountain();
this.stun = config.stun();
this.overlay.setSafeCol(config.safeCol());
this.overlay.setMedCol(config.medCol());
this.overlay.setBadCol(config.badCol());
this.sceneOverlay.setPoisonBorder(config.poisonBorderCol());
this.sceneOverlay.setPoisonFill(config.poisonCol());
this.sceneOverlay.setBadFountain(config.fountainColA());
this.sceneOverlay.setGoodFountain(config.fountainColB());
}
private void addFightSubscriptions()
{
eventBus.subscribe(AnimationChanged.class, "fight", this::onAnimationChanged);
@@ -124,6 +165,48 @@ public class HydraPlugin extends Plugin
eventBus.subscribe(ChatMessage.class, "fight", this::onChatMessage);
}
private void onConfigChanged(ConfigChanged event)
{
if (!event.getGroup().equals("betterHydra"))
{
return;
}
switch (event.getKey())
{
case "counting":
this.counting = config.counting();
break;
case "fountain":
this.fountain = config.fountain();
break;
case "stun":
this.stun = config.stun();
break;
case "safeCol":
overlay.setSafeCol(config.safeCol());
return;
case "medCol":
overlay.setMedCol(config.medCol());
return;
case "badCol":
overlay.setBadCol(config.badCol());
return;
case "poisonBorderCol":
sceneOverlay.setPoisonBorder(config.poisonBorderCol());
break;
case "poisonCol":
sceneOverlay.setPoisonFill(config.poisonCol());
break;
case "fountainColA":
sceneOverlay.setBadFountain(config.fountainColA());
break;
case "fountainColB":
sceneOverlay.setGoodFountain(config.fountainColB());
break;
}
}
private void onGameStateChanged(GameStateChanged state)
{
if (state.getGameState() != GameState.LOGGED_IN)
@@ -270,12 +353,27 @@ public class HydraPlugin extends Plugin
private void onChatMessage(ChatMessage event)
{
if (!event.getMessage().equals("The chemicals neutralise the Alchemical Hydra's defences!"))
if (event.getMessage().equals("The chemicals neutralise the Alchemical Hydra's defences!"))
{
return;
hydra.setWeakened(true);
}
else if (event.getMessage().equals("The Alchemical Hydra temporarily stuns you."))
{
if (isStun())
{
overlay.setStunTicks(STUN_LENGTH);
eventBus.subscribe(GameTick.class, "hydraStun", this::onGameTick);
}
}
}
hydra.setWeakened(true);
private void onGameTick(GameTick tick)
{
if (overlay.onGameTick())
{
// unregister self when 7 ticks have passed
eventBus.unregister("hydraStun");
}
}
private boolean checkArea()
@@ -285,13 +383,20 @@ public class HydraPlugin extends Plugin
private void addOverlays()
{
overlayManager.add(overlay);
overlayManager.add(poisonOverlay);
if (counting || stun)
{
overlayManager.add(overlay);
}
if (counting || fountain)
{
overlayManager.add(sceneOverlay);
}
}
private void removeOverlays()
{
overlayManager.remove(overlay);
overlayManager.remove(poisonOverlay);
overlayManager.remove(sceneOverlay);
}
}

View File

@@ -34,6 +34,8 @@ import java.util.Collection;
import java.util.Map;
import javax.inject.Inject;
import javax.inject.Singleton;
import lombok.AccessLevel;
import lombok.Setter;
import net.runelite.api.Client;
import static net.runelite.api.Perspective.getCanvasTileAreaPoly;
import net.runelite.api.Projectile;
@@ -47,9 +49,17 @@ import net.runelite.client.ui.overlay.OverlayPosition;
@Singleton
class HydraSceneOverlay extends Overlay
{
private static final Color GREEN = new Color(0, 255, 0, 100);
private static final Color RED = new Color(255, 0, 0, 100);
private static final Color REDFILL = new Color(255, 0, 0, 50);
@Setter(AccessLevel.PACKAGE)
private Color poisonBorder;
@Setter(AccessLevel.PACKAGE)
private Color poisonFill;
@Setter(AccessLevel.PACKAGE)
private Color goodFountain;
@Setter(AccessLevel.PACKAGE)
private Color badFountain;
private final HydraPlugin plugin;
private final Client client;
@@ -69,12 +79,12 @@ class HydraSceneOverlay extends Overlay
Hydra hydra = plugin.getHydra();
final Map<LocalPoint, Projectile> poisonProjectiles = plugin.getPoisonProjectiles();
if (!poisonProjectiles.isEmpty())
if (plugin.isCounting() && !poisonProjectiles.isEmpty())
{
drawPoisonArea(graphics, poisonProjectiles);
}
if (hydra.getPhase().getFountain() != null)
if (plugin.isFountain() && hydra.getPhase().getFountain() != null)
{
drawFountain(graphics, hydra);
}
@@ -103,9 +113,9 @@ class HydraSceneOverlay extends Overlay
}
graphics.setPaintMode();
graphics.setColor(RED);
graphics.setColor(poisonBorder);
graphics.draw(poisonTiles);
graphics.setColor(REDFILL);
graphics.setColor(poisonFill);
graphics.fill(poisonTiles);
}
@@ -141,11 +151,11 @@ class HydraSceneOverlay extends Overlay
if (hydra.getNpc().getWorldArea().intersectsWith(new WorldArea(wp, 1, 1))) // coords
{ // WHICH FUCKING RETARD DID X, Y, dX, dY, Z???? IT'S XYZdXdY REEEEEEEEEE
color = GREEN;
color = goodFountain;
}
else
{
color = RED;
color = badFountain;
}
graphics.setColor(color);

View File

@@ -1086,4 +1086,8 @@ public interface RSClient extends RSGameShell, Client
@Import("selectedSpellChildIndex")
@Override
void setSelectedSpellChildIndex(int index);
@Import("Sprite_drawScaled")
@Override
void scaleSprite(int[] canvas, int[] pixels, int color, int pixelX, int pixelY, int canvasIdx, int canvasOffset, int newWidth, int newHeight, int pixelWidth, int pixelHeight, int oldWidth);
}

View File

@@ -25,14 +25,34 @@ public interface RSSprite extends Sprite
void setRaster();
@Import("width")
@Override
int getMaxWidth();
@Import("width")
@Override
void setMaxWidth(int maxWidth);
@Import("height")
@Override
int getMaxHeight();
@Import("height")
@Override
void setMaxHeight(int maxHeight);
@Import("xOffset")
@Override
int getOffsetX(); ;
@Import("xOffset")
@Override
void setOffsetX(int offsetX);
@Import("yOffset")
@Override
int getOffsetY(); ;
@Import("yOffset")
@Override
void setOffsetY(int offsetY);
}