Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Arcadion initial #522

Merged
merged 14 commits into from
Jul 19, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
package gg.xp.xivsupport.triggers.Arcadion;

import gg.xp.reevent.events.BaseEvent;
import gg.xp.reevent.events.EventContext;
import gg.xp.reevent.scan.AutoChildEventHandler;
import gg.xp.reevent.scan.AutoFeed;
import gg.xp.reevent.scan.FilteredEventHandler;
import gg.xp.xivdata.data.duties.*;
import gg.xp.xivsupport.callouts.CalloutRepo;
import gg.xp.xivsupport.callouts.ModifiableCallout;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.events.actlines.events.AbilityUsedEvent;
import gg.xp.xivsupport.events.actlines.events.SnapshotLocationDataEvent;
import gg.xp.xivsupport.events.state.XivState;
import gg.xp.xivsupport.events.triggers.seq.SequentialTrigger;
import gg.xp.xivsupport.events.triggers.seq.SqtTemplates;
import gg.xp.xivsupport.events.triggers.support.NpcCastCallout;
import gg.xp.xivsupport.models.Position;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;

@CalloutRepo(name = "M1N", duty = KnownDuty.M1N)
public class M1N extends AutoChildEventHandler implements FilteredEventHandler {
public static final Logger log = LoggerFactory.getLogger(M1N.class);

@NpcCastCallout(0x9309)
private final ModifiableCallout<AbilityCastStart> oneTwoPawEastWest = ModifiableCallout.durationBasedCall("One-Two Paw: West safe", "West then East");
@NpcCastCallout(0x930C)
private final ModifiableCallout<AbilityCastStart> oneTwoPawWestEast = ModifiableCallout.durationBasedCall("One-Two Paw: East safe", "East then West");
@NpcCastCallout(0x0)
private final ModifiableCallout<AbilityCastStart> blackCatCrossingCardInter = ModifiableCallout.durationBasedCall("Black Cat Crossing: Inter safe", "Intercardinals then cardinals");
@NpcCastCallout(0x930F)
private final ModifiableCallout<AbilityCastStart> blackCatCrossingInterCard = ModifiableCallout.durationBasedCall("Black Cat Crossing: Card safe", "Cardinals then intercardinals");
@NpcCastCallout({0x9321, 0x931F})
private final ModifiableCallout<AbilityCastStart> leapingOneTwoPawEastWest = ModifiableCallout.durationBasedCall("Leaping One-Two Paw: West safe", "West then East");
@NpcCastCallout({0x9320, 0x9322}) //dont know what the difference is (maybe leap direction?)
private final ModifiableCallout<AbilityCastStart> leapingOneTwoPawWestEast = ModifiableCallout.durationBasedCall("Leaping One-Two Paw: East safe", "East then West");
@NpcCastCallout(0x0) //TODO: confirm these
private final ModifiableCallout<AbilityCastStart> leapingBlackCatCrossingCardInter = ModifiableCallout.durationBasedCall("Leaping Black Cat Crossing: Inter safe", "Inter then Cardinal");
@NpcCastCallout(0x9329)
private final ModifiableCallout<AbilityCastStart> leapingBlackCatCrossingInterCard = ModifiableCallout.durationBasedCall("Leaping Black Cat Crossing: Card safe", "Cardinal then inter");

public M1N(XivState state) {
this.state = state;
}
private final XivState state;

@Override
public boolean enabled(EventContext context) {
return state.dutyIs(KnownDuty.M1N);
}

private final ModifiableCallout<?> startSW = new ModifiableCallout<>("Mouser: Start SW", "Start South West");
private final ModifiableCallout<?> startSE = new ModifiableCallout<>("Mouser: Start SE", "Start South East");
private final ModifiableCallout<?> swSafe = new ModifiableCallout<>("Mouser: SW", "South West");
private final ModifiableCallout<?> seSafe = new ModifiableCallout<>("Mouser: SE", "South East");

private boolean isSouthernTile(Position pos) {
return pos.getY() > 100 //below the center horizontal
&& pos.getX() < 106 //clamps to middle two tiles
&& pos.getX() > 94;
}
private boolean isWestTile(Position pos) {
return pos.getX() < 100;
}

@AutoFeed
private final SequentialTrigger<BaseEvent> mouser = SqtTemplates.sq(20_000, AbilityCastStart.class,
acs -> acs.abilityIdMatches(0x9313),
(e1, s) -> {
log.info("Mouser: Start");
//check both damage hits, plus the shatter hit. may be double or single movement
List<AbilityUsedEvent> hits = s.waitEvents(3, AbilityUsedEvent.class, aue ->
aue.abilityIdMatches(0x9315, 0x996B)
&& isSouthernTile(aue.getTarget().getPos()));

Position hit1Pos = hits.get(0).getTarget().getPos();
Position hit2Pos = hits.get(1).getTarget().getPos();
Position hit3Pos = hits.get(2).getTarget().getPos();

//which tile was most recently hit
boolean wasWest = true;
//Starting position
if(isWestTile(hit1Pos))
s.updateCall(startSE);
else {
s.updateCall(startSW);
wasWest = false;
}

//Wait for each southern mouser cast
//hit 1
s.waitEvent(SnapshotLocationDataEvent.class, slde ->
slde.originalEvent().abilityIdMatches(0x9316, 0x94A5)
&& isSouthernTile(slde.getPos()));
//Call new if its different
if(isWestTile(hit2Pos) && !wasWest)
s.updateCall(seSafe);
else if(!isWestTile(hit2Pos) && wasWest)
s.updateCall(swSafe);

wasWest = isWestTile(hit2Pos);

//hit 2
s.waitEvent(SnapshotLocationDataEvent.class, slde ->
slde.originalEvent().abilityIdMatches(0x9316, 0x94A5)
&& isSouthernTile(slde.getPos()));
//Call new if its different
if(isWestTile(hit3Pos) && !wasWest)
s.updateCall(seSafe);
else if(!isWestTile(hit3Pos) && wasWest)
s.updateCall(swSafe);

//hit 3, dont need to move again
});
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package gg.xp.xivsupport.triggers.Arcadion;

import gg.xp.reevent.events.EventContext;
import gg.xp.reevent.scan.AutoChildEventHandler;
import gg.xp.reevent.scan.FilteredEventHandler;
import gg.xp.xivdata.data.duties.*;
import gg.xp.xivsupport.callouts.CalloutRepo;
import gg.xp.xivsupport.callouts.ModifiableCallout;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.events.state.XivState;
import gg.xp.xivsupport.events.triggers.support.NpcCastCallout;

@CalloutRepo(name = "M3N", duty = KnownDuty.M3N)
public class M3N extends AutoChildEventHandler implements FilteredEventHandler {

@NpcCastCallout(0x9AD4)
private final ModifiableCallout<AbilityCastStart> brutalLariatWest = ModifiableCallout.durationBasedCall("Brutal Lariat: East safe", "East");
@NpcCastCallout(0x9AD5)
private final ModifiableCallout<AbilityCastStart> brutalLariatEast = ModifiableCallout.durationBasedCall("Brutal Lariat: West safe", "West");
@NpcCastCallout(0x9ADC)
private final ModifiableCallout<AbilityCastStart> lariatComboWestEast = ModifiableCallout.durationBasedCall("Lariat Combo: East then west", "East then West");
@NpcCastCallout(0x9ADE)
private final ModifiableCallout<AbilityCastStart> lariatComboEastWest = ModifiableCallout.durationBasedCall("Lariat Combo: West then east", "West then East");
@NpcCastCallout(0x9ADD)
private final ModifiableCallout<AbilityCastStart> lariatComboWestWest = ModifiableCallout.durationBasedCall("Lariat Combo: East safe", "East, stay east");
@NpcCastCallout(0x9ADF)
private final ModifiableCallout<AbilityCastStart> lariatComboEastEast = ModifiableCallout.durationBasedCall("Lariat Combo: West safe", "West, stay west");

public M3N(XivState state) {
this.state = state;
}
private final XivState state;

@Override
public boolean enabled(EventContext context) {
return state.dutyIs(KnownDuty.M3N);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
package gg.xp.xivsupport.triggers.Arcadion;

import gg.xp.reevent.events.BaseEvent;
import gg.xp.reevent.events.EventContext;
import gg.xp.reevent.scan.AutoChildEventHandler;
import gg.xp.reevent.scan.AutoFeed;
import gg.xp.reevent.scan.FilteredEventHandler;
import gg.xp.xivdata.data.duties.*;
import gg.xp.xivsupport.callouts.CalloutRepo;
import gg.xp.xivsupport.callouts.ModifiableCallout;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.events.actlines.events.AbilityUsedEvent;
import gg.xp.xivsupport.events.actlines.events.BuffApplied;
import gg.xp.xivsupport.events.actlines.events.vfx.StatusLoopVfxApplied;
import gg.xp.xivsupport.events.state.XivState;
import gg.xp.xivsupport.events.triggers.seq.SequentialTrigger;
import gg.xp.xivsupport.events.triggers.seq.SqtTemplates;
import gg.xp.xivsupport.events.triggers.support.NpcAbilityUsedCallout;
import gg.xp.xivsupport.events.triggers.support.NpcCastCallout;
import gg.xp.xivsupport.models.ArenaPos;
import gg.xp.xivsupport.models.ArenaSector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.List;
import java.util.Map;

@CalloutRepo(name = "M4N", duty = KnownDuty.M4N)
public class M4N extends AutoChildEventHandler implements FilteredEventHandler {
public static final Logger log = LoggerFactory.getLogger(M4N.class);

@NpcCastCallout({0x92BD, 0x92BF}) //second ID is while wings are active
private final ModifiableCallout<AbilityCastStart> sidewiseSparkWest = ModifiableCallout.durationBasedCall("Sidewise Spark: East safe", "East safe");
@NpcCastCallout({0x92BC, 0x92BE})
private final ModifiableCallout<AbilityCastStart> sidewiseSparkEast = ModifiableCallout.durationBasedCall("Sidewise Spark: West safe", "West safe");
@NpcAbilityUsedCallout(0x92AB)
private final ModifiableCallout<AbilityUsedEvent> stampedingThunderWest = new ModifiableCallout<>("Stampeding Thunder: Go East", "East safe");
@NpcAbilityUsedCallout(0x92AC)
private final ModifiableCallout<AbilityUsedEvent> stampedingThunderEast = new ModifiableCallout<>("Stampeding Thunder: Go West", "West safe");

public M4N(XivState state) {
this.state = state;
}
private final XivState state;

@Override
public boolean enabled(EventContext context) {
return state.dutyIs(KnownDuty.M4N);
}

public static final List<Long> validStacks = List.of(723L,724L);

public ArenaSector getSafeDirectionForBA(BuffApplied ba) {
//723 hits north, go south
//724 hits south, go north
long stacks = ba.getRawStacks();
return stacks == 723 ? ArenaSector.SOUTH : ArenaSector.NORTH;
}

private final ModifiableCallout<?> gunStart = new ModifiableCallout<>("Gun Blasts: Start", "Start {safe}");
private final ModifiableCallout<?> gunSafe = new ModifiableCallout<>("Gun Blasts: Next Safe", "{safe}");

@AutoFeed
private final SequentialTrigger<BaseEvent> gunBlast = SqtTemplates.sq(30_000, AbilityCastStart.class,
acs -> acs.abilityIdMatches(0x92B0, 0x9B4F, 0x9B56, 0x92AD, 0x9B55, 0x9B57),
(e1, s) -> {
log.info("Gun Blasts: Start");
int iterations = switch((int)e1.getAbility().getId()) {
case 0x92B0, 0x92AD -> 2;
case 0x9B4F, 0x9B55 -> 3;
case 0x9B56, 0x9B57 -> 4;
default -> 0;
};

//Buff 0xB9A
ArenaSector buff1 = getSafeDirectionForBA(s.waitEvent(BuffApplied.class, ba -> validStacks.contains(ba.getRawStacks()) && ba.buffIdMatches(0xB9A)));
log.info("Gun Blasts Start: {}", buff1);
s.setParam("safe", buff1);
s.updateCall(gunStart);

List<BuffApplied> furtherBuffs = s.waitEvents(iterations, BuffApplied.class, ba -> validStacks.contains(ba.getRawStacks()) && ba.buffIdMatches(0xB9A));

boolean wasNorth = buff1 != ArenaSector.SOUTH;
//repeats up to 4 times
for(int i = 0; i < iterations; i++) {
//many IDs. Some may be missing. Ability name "Wicked Cannon"
//0x4E40, 0x9BAC, 0x92AE, 0x9BBE, 0x92AF
s.waitEvent(AbilityUsedEvent.class, aue -> aue.abilityIdMatches(0x4E40, 0x9BAC, 0x92AE, 0x9BBE, 0x92AF) && aue.isFirstTarget());
ArenaSector safeSide = getSafeDirectionForBA(furtherBuffs.get(i));
log.info("Gun Blasts {}: {}", i, safeSide);
if(wasNorth && safeSide == ArenaSector.SOUTH) {
s.setParam("safe", ArenaSector.SOUTH);
s.updateCall(gunSafe);
wasNorth = false;
}
else if(!wasNorth && safeSide == ArenaSector.NORTH) {
s.setParam("safe", ArenaSector.NORTH);
s.updateCall(gunSafe);
wasNorth = true;
}
}
});

public static final List<Long> validIDs = List.of(793L, 794L);

private final ArenaPos arenaPos = new ArenaPos(100, 100, 5, 5);

private final ModifiableCallout<?> shadowSafe = new ModifiableCallout<>("Shadow's Sabbath", "{safe} safe");

@AutoFeed
//TODO:these calls overlap slightly when it does four in a row, especially when she also does a cleave
private final SequentialTrigger<BaseEvent> shadowsSabbeth = SqtTemplates.sq(20_000, StatusLoopVfxApplied.class,
slva -> validIDs.contains(slva.getStatusLoopVfx().getId()),
(e1, s) -> {
ArenaSector addDir = arenaPos.forCombatant(e1.getTarget());
boolean hittingLeft = e1.getStatusLoopVfx().getId() == 794;
//794 left
//793 right
ArenaSector safeDir = switch(addDir) {
case SOUTH -> hittingLeft ? ArenaSector.EAST : ArenaSector.WEST;
case NORTH -> hittingLeft ? ArenaSector.WEST : ArenaSector.EAST;
case EAST -> hittingLeft ? ArenaSector.NORTH : ArenaSector.SOUTH;
case WEST -> hittingLeft ? ArenaSector.SOUTH : ArenaSector.NORTH;
default -> ArenaSector.UNKNOWN;
};

s.setParam("safe", safeDir);
s.updateCall(shadowSafe);
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@ public enum KnownDuty implements Duty {
DtEx1("EX1", 1196, Expansion.DT, DutyType.TRIAL_EX),
DtEx2("EX2", 1201, Expansion.DT, DutyType.TRIAL_EX),

M1N("M1N", 1225,Expansion.DT, DutyType.RAID),
M2N("M2N", 1227,Expansion.DT, DutyType.RAID),
M3N("M3N", 1229,Expansion.DT, DutyType.RAID),
M4N("M4N", 1231,Expansion.DT, DutyType.RAID),

;

private final String name;
Expand Down
Loading