diff --git a/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M1N.java b/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M1N.java new file mode 100644 index 000000000000..8bb3a5b69eb4 --- /dev/null +++ b/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M1N.java @@ -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 oneTwoPawEastWest = ModifiableCallout.durationBasedCall("One-Two Paw: West safe", "West then East"); + @NpcCastCallout(0x930C) + private final ModifiableCallout oneTwoPawWestEast = ModifiableCallout.durationBasedCall("One-Two Paw: East safe", "East then West"); + @NpcCastCallout(0x0) + private final ModifiableCallout blackCatCrossingCardInter = ModifiableCallout.durationBasedCall("Black Cat Crossing: Inter safe", "Intercardinals then cardinals"); + @NpcCastCallout(0x930F) + private final ModifiableCallout blackCatCrossingInterCard = ModifiableCallout.durationBasedCall("Black Cat Crossing: Card safe", "Cardinals then intercardinals"); + @NpcCastCallout({0x9321, 0x931F}) + private final ModifiableCallout 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 leapingOneTwoPawWestEast = ModifiableCallout.durationBasedCall("Leaping One-Two Paw: East safe", "East then West"); + @NpcCastCallout(0x0) //TODO: confirm these + private final ModifiableCallout leapingBlackCatCrossingCardInter = ModifiableCallout.durationBasedCall("Leaping Black Cat Crossing: Inter safe", "Inter then Cardinal"); + @NpcCastCallout(0x9329) + private final ModifiableCallout 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 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 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 + }); +} diff --git a/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M3N.java b/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M3N.java new file mode 100644 index 000000000000..99c709bc8e4a --- /dev/null +++ b/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M3N.java @@ -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 brutalLariatWest = ModifiableCallout.durationBasedCall("Brutal Lariat: East safe", "East"); + @NpcCastCallout(0x9AD5) + private final ModifiableCallout brutalLariatEast = ModifiableCallout.durationBasedCall("Brutal Lariat: West safe", "West"); + @NpcCastCallout(0x9ADC) + private final ModifiableCallout lariatComboWestEast = ModifiableCallout.durationBasedCall("Lariat Combo: East then west", "East then West"); + @NpcCastCallout(0x9ADE) + private final ModifiableCallout lariatComboEastWest = ModifiableCallout.durationBasedCall("Lariat Combo: West then east", "West then East"); + @NpcCastCallout(0x9ADD) + private final ModifiableCallout lariatComboWestWest = ModifiableCallout.durationBasedCall("Lariat Combo: East safe", "East, stay east"); + @NpcCastCallout(0x9ADF) + private final ModifiableCallout 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); + } +} diff --git a/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M4N.java b/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M4N.java new file mode 100644 index 000000000000..f4f0c80bf264 --- /dev/null +++ b/triggers/triggers-dt/src/main/java/gg/xp/xivsupport/triggers/Arcadion/M4N.java @@ -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 sidewiseSparkWest = ModifiableCallout.durationBasedCall("Sidewise Spark: East safe", "East safe"); + @NpcCastCallout({0x92BC, 0x92BE}) + private final ModifiableCallout sidewiseSparkEast = ModifiableCallout.durationBasedCall("Sidewise Spark: West safe", "West safe"); + @NpcAbilityUsedCallout(0x92AB) + private final ModifiableCallout stampedingThunderWest = new ModifiableCallout<>("Stampeding Thunder: Go East", "East safe"); + @NpcAbilityUsedCallout(0x92AC) + private final ModifiableCallout 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 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 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 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 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 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); + }); +} diff --git a/xivdata/src/main/java/gg/xp/xivdata/data/duties/KnownDuty.java b/xivdata/src/main/java/gg/xp/xivdata/data/duties/KnownDuty.java index a80e2f492530..5b711d44d044 100644 --- a/xivdata/src/main/java/gg/xp/xivdata/data/duties/KnownDuty.java +++ b/xivdata/src/main/java/gg/xp/xivdata/data/duties/KnownDuty.java @@ -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;