diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/Hydra.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/Hydra.java index 24e2845792..ff979672a2 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/Hydra.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/Hydra.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2019, Lucas * All rights reserved. * * 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 lombok.Getter; import lombok.Setter; +import net.runelite.api.NPC; import net.runelite.api.Prayer; import net.runelite.api.ProjectileID; @@ -51,6 +52,9 @@ class Hydra } } + @Getter + private NPC npc; + @Getter @Setter private HydraPhase phase; @@ -75,12 +79,19 @@ class Hydra @Setter private AttackStyle lastAttack; - Hydra() + @Getter + @Setter + private boolean weakened; + + Hydra(NPC npc) { + this.npc = npc; this.phase = HydraPhase.ONE; this.nextAttack = AttackStyle.MAGIC; + this.lastAttack = AttackStyle.MAGIC; // important, else we wouldn't switch if the first attack is ranged this.nextSpecial = 3; this.nextSwitch = phase.getAttacksPerSwitch(); this.attackCount = 0; + this.weakened = false; } } diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraOverlay.java index 89610cc6f9..9f09b5a3da 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraOverlay.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2019, Lucas * All rights reserved. * * Redistribution and use in source and binary forms, with or without diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPhase.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPhase.java index 92418d9f13..471e8517fb 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPhase.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPhase.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2019, Lucas * All rights reserved. * * 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.ProjectileID; import net.runelite.api.SpriteID; +import net.runelite.api.coords.WorldPoint; 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); +{ // Sorry for the autism + 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, new WorldPoint(1371, 10272, 0)), + 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, null); @Getter - private int attacksPerSwitch; + private final int attacksPerSwitch; @Getter - private int deathAnim1; + private final int deathAnim1; @Getter - private int deathAnim2; + private final int deathAnim2; @Getter - private int specProjectileId; + private final int specProjectileId; @Getter - private int specAnimationId; + private final int specAnimationId; @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.deathAnim1 = deathAnim1; @@ -62,5 +66,6 @@ enum HydraPhase this.specProjectileId = specProjectileId; this.specAnimationId = specAnimationId; this.specImage = specImage; + this.fountain = fountain; } } \ No newline at end of file diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPlugin.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPlugin.java index ece901bcea..515877684a 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPlugin.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPlugin.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2019, Lucas * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,17 +25,22 @@ package net.runelite.client.plugins.alchemicalhydra; import java.util.Arrays; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.Set; 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.NPC; 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.ChatMessage; import net.runelite.api.events.GameStateChanged; import net.runelite.api.events.NpcSpawned; import net.runelite.api.events.ProjectileMoved; @@ -55,14 +60,13 @@ import net.runelite.client.ui.overlay.OverlayManager; public class HydraPlugin extends Plugin { @Getter - private HashSet poisonPoints = new HashSet<>(); + private Map poisonProjectiles = new HashMap<>(); @Getter private Hydra hydra; private boolean inHydraInstance; private int lastAttackTick; - private int lastPoisonTick; private static final int[] HYDRA_REGIONS = { 5279, 5280, @@ -86,7 +90,7 @@ public class HydraPlugin extends Plugin { inHydraInstance = checkArea(); lastAttackTick = -1; - poisonPoints.clear(); + poisonProjectiles.clear(); } @Override @@ -94,7 +98,7 @@ public class HydraPlugin extends Plugin { inHydraInstance = false; hydra = null; - poisonPoints.clear(); + poisonProjectiles.clear(); removeOverlays(); lastAttackTick = -1; } @@ -109,18 +113,29 @@ public class HydraPlugin extends Plugin inHydraInstance = checkArea(); - if (inHydraInstance) + if (!inHydraInstance) { - hydra = new Hydra(); - log.debug("Entered hydra instance"); - addOverlays(); - } - else if (hydra != null) + + if (hydra != null) { removeOverlays(); 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 @@ -131,8 +146,7 @@ public class HydraPlugin extends Plugin return; } - hydra = new Hydra(); - log.debug("Hydra spawned"); + hydra = new Hydra(event.getNpc()); addOverlays(); } @@ -148,47 +162,56 @@ public class HydraPlugin extends Plugin 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()) + if (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) { case ONE: changePhase(HydraPhase.TWO); - log.debug("Hydra phase 2"); + hydra.setWeakened(false); return; case TWO: changePhase(HydraPhase.THREE); - log.debug("Hydra phase 3"); + hydra.setWeakened(false); return; case THREE: changePhase(HydraPhase.FOUR); - log.debug("Hydra phase 4"); return; case FOUR: hydra = null; - poisonPoints.clear(); - log.debug("Hydra dead"); + poisonProjectiles.clear(); 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()) + if (poisonProjectiles.isEmpty()) { - poisonPoints.clear(); + return; + } + + Set exPoisonProjectiles = new HashSet<>(); + for (Map.Entry 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(); int id = projectile.getId(); + if (hydra.getPhase().getSpecProjectileId() != 0 && hydra.getPhase().getSpecProjectileId() == id) { - poisonPoints.add(event.getPosition()); + poisonProjectiles.put(event.getPosition(), projectile); hydra.setNextSpecial(hydra.getNextSpecial() + 9); - lastPoisonTick = client.getTickCount(); } else if (client.getTickCount() != lastAttackTick && (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() { return Arrays.equals(client.getMapRegions(), HYDRA_REGIONS) && client.isInInstancedRegion(); @@ -255,15 +289,25 @@ public class HydraPlugin extends Plugin private void handleAttack(int id) { - hydra.setNextSwitch(hydra.getNextSwitch() - 1); - hydra.setAttackCount(hydra.getAttackCount() + 1); - hydra.setLastAttack(hydra.getNextAttack()); - - if (id != hydra.getNextAttack().getProjId()) - { + if (id != hydra.getNextAttack().getProjId() && id != hydra.getLastAttack().getProjId()) + { // If the current attack isn't what was expected and we should have switched prayers 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(); hydra.setNextSwitch(hydra.getPhase().getAttacksPerSwitch()); diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPoisonOverlay.java b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPoisonOverlay.java index ea5fb6cb66..1158634635 100644 --- a/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPoisonOverlay.java +++ b/runelite-client/src/main/java/net/runelite/client/plugins/alchemicalhydra/HydraPoisonOverlay.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018, https://runelitepl.us + * Copyright (c) 2019, Lucas * All rights reserved. * * 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.Polygon; import java.awt.geom.Area; -import java.util.HashSet; +import java.util.Collection; +import java.util.Map; import javax.inject.Inject; import javax.inject.Singleton; import net.runelite.api.Client; import static net.runelite.api.Perspective.getCanvasTileAreaPoly; +import net.runelite.api.Projectile; import net.runelite.api.coords.LocalPoint; +import net.runelite.api.coords.WorldArea; +import net.runelite.api.coords.WorldPoint; import net.runelite.client.ui.overlay.Overlay; import net.runelite.client.ui.overlay.OverlayLayer; import net.runelite.client.ui.overlay.OverlayPosition; @@ -57,29 +61,85 @@ class HydraPoisonOverlay extends Overlay @Override public Dimension render(Graphics2D graphics) { - final HashSet initialPoints = plugin.getPoisonPoints(); - Area poisonTiles = new Area(); - for (LocalPoint point : initialPoints) - { - Polygon poly = getCanvasTileAreaPoly(client, point, 3); - if (poly == null) - { - break; - } + Hydra hydra = plugin.getHydra(); + final Map poisonProjectiles = plugin.getPoisonProjectiles(); - 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; } + + private void drawPoisonArea(Graphics2D graphics, Map poisonProjectiles) + { + Area poisonTiles = new Area(); + + for (Map.Entry 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 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); + } } \ No newline at end of file