Alch hydra: Improve poison overlay, add defence overlay, bugfixes (#129)

* Alch hydra: Improve poison overlay, add defence overlay, bugfixes

* Hydra: use WorldArea instead of npc poly tile or whatever it was
This commit is contained in:
Lucwousin
2019-04-26 10:20:09 +02:00
committed by Kyleeld
parent d5359261f0
commit 0a63a79a8c
5 changed files with 192 additions and 72 deletions

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, https://runelitepl.us * Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -27,6 +27,7 @@ package net.runelite.client.plugins.alchemicalhydra;
import javax.inject.Singleton; import javax.inject.Singleton;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.runelite.api.NPC;
import net.runelite.api.Prayer; import net.runelite.api.Prayer;
import net.runelite.api.ProjectileID; import net.runelite.api.ProjectileID;
@@ -51,6 +52,9 @@ class Hydra
} }
} }
@Getter
private NPC npc;
@Getter @Getter
@Setter @Setter
private HydraPhase phase; private HydraPhase phase;
@@ -75,12 +79,19 @@ class Hydra
@Setter @Setter
private AttackStyle lastAttack; private AttackStyle lastAttack;
Hydra() @Getter
@Setter
private boolean weakened;
Hydra(NPC npc)
{ {
this.npc = npc;
this.phase = HydraPhase.ONE; this.phase = HydraPhase.ONE;
this.nextAttack = AttackStyle.MAGIC; this.nextAttack = AttackStyle.MAGIC;
this.lastAttack = AttackStyle.MAGIC; // important, else we wouldn't switch if the first attack is ranged
this.nextSpecial = 3; this.nextSpecial = 3;
this.nextSwitch = phase.getAttacksPerSwitch(); this.nextSwitch = phase.getAttacksPerSwitch();
this.attackCount = 0; this.attackCount = 0;
this.weakened = false;
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, https://runelitepl.us * Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, https://runelitepl.us * Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -28,33 +28,37 @@ import lombok.Getter;
import net.runelite.api.AnimationID; import net.runelite.api.AnimationID;
import net.runelite.api.ProjectileID; import net.runelite.api.ProjectileID;
import net.runelite.api.SpriteID; import net.runelite.api.SpriteID;
import net.runelite.api.coords.WorldPoint;
enum HydraPhase enum HydraPhase
{ { // Sorry for the autism
ONE(3, AnimationID.HYDRA_1_1, AnimationID.HYDRA_1_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL), ONE (3, AnimationID.HYDRA_1_1, AnimationID.HYDRA_1_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL, new WorldPoint(1371, 10263, 0)),
TWO(3, AnimationID.HYDRA_2_1, AnimationID.HYDRA_2_2, 0, AnimationID.HYDRA_LIGHTNING, SpriteID.BIG_SPEC_TRANSFER), TWO (3, AnimationID.HYDRA_2_1, AnimationID.HYDRA_2_2, 0, AnimationID.HYDRA_LIGHTNING, SpriteID.BIG_SPEC_TRANSFER, new WorldPoint(1371, 10272, 0)),
THREE(3, AnimationID.HYDRA_3_1, AnimationID.HYDRA_3_2, 0, AnimationID.HYDRA_FIRE, SpriteID.BIG_SUPERHEAT), THREE (3, AnimationID.HYDRA_3_1, AnimationID.HYDRA_3_2, 0, AnimationID.HYDRA_FIRE, SpriteID.BIG_SUPERHEAT, new WorldPoint(1362, 10272, 0)),
FOUR(1, AnimationID.HYDRA_4_1, AnimationID.HYDRA_4_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL); FOUR (1, AnimationID.HYDRA_4_1, AnimationID.HYDRA_4_2, ProjectileID.HYDRA_POISON, 0, SpriteID.BIG_ASS_GUTHIX_SPELL, null);
@Getter @Getter
private int attacksPerSwitch; private final int attacksPerSwitch;
@Getter @Getter
private int deathAnim1; private final int deathAnim1;
@Getter @Getter
private int deathAnim2; private final int deathAnim2;
@Getter @Getter
private int specProjectileId; private final int specProjectileId;
@Getter @Getter
private int specAnimationId; private final int specAnimationId;
@Getter @Getter
private int specImage; private final int specImage;
HydraPhase(int attacksPerSwitch, int deathAnim1, int deathAnim2, int specProjectileId, int specAnimationId, int specImage) @Getter
private WorldPoint fountain;
HydraPhase(int attacksPerSwitch, int deathAnim1, int deathAnim2, int specProjectileId, int specAnimationId, int specImage, WorldPoint fountain)
{ {
this.attacksPerSwitch = attacksPerSwitch; this.attacksPerSwitch = attacksPerSwitch;
this.deathAnim1 = deathAnim1; this.deathAnim1 = deathAnim1;
@@ -62,5 +66,6 @@ enum HydraPhase
this.specProjectileId = specProjectileId; this.specProjectileId = specProjectileId;
this.specAnimationId = specAnimationId; this.specAnimationId = specAnimationId;
this.specImage = specImage; this.specImage = specImage;
this.fountain = fountain;
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, https://runelitepl.us * Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -25,17 +25,22 @@
package net.runelite.client.plugins.alchemicalhydra; package net.runelite.client.plugins.alchemicalhydra;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject; import javax.inject.Inject;
import lombok.Getter; import lombok.Getter;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import net.runelite.api.Actor; import net.runelite.api.Actor;
import net.runelite.api.Client; import net.runelite.api.Client;
import net.runelite.api.GameState; import net.runelite.api.GameState;
import net.runelite.api.NPC;
import net.runelite.api.NpcID; import net.runelite.api.NpcID;
import net.runelite.api.Projectile; import net.runelite.api.Projectile;
import net.runelite.api.coords.LocalPoint; import net.runelite.api.coords.LocalPoint;
import net.runelite.api.events.AnimationChanged; import net.runelite.api.events.AnimationChanged;
import net.runelite.api.events.ChatMessage;
import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.GameStateChanged;
import net.runelite.api.events.NpcSpawned; import net.runelite.api.events.NpcSpawned;
import net.runelite.api.events.ProjectileMoved; import net.runelite.api.events.ProjectileMoved;
@@ -55,14 +60,13 @@ import net.runelite.client.ui.overlay.OverlayManager;
public class HydraPlugin extends Plugin public class HydraPlugin extends Plugin
{ {
@Getter @Getter
private HashSet<LocalPoint> poisonPoints = new HashSet<>(); private Map<LocalPoint, Projectile> poisonProjectiles = new HashMap<>();
@Getter @Getter
private Hydra hydra; private Hydra hydra;
private boolean inHydraInstance; private boolean inHydraInstance;
private int lastAttackTick; private int lastAttackTick;
private int lastPoisonTick;
private static final int[] HYDRA_REGIONS = { private static final int[] HYDRA_REGIONS = {
5279, 5280, 5279, 5280,
@@ -86,7 +90,7 @@ public class HydraPlugin extends Plugin
{ {
inHydraInstance = checkArea(); inHydraInstance = checkArea();
lastAttackTick = -1; lastAttackTick = -1;
poisonPoints.clear(); poisonProjectiles.clear();
} }
@Override @Override
@@ -94,7 +98,7 @@ public class HydraPlugin extends Plugin
{ {
inHydraInstance = false; inHydraInstance = false;
hydra = null; hydra = null;
poisonPoints.clear(); poisonProjectiles.clear();
removeOverlays(); removeOverlays();
lastAttackTick = -1; lastAttackTick = -1;
} }
@@ -109,18 +113,29 @@ public class HydraPlugin extends Plugin
inHydraInstance = checkArea(); inHydraInstance = checkArea();
if (inHydraInstance) if (!inHydraInstance)
{ {
hydra = new Hydra();
log.debug("Entered hydra instance"); if (hydra != null)
addOverlays();
}
else if (hydra != null)
{ {
removeOverlays(); removeOverlays();
hydra = null; hydra = null;
log.debug("Left hydra instance"); }
return;
} }
NPC hydraNpc = null;
for (NPC npc : client.getNpcs())
{
if (npc.getId() == NpcID.ALCHEMICAL_HYDRA)
{
hydraNpc = npc;
break;
}
}
hydra = new Hydra(hydraNpc);
addOverlays();
} }
@Subscribe @Subscribe
@@ -131,8 +146,7 @@ public class HydraPlugin extends Plugin
return; return;
} }
hydra = new Hydra(); hydra = new Hydra(event.getNpc());
log.debug("Hydra spawned");
addOverlays(); addOverlays();
} }
@@ -148,47 +162,56 @@ public class HydraPlugin extends Plugin
HydraPhase phase = hydra.getPhase(); HydraPhase phase = hydra.getPhase();
// Using the first animation sometimes fucks shit up, so just use 2 if (actor.getAnimation() == phase.getDeathAnim2() &&
if ( /* actor.getAnimation() == phase.getDeathAnim1() || */ actor.getAnimation() == phase.getDeathAnim2()) phase != HydraPhase.THREE // Else log's gonna say "Tried some weird shit"
|| actor.getAnimation() == phase.getDeathAnim1() &&
phase == HydraPhase.THREE) // We want the pray to switch ye ok ty
{ {
switch (phase) switch (phase)
{ {
case ONE: case ONE:
changePhase(HydraPhase.TWO); changePhase(HydraPhase.TWO);
log.debug("Hydra phase 2"); hydra.setWeakened(false);
return; return;
case TWO: case TWO:
changePhase(HydraPhase.THREE); changePhase(HydraPhase.THREE);
log.debug("Hydra phase 3"); hydra.setWeakened(false);
return; return;
case THREE: case THREE:
changePhase(HydraPhase.FOUR); changePhase(HydraPhase.FOUR);
log.debug("Hydra phase 4");
return; return;
case FOUR: case FOUR:
hydra = null; hydra = null;
poisonPoints.clear(); poisonProjectiles.clear();
log.debug("Hydra dead");
removeOverlays(); removeOverlays();
return; return;
default: default:
log.debug("Tried some weird shit"); log.debug("Tried some weird shit");
break; break;
} }
if (actor.getAnimation() == phase.getDeathAnim1() && phase == HydraPhase.THREE)
{
changePhase(HydraPhase.FOUR);
} }
}
else if (actor.getAnimation() == phase.getSpecAnimationId() && phase.getSpecAnimationId() != 0) else if (actor.getAnimation() == phase.getSpecAnimationId() && phase.getSpecAnimationId() != 0)
{ {
hydra.setNextSpecial(hydra.getNextSpecial() + 9); hydra.setNextSpecial(hydra.getNextSpecial() + 9);
} }
if (!poisonPoints.isEmpty() && lastPoisonTick + 10 < client.getTickCount()) if (poisonProjectiles.isEmpty())
{ {
poisonPoints.clear(); return;
}
Set<LocalPoint> exPoisonProjectiles = new HashSet<>();
for (Map.Entry<LocalPoint, Projectile> entry : poisonProjectiles.entrySet())
{
if (entry.getValue().getEndCycle() < client.getGameCycle())
{
exPoisonProjectiles.add(entry.getKey());
}
}
for (LocalPoint toRemove : exPoisonProjectiles)
{
poisonProjectiles.remove(toRemove);
} }
} }
@@ -203,11 +226,11 @@ public class HydraPlugin extends Plugin
Projectile projectile = event.getProjectile(); Projectile projectile = event.getProjectile();
int id = projectile.getId(); int id = projectile.getId();
if (hydra.getPhase().getSpecProjectileId() != 0 && hydra.getPhase().getSpecProjectileId() == id) if (hydra.getPhase().getSpecProjectileId() != 0 && hydra.getPhase().getSpecProjectileId() == id)
{ {
poisonPoints.add(event.getPosition()); poisonProjectiles.put(event.getPosition(), projectile);
hydra.setNextSpecial(hydra.getNextSpecial() + 9); hydra.setNextSpecial(hydra.getNextSpecial() + 9);
lastPoisonTick = client.getTickCount();
} }
else if (client.getTickCount() != lastAttackTick else if (client.getTickCount() != lastAttackTick
&& (id == Hydra.AttackStyle.MAGIC.getProjId() || id == Hydra.AttackStyle.RANGED.getProjId())) && (id == Hydra.AttackStyle.MAGIC.getProjId() || id == Hydra.AttackStyle.RANGED.getProjId()))
@@ -217,6 +240,17 @@ public class HydraPlugin extends Plugin
} }
} }
@Subscribe
public void onChatMessage(ChatMessage event)
{
if (!event.getMessage().equals("The chemicals neutralise the Alchemical Hydra's defences!"))
{
return;
}
hydra.setWeakened(true);
}
private boolean checkArea() private boolean checkArea()
{ {
return Arrays.equals(client.getMapRegions(), HYDRA_REGIONS) && client.isInInstancedRegion(); return Arrays.equals(client.getMapRegions(), HYDRA_REGIONS) && client.isInInstancedRegion();
@@ -255,15 +289,25 @@ public class HydraPlugin extends Plugin
private void handleAttack(int id) private void handleAttack(int id)
{ {
hydra.setNextSwitch(hydra.getNextSwitch() - 1); if (id != hydra.getNextAttack().getProjId() && id != hydra.getLastAttack().getProjId())
hydra.setAttackCount(hydra.getAttackCount() + 1); { // If the current attack isn't what was expected and we should have switched prayers
hydra.setLastAttack(hydra.getNextAttack());
if (id != hydra.getNextAttack().getProjId())
{
switchStyles(); switchStyles();
hydra.setNextSwitch(hydra.getPhase().getAttacksPerSwitch() - 1);
hydra.setLastAttack(hydra.getNextAttack());
} }
else if (hydra.getNextSwitch() <= 0) else if (id != hydra.getNextAttack().getProjId() && id == hydra.getLastAttack().getProjId())
{ // If the current attack isn't what was expected and we accidentally counted 1 too much
return;
}
else
{
hydra.setNextSwitch(hydra.getNextSwitch() - 1);
hydra.setLastAttack(hydra.getNextAttack());
}
hydra.setAttackCount(hydra.getAttackCount() + 1);
if (hydra.getNextSwitch() <= 0)
{ {
switchStyles(); switchStyles();
hydra.setNextSwitch(hydra.getPhase().getAttacksPerSwitch()); hydra.setNextSwitch(hydra.getPhase().getAttacksPerSwitch());

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2018, https://runelitepl.us * Copyright (c) 2019, Lucas <https://github.com/lucwousin>
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
@@ -29,12 +29,16 @@ import java.awt.Dimension;
import java.awt.Graphics2D; import java.awt.Graphics2D;
import java.awt.Polygon; import java.awt.Polygon;
import java.awt.geom.Area; import java.awt.geom.Area;
import java.util.HashSet; import java.util.Collection;
import java.util.Map;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
import net.runelite.api.Client; import net.runelite.api.Client;
import static net.runelite.api.Perspective.getCanvasTileAreaPoly; import static net.runelite.api.Perspective.getCanvasTileAreaPoly;
import net.runelite.api.Projectile;
import net.runelite.api.coords.LocalPoint; 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.Overlay;
import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayLayer;
import net.runelite.client.ui.overlay.OverlayPosition; import net.runelite.client.ui.overlay.OverlayPosition;
@@ -57,29 +61,85 @@ class HydraPoisonOverlay extends Overlay
@Override @Override
public Dimension render(Graphics2D graphics) public Dimension render(Graphics2D graphics)
{ {
final HashSet<LocalPoint> initialPoints = plugin.getPoisonPoints(); Hydra hydra = plugin.getHydra();
Area poisonTiles = new Area(); final Map<LocalPoint, Projectile> poisonProjectiles = plugin.getPoisonProjectiles();
for (LocalPoint point : initialPoints)
{
Polygon poly = getCanvasTileAreaPoly(client, point, 3);
if (poly == null)
{
break;
}
poisonTiles.add(new Area(poly)); if (!poisonProjectiles.isEmpty())
{
drawPoisonArea(graphics, poisonProjectiles);
} }
if (poisonTiles.isEmpty()) if (hydra.getPhase().getFountain() != null)
{ {
return null; drawFountain(graphics, hydra);
} }
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; return null;
} }
private void drawPoisonArea(Graphics2D graphics, Map<LocalPoint, Projectile> poisonProjectiles)
{
Area poisonTiles = new Area();
for (Map.Entry<LocalPoint, Projectile> entry : poisonProjectiles.entrySet())
{
if (entry.getValue().getEndCycle() < client.getGameCycle())
{
continue;
}
LocalPoint point = entry.getKey();
Polygon poly = getCanvasTileAreaPoly(client, point, 3);
if (poly != null)
{
poisonTiles.add(new Area(poly));
}
}
graphics.setPaintMode();
graphics.setColor(new Color(255, 0, 0, 100));
graphics.draw(poisonTiles);
graphics.setColor(new Color(255, 0, 0, 50));
graphics.fill(poisonTiles);
}
private void drawFountain(Graphics2D graphics, Hydra hydra)
{
Collection<WorldPoint> fountainWorldPoint = WorldPoint.toLocalInstance(client, hydra.getPhase().getFountain()); // thanks
if (fountainWorldPoint.size() > 1) // for
{
return;
}
WorldPoint wp = null;
for (WorldPoint p : fountainWorldPoint) // this
{
wp = p;
}
LocalPoint fountainPoint = wp == null ? null : LocalPoint.fromWorld(client, wp); // trash
if (fountainPoint == null || hydra.isWeakened()) // I
{
return;
}
final Polygon poly = getCanvasTileAreaPoly(client, fountainPoint, 3); // don't
if (poly == null)
{
return;
}
Color color = new Color(255, 0, 0, 100); // like
if (hydra.getNpc().getWorldArea().intersectsWith(new WorldArea(wp.getX() - 1, wp.getY() - 1, 3, 3, wp.getPlane()))) // coords
{ // WHICH FUCKING RETARD DID X, Y, dX, dY, Z???? IT'S XYZdXdY REEEEEEEEEE
color = new Color(0, 255, 0, 100);
}
graphics.setColor(color);
graphics.draw(poly);
}
} }