Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
237 changes: 9 additions & 228 deletions src/main/java/io/blert/challenges/mokhaiotl/DelveDataTracker.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,9 @@

package io.blert.challenges.mokhaiotl;

import com.google.common.collect.ImmutableSet;
import io.blert.core.*;
import io.blert.events.NpcAttackEvent;
import io.blert.events.mokhaiotl.*;
import io.blert.util.ChatText;
import io.blert.util.Location;
import io.blert.util.Tick;
import java.util.*;
import java.util.regex.Matcher;
Expand All @@ -41,47 +38,13 @@

@Slf4j
public class DelveDataTracker extends DataTracker {
private static final int MOKHAIOTL_AUTO_ANIMATION_ID = 12406;
private static final int MOKHAIOTL_BALL_ANIMATION_ID = 12407;
private static final int MOKHAIOTL_CHARGE_ANIMATION_ID = 12409;
private static final int MOKHAIOTL_BLAST_ANIMATION_ID = 12411;
private static final int MOKHAIOTL_MELEE_ANIMATION_ID = 12416;
private static final int MOKHAIOTL_RACECAR_ANIMATION_ID = 12417;

private static final int MOKHAIOTL_RANGED_BALL_PROJECTILE_ID = 3384;
private static final int MOKHAIOTL_MAGE_BALL_PROJECTILE_ID = 3385;

private static final Set<Number> SHOCKWAVE_GRAPHICS_IDs = ImmutableSet.of(3405, 3406, 3407);

private static final int[] HITPOINTS_BY_DELVE = new int[] {
-1, // 1-indexed
525, // Delve 1
550, // Delve 2
575, // Delve 3
600, // Delve 4
625, // Delve 5
650, // Delve 6
650, // Delve 7
675, // Delve 8+
};
private static final int SHIELD_HITPOINTS = 500;

private enum PhaseChange {
NONE,
SHIELD_START,
SHIELD_END,
}
private static final Set<Integer> MOKHAIOTL_IDS = Set.of(14707, 14708, 14709);
private static final Set<Integer> LARVA_IDS = Set.of(14710, 14711, 14712, 14713, 14788, 14789);

private final Pattern delveEndRegex;
private final int delve;

private BasicTrackedNpc mokhaiotl = null;
PhaseChange phaseChange;
private int hitpointsBeforeShield;
private int unidentifiedAttackTick;
private int lastRacecarTick;
private final Map<Projectile, Orb> activeOrbs = new HashMap<>();
private boolean shockwaveThisTick;
private @Nullable NPC mokhaiotl = null;
private final Queue<Hitsplat> healsThisTick = new LinkedList<>();
private final Queue<TrackedNpc> larvaeLeakedThisTick = new LinkedList<>();

Expand All @@ -103,9 +66,6 @@ public DelveDataTracker(
this.delveEndRegex =
Pattern.compile("Delve level: " + delve + " duration: (" + Tick.TIME_STRING_REGEX + ")");
}
this.unidentifiedAttackTick = -1;
this.lastRacecarTick = -1;
this.phaseChange = PhaseChange.NONE;
}

protected void start() {
Expand All @@ -117,15 +77,6 @@ protected void start() {
protected void onTick() {
final int tick = getTick();

checkForNewOrbs(tick);

if (phaseChange == PhaseChange.SHIELD_START) {
hitpointsBeforeShield = mokhaiotl.getHitpoints().getCurrent();
mokhaiotl.setHitpoints(new Hitpoints(SHIELD_HITPOINTS));
} else if (phaseChange == PhaseChange.SHIELD_END) {
mokhaiotl.setHitpoints(new Hitpoints(hitpointsBeforeShield, HITPOINTS_BY_DELVE[Math.min(delve, 8)]));
}

while (!healsThisTick.isEmpty()) {
Hitsplat hitsplat = healsThisTick.remove();
TrackedNpc larva = larvaeLeakedThisTick.poll();
Expand All @@ -136,17 +87,8 @@ protected void onTick() {
dispatchEvent(new MokhaiotlLarvaLeakEvent(getStage(), tick, larva.getRoomId(), hitsplat.getAmount()));
}

if (shockwaveThisTick) {
boolean isSlam = mokhaiotl.getNpc().getId() == MokhaiotlNpc.MOKHAIOTL_BURROWED.getId()
|| lastRacecarTick >= tick - 12;
NpcAttack attack = isSlam ? NpcAttack.MOKHAIOTL_SLAM : NpcAttack.MOKHAIOTL_SHOCKWAVE;
dispatchEvent(new NpcAttackEvent(getStage(), tick, getWorldLocation(mokhaiotl), attack, mokhaiotl));
}

shockwaveThisTick = false;
healsThisTick.clear();
larvaeLeakedThisTick.clear();
phaseChange = PhaseChange.NONE;
}

@Override
Expand All @@ -168,25 +110,15 @@ protected void onMessage(ChatMessage event) {
@Override
protected Optional<? extends TrackedNpc> onNpcSpawn(NpcSpawned event) {
NPC npc = event.getNpc();
Optional<MokhaiotlNpc> maybeNpc = MokhaiotlNpc.withId(npc.getId());
if (maybeNpc.isEmpty()) {
return Optional.empty();
}

MokhaiotlNpc mokhaiotlNpc = maybeNpc.get();
if (mokhaiotlNpc.isMokhaiotl()) {
if (MOKHAIOTL_IDS.contains(npc.getId())) {
if (getState() == State.NOT_STARTED) {
start();
}

if (mokhaiotl == null) {
int hitpoints = HITPOINTS_BY_DELVE[Math.min(delve, 8)];
mokhaiotl = new BasicTrackedNpc(npc, generateRoomId(npc), new Hitpoints(hitpoints));
}
return Optional.of(mokhaiotl);
mokhaiotl = npc;
}

return Optional.of(new BasicTrackedNpc(npc, generateRoomId(npc), new Hitpoints(mokhaiotlNpc.getHitpoints())));
return Optional.empty();
}

@Override
Expand All @@ -195,16 +127,10 @@ protected boolean onNpcDespawn(NpcDespawned event, @Nullable TrackedNpc trackedN
return false;
}

Optional<MokhaiotlNpc> maybeNpc = MokhaiotlNpc.withId(event.getNpc().getId());
if (maybeNpc.isEmpty()) {
return false;
}

MokhaiotlNpc mokhaiotlNpc = maybeNpc.get();
if (mokhaiotlNpc.isLarva() && mokhaiotl != null && mokhaiotl.getNpc() != null) {
if (LARVA_IDS.contains(event.getNpc().getId()) && mokhaiotl != null) {
NPC larva = trackedNpc.getNpc();
WorldPoint mokhaiotlSouthwest = getWorldLocation(mokhaiotl);
int mokhaiotlSize = mokhaiotl.getNpc().getComposition().getSize();
int mokhaiotlSize = mokhaiotl.getComposition().getSize();
WorldPoint mokhaiotlCenter = new WorldPoint(
mokhaiotlSouthwest.getX() + mokhaiotlSize / 2,
mokhaiotlSouthwest.getY() + mokhaiotlSize / 2,
Expand All @@ -218,27 +144,9 @@ protected boolean onNpcDespawn(NpcDespawned event, @Nullable TrackedNpc trackedN
return true;
}

@Override
protected void onNpcChange(NpcChanged event) {
Optional<MokhaiotlNpc> maybeBefore = MokhaiotlNpc.withId(event.getOld().getId());
Optional<MokhaiotlNpc> maybeAfter = MokhaiotlNpc.withId(event.getNpc().getId());
if (maybeBefore.isEmpty() || maybeAfter.isEmpty()) {
return;
}

MokhaiotlNpc before = maybeBefore.get();
MokhaiotlNpc after = maybeAfter.get();

if (before == MokhaiotlNpc.MOKHAIOTL && after == MokhaiotlNpc.MOKHAIOTL_SHIELDED) {
phaseChange = PhaseChange.SHIELD_START;
} else if (before == MokhaiotlNpc.MOKHAIOTL_SHIELDED) {
phaseChange = PhaseChange.SHIELD_END;
}
}

@Override
protected void onHitsplat(HitsplatApplied hitsplatApplied) {
if (hitsplatApplied.getActor() != mokhaiotl.getNpc()) {
if (hitsplatApplied.getActor() != mokhaiotl) {
return;
}

Expand All @@ -247,131 +155,4 @@ protected void onHitsplat(HitsplatApplied hitsplatApplied) {
healsThisTick.add(hitsplat);
}
}

@Override
protected void onAnimation(AnimationChanged event) {
if (event.getActor() != mokhaiotl.getNpc()) {
return;
}

final int tick = getTick();
NpcAttack attack;

switch (event.getActor().getAnimation()) {
case MOKHAIOTL_AUTO_ANIMATION_ID:
attack = NpcAttack.MOKHAIOTL_AUTO;
unidentifiedAttackTick = tick;
break;
case MOKHAIOTL_BALL_ANIMATION_ID:
attack = NpcAttack.MOKHAIOTL_BALL;
unidentifiedAttackTick = tick;
break;
case MOKHAIOTL_CHARGE_ANIMATION_ID:
attack = NpcAttack.MOKHAIOTL_CHARGE;
break;
case MOKHAIOTL_MELEE_ANIMATION_ID:
attack = NpcAttack.MOKHAIOTL_MELEE;
break;
case MOKHAIOTL_BLAST_ANIMATION_ID:
attack = NpcAttack.MOKHAIOTL_BLAST;
break;
case MOKHAIOTL_RACECAR_ANIMATION_ID:
if (lastRacecarTick > tick - 5) {
return;
}
attack = NpcAttack.MOKHAIOTL_RACECAR;
lastRacecarTick = tick;
break;
default:
log.debug("Unhandled Mokhaiotl animation: {}", event.getActor().getAnimation());
return;
}

WorldPoint location = getWorldLocation(mokhaiotl);
dispatchEvent(new NpcAttackEvent(getStage(), tick, location, attack, mokhaiotl));
}

@Override
protected void onProjectile(ProjectileMoved event) {
Projectile projectile = event.getProjectile();
int elapsed = projectile.getEndCycle() - projectile.getStartCycle() - projectile.getRemainingCycles();

MokhaiotlAttackStyleEvent.Style style;

switch (projectile.getId()) {
case Orb.MELEE_PROJECTILE_ID:
style = MokhaiotlAttackStyleEvent.Style.MELEE;
break;
case Orb.RANGED_PROJECTILE_ID:
case MOKHAIOTL_RANGED_BALL_PROJECTILE_ID:
style = MokhaiotlAttackStyleEvent.Style.RANGED;
break;
case Orb.MAGE_PROJECTILE_ID:
case MOKHAIOTL_MAGE_BALL_PROJECTILE_ID:
style = MokhaiotlAttackStyleEvent.Style.MAGE;
break;
default:
return;
}

if (elapsed == 0 && mokhaiotl != null && mokhaiotl.getNpc() != null) {
boolean fromMokhaiotl = mokhaiotl.getNpc().getWorldArea().contains(projectile.getSourcePoint());

if (fromMokhaiotl && unidentifiedAttackTick != -1) {
dispatchEvent(new MokhaiotlAttackStyleEvent(getStage(), getTick(), style, unidentifiedAttackTick));
unidentifiedAttackTick = -1;
}
} else if (projectile.getRemainingCycles() <= 3) {
Orb orb = activeOrbs.get(projectile);
if (orb != null && orb.isActive()) {
orb.setLanded(getTick());
WorldPoint sourcePoint = Location.getWorldLocation(client, projectile.getSourcePoint());
dispatchEvent(new MokhaiotlOrbEvent(getStage(), orb.getLandedTick(), orb, sourcePoint));
}
}
}

@Override
protected void onGraphicsObjectCreation(GraphicsObjectCreated event) {
if (SHOCKWAVE_GRAPHICS_IDs.contains(event.getGraphicsObject().getId())) {
shockwaveThisTick = true;
}
}

@Override
protected boolean npcIgnoresCooldown(NPC npc) {
return MokhaiotlNpc.withId(npc.getId())
.map(m -> {
if (m.isLarva() || m == MokhaiotlNpc.VOLATILE_EARTH) {
return true;
}

if (m == MokhaiotlNpc.MOKHAIOTL && mokhaiotl != null && mokhaiotl.getNpc() == npc) {
// Charging regular (non-shielded) Mokhaiotl can be interrupted off cooldown.
return npc.getAnimation() == MOKHAIOTL_CHARGE_ANIMATION_ID;
}

return false;
})
.orElse(false);
}

void checkForNewOrbs(int tick) {
if (mokhaiotl == null) {
return;
}

for (Projectile projectile : client.getProjectiles()) {
if (!Orb.isOrb(projectile.getId())) {
continue;
}

if (!activeOrbs.containsKey(projectile)) {
boolean fromMokhaiotl = mokhaiotl.getNpc().getWorldArea().contains(projectile.getSourcePoint());

Orb.Source source = fromMokhaiotl ? Orb.Source.MOKHAIOTL : Orb.Source.BALL;
activeOrbs.put(projectile, new Orb(projectile, source, tick));
}
}
}
}
76 changes: 0 additions & 76 deletions src/main/java/io/blert/challenges/mokhaiotl/MokhaiotlNpc.java

This file was deleted.

Loading
Loading