Skip to content

Commit

Permalink
More FRU and general improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
xpdota committed Nov 27, 2024
1 parent d3ebc7f commit 2aed5ef
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,34 @@
import gg.xp.reevent.scan.FeedHelperAdapter;
import gg.xp.reevent.scan.ScanMe;
import gg.xp.xivsupport.callouts.ModifiableCallout;
import gg.xp.xivsupport.callouts.RawModifiedCallout;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.events.state.combatstate.ActiveCastRepository;
import gg.xp.xivsupport.events.state.combatstate.CastTracker;
import gg.xp.xivsupport.events.triggers.util.RepeatSuppressor;

import java.time.Duration;
import java.util.Optional;

@ScanMe
public class NpcCastAdapter implements FeedHelperAdapter<NpcCastCallout, AbilityCastStart, ModifiableCallout<AbilityCastStart>> {

private final ActiveCastRepository casts;

public NpcCastAdapter(ActiveCastRepository casts) {
this.casts = casts;
}

@Override
public Class<AbilityCastStart> eventType() {
return AbilityCastStart.class;
}

@Override
public TypedEventHandler<AbilityCastStart> makeHandler(FeedHandlerChildInfo<NpcCastCallout, ModifiableCallout<AbilityCastStart>> info) {
long[] castIds = info.getAnnotation().value();
long suppMs = info.getAnnotation().suppressMs();
NpcCastCallout ann = info.getAnnotation();
long[] castIds = ann.value();
long suppMs = ann.suppressMs();
RepeatSuppressor supp;
if (suppMs >= 0) {
supp = new RepeatSuppressor(Duration.ofMillis(suppMs));
Expand All @@ -41,7 +52,37 @@ public void handle(EventContext context, AbilityCastStart event) {
for (int i = 0; i < castIds.length; i++) {
if (!event.getSource().isPc() && castIds[i] == event.getAbility().getId()) {
if (supp.check(event)) {
context.accept(info.getHandlerFieldValue().getModified(event));
RawModifiedCallout<AbilityCastStart> modified = info.getHandlerFieldValue().getModified(event);
if (ann.cancellable()) {
modified.addExpiryCondition(() -> {
Optional<CastTracker> ctOpt = casts.forCast(event);
if (ctOpt.isEmpty()) {
return true;
}
CastTracker ct = ctOpt.get();
switch (ct.getResult()) {
// Still in progress
case IN_PROGRESS -> {
return false;
}
// Wait for normal expiry delay. This acts as an "OR" so returning false
// means to defer to the existing logic.
case SUCCESS -> {
return false;
}
// Remove immediately if interrupted
case INTERRUPTED -> {
return true;
}
// ?
case UNKNOWN -> {
return false;
}
}
return false;
});
}
context.accept(modified);
}
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@
long[] value();

long suppressMs() default -1;

/**
* @return Whether the callout should be removed if the cast stops
*/
boolean cancellable() default false;
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
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.ActorControlExtraEvent;
import gg.xp.xivsupport.events.actlines.events.BuffApplied;
import gg.xp.xivsupport.events.actlines.events.HasAbility;
import gg.xp.xivsupport.events.actlines.events.HasSourceEntity;
Expand All @@ -32,6 +33,7 @@
import gg.xp.xivsupport.persistence.settings.JobSortOverrideSetting;
import gg.xp.xivsupport.persistence.settings.JobSortSetting;

import java.io.Serial;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
Expand Down Expand Up @@ -109,8 +111,8 @@ public boolean enabled(EventContext context) {
s.updateCall(powderMarkBombSoon, debuff);
});

private final ModifiableCallout<AbilityCastStart> utopianSkyStackInitial = ModifiableCallout.durationBasedCall("Utopian Sky: Initial (Fire)", "Stack Later");
private final ModifiableCallout<AbilityCastStart> utopianSkySpreadInitial = ModifiableCallout.durationBasedCall("Utopian Sky: Initial (Lightning)", "Spread Later");
private final ModifiableCallout<AbilityCastStart> utopianSkyStackInitial = ModifiableCallout.durationBasedCallWithoutDurationText("Utopian Sky: Initial (Fire)", "Stack Later");
private final ModifiableCallout<AbilityCastStart> utopianSkySpreadInitial = ModifiableCallout.durationBasedCallWithoutDurationText("Utopian Sky: Initial (Lightning)", "Spread Later");
private final ModifiableCallout<AbilityCastStart> utopianSkyStackSafeSpot = ModifiableCallout.durationBasedCall("Utopian Sky: Safe Spot (Fire)", "Stack {safe}");
private final ModifiableCallout<AbilityCastStart> utopianSkySpreadSafeSpot = ModifiableCallout.durationBasedCall("Utopian Sky: Safe Spot (Lightning)", "Spread {safe}");

Expand All @@ -122,11 +124,13 @@ public boolean enabled(EventContext context) {
(e1, s) -> {
boolean isFire = e1.abilityIdMatches(0x9CDA);
s.updateCall(isFire ? utopianSkyStackInitial : utopianSkySpreadInitial, e1);
var casts = s.waitEventsQuickSuccession(3, AbilityCastStart.class, acs -> acs.abilityIdMatches(0x9CDE));
// This is the "raising" animation. We can't use the casts directly since they position themselves in the middle.
// Could use cast locations but that ends up being more complicated.
var events = s.waitEventsQuickSuccession(3, ActorControlExtraEvent.class, acee -> acee.getCategory() == 0x3f && acee.getData0() == 0x4);
List<ArenaSector> safe;
do {
List<ArenaSector> tmpSafe = new ArrayList<>(ArenaSector.all);
casts.stream().map(cast -> arenaPos.forCombatant(state.getLatestCombatantData(cast.getSource())))
events.stream().map(acee -> arenaPos.forCombatant(state.getLatestCombatantData(acee.getTarget())))
.forEach(pos -> {
tmpSafe.remove(pos);
tmpSafe.remove(pos.opposite());
Expand All @@ -137,7 +141,9 @@ public boolean enabled(EventContext context) {
}
} while (safe.size() != 2);
s.setParam("safe", safe);
s.updateCall(isFire ? utopianSkyStackSafeSpot : utopianSkySpreadSafeSpot);
// Get the actual cast so that we have something to display
var cast = s.waitEvent(AbilityCastStart.class, acs -> acs.abilityIdMatches(0x9CDE));
s.updateCall(isFire ? utopianSkyStackSafeSpot : utopianSkySpreadSafeSpot, cast);
});

@AutoFeed
Expand All @@ -158,10 +164,7 @@ public boolean enabled(EventContext context) {
// Debounce
s.waitMs(1_000);

var e4 = s.waitEvent(AbilityUsedEvent.class, aue -> aue.abilityIdMatches(0x9CD2));
s.updateCall(cyclonicBreakMove3, e4);
// Debounce
s.waitMs(1_000);
// The final movement is handled by turnNSSafe call below

// Prior to the proteans (i.e. need another sq), there is, in one example:
// Turn of the Heavens (6.7s) 9CD7 - determines safe spot?
Expand All @@ -170,29 +173,46 @@ public boolean enabled(EventContext context) {
// Burnout (9.4s) 9CE4 - probably KB?
});

private final ModifiableCallout<AbilityCastStart> turnNSSafe = ModifiableCallout.durationBasedCall("Turn of the Heavens: Dodge Lightning", "North/South Out");
// private final ModifiableCallout<AbilityCastStart> turnKB = ModifiableCallout.durationBasedCall("Turn of the Heavens: Dodge Lightning", "Get Knocked to {redSafe ? 'Red' : 'Blue'}");
private final ModifiableCallout<AbilityCastStart> turnKB = ModifiableCallout.durationBasedCall("Turn of the Heavens: Dodge Lightning", "Get Knocked to Safe Side");
private final ModifiableCallout<AbilityCastStart> turnInitial = new ModifiableCallout<>("Turn of the Heavens: Initial", "{redSafe ? 'Red' : 'Blue'} Safe");
private final ModifiableCallout<AbilityCastStart> turnNSSafe = ModifiableCallout.durationBasedCall("Turn of the Heavens: Dodge Lightning", "Move, North/South Out");
private final ModifiableCallout<AbilityCastStart> turnKB = ModifiableCallout.durationBasedCall("Turn of the Heavens: Dodge Lightning", "Get Knocked {safe}");

@AutoFeed
private final SequentialTrigger<BaseEvent> turnOfTheHeavensSq = SqtTemplates.sq(30_000,
AbilityCastStart.class, acs -> acs.abilityIdMatches(0x9CD7, 0x9CD6),
AbilityCastStart.class, acs -> acs.abilityIdMatches(0x9CD6, 0x9CD7),
(e1, s) -> {
// TODO: which is red vs blue safe?
// How do we determine where red or blue is?
// s.updateCall(TODO);
s.waitMs(4_000);
// For the rings, they are called Halo of Flame (NPC 17821:9710) or Halo of Levin (17822:9711)
// 9CD6 is blue safe, 9CD7 is red safe
boolean redSafe = e1.abilityIdMatches(0x9CD7);
s.setParam("redSafe", redSafe);
s.updateCall(turnInitial);
s.waitMs(3_700);

s.setParam("redSafe", true); // TODO

Optional<CastTracker> lightning = casts.getActiveCastById(0x9CE3);
s.updateCall(turnNSSafe, lightning.map(CastTracker::getCast).orElse(e1));
s.waitEvent(AbilityUsedEvent.class, aue -> aue.abilityIdMatches(0x9CE3));
Optional<CastTracker> burnout = casts.getActiveCastById(0x9CE4);
ArenaSector safe;
do {
// Find orbs that are east or west
// We are looking for the safe spot, so if fire is safe, look for the fire orb
List<ArenaSector> eastWest = state.npcsById(redSafe ? 17821 : 17822).stream()
//
.map(fireOrb -> arenaPos.forCombatant(state.getLatestCombatantData(fireOrb)))
.filter(pos -> pos == ArenaSector.WEST || pos == ArenaSector.EAST)
.toList();
if (eastWest.size() == 1) {
safe = eastWest.get(0);
break;
}
else {
s.waitThenRefreshCombatants(200);
}
} while (true);
s.setParam("safe", safe);
s.updateCall(turnKB, burnout.map(CastTracker::getCast).orElse(e1));
s.waitEvent(AbilityUsedEvent.class, aue -> aue.abilityIdMatches(0x9CE4));


});


Expand Down Expand Up @@ -233,6 +253,8 @@ public enum MechType {
}

public static class FruP1TetherEvent extends BaseEvent implements HasSourceEntity, HasTargetEntity, HasAbility {
@Serial
private static final long serialVersionUID = -9182985137525672763L;
private final AbilityCastStart cast;
private final XivCombatant source;
private final XivPlayerCharacter target;
Expand Down Expand Up @@ -324,8 +346,7 @@ private static ModifiableCallout<FruP1TetherEvent> makeTetherDefault(String desc

s.waitMs(1_000);

// Wait for floating fetters
s.waitEvent(AbilityUsedEvent.class, aue -> aue.abilityIdMatches(0x9CEB));
// Call the first one soon, but for the rest, wait until the previous tether goes off
s.updateCall(fourTetherResolving1);
s.waitEvent(AbilityUsedEvent.class, aue -> aue.abilityIdMatches(0x9CEB));
s.updateCall(fourTetherResolving2);
Expand Down Expand Up @@ -358,7 +379,7 @@ private static ModifiableCallout<FruP1TetherEvent> makeTetherDefault(String desc
}
);

@NpcCastCallout(0x9CC0)
@NpcCastCallout(value = 0x9CC0, cancellable = true)
private final ModifiableCallout<AbilityCastStart> p1enrage = ModifiableCallout.durationBasedCall("P1 Enrage", "Enrage");

/*
Expand All @@ -373,6 +394,24 @@ private static ModifiableCallout<FruP1TetherEvent> makeTetherDefault(String desc
private final ModifiableCallout<AbilityCastStart> quadrupleSlap = ModifiableCallout.durationBasedCall("Quadruple Slap", "Buster on {event.target}");
@NpcCastCallout(0x9D05)
private final ModifiableCallout<AbilityCastStart> diamondDust = ModifiableCallout.durationBasedCall("Diamond Dust", "Raidwide");

@AutoFeed
private final SequentialTrigger<BaseEvent> axeScythe = SqtTemplates.sq(60_000,
AbilityCastStart.class, acs -> acs.abilityIdMatches(0x9D0A, 0x9D0B),
(e1, s) -> {
boolean isAxeKick = e1.abilityIdMatches(0x9D0A);
if (isAxeKick) {
// out
}
else {
// in
}
});

@NpcCastCallout(0x9D01)
private final ModifiableCallout<AbilityCastStart> twinStillness = ModifiableCallout.durationBasedCall("Twin Stillness", "Back to Front");
@NpcCastCallout(0x9D02)
private final ModifiableCallout<AbilityCastStart> twinSilence = ModifiableCallout.durationBasedCall("Twin Silence", "Front to Back");
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ public class RawModifiedCallout<X> extends BaseEvent implements HasCalloutTracki
private final @Nullable X event;
private final Map<String, Object> arguments;
private final Function<? super X, ? extends @Nullable Component> guiProvider;
private final Predicate<RawModifiedCallout<X>> expiry;
private Predicate<RawModifiedCallout<X>> expiry;
private @Nullable HasCalloutTrackingKey replaces;
private @Nullable Color colorOverride;
private final ModifiedCalloutHandle handle;
Expand Down Expand Up @@ -74,6 +74,14 @@ public BooleanSupplier getExpiry() {
return () -> expiry.test(this) || this.forceExpired;
}

public void addExpiryPredicate(Predicate<RawModifiedCallout<X>> condition) {
this.expiry = this.expiry.or(condition);
}

public void addExpiryCondition(BooleanSupplier newExpiry) {
this.expiry = this.expiry.or(ignored -> newExpiry.getAsBoolean());
}

public @Nullable HasCalloutTrackingKey getReplaces() {
return replaces;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package gg.xp.xivsupport.events.state.combatstate;

import gg.xp.reevent.scan.Alias;
import gg.xp.xivsupport.events.actlines.events.AbilityCastStart;
import gg.xp.xivsupport.models.XivCombatant;
import org.jetbrains.annotations.Nullable;

Expand Down Expand Up @@ -29,4 +30,11 @@ default Optional<CastTracker> getActiveCastById(long... ids) {
.filter(ct -> ct.getCast().abilityIdMatches(ids))
.findFirst();
}

default Optional<CastTracker> forCast(AbilityCastStart event) {
return getAll()
.stream()
.filter(ct -> ct.getCast() == event)
.findFirst();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -261,13 +261,16 @@ private void refreshCallouts() {
}
}
synchronized (lock) {
outer:
for (CalloutEvent callout : toAdd) {
for (int i = 0; i < currentCallouts.size(); i++) {
if (callout.shouldReplace(currentCallouts.get(i).event)) {
// If replacing a call, replace it in the list as-is
currentCallouts.set(i, new VisualCalloutItem(callout));
return;
continue outer;
}
}
// Otherwise, add it to the end of the list
currentCallouts.add(new VisualCalloutItem(callout));
}
currentCallouts.removeIf(visualCalloutItem -> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,7 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole
}
});
c.setMinWidth(50);
c.setMaxWidth(50);
c.setMaxWidth(100);
});

public static final CustomColumn<XivCombatant> combatantRawTypeColumn
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,8 @@ public void validateJobSortOrder(List<Job> newSort) {
int expectedNumberOfJobs = allValidJobs.size();
int actualNumberOfJobs = newSort.size();
if (expectedNumberOfJobs != actualNumberOfJobs) {
// TODO: this spams log on startup
// Make it more intelligent - if saved sort is a subset of the available jobs, don't throw this
throw new IllegalArgumentException(String.format("New jail sort order was not the same size! %s -> %s", expectedNumberOfJobs, actualNumberOfJobs));
}
EnumSet<Job> newSortAsSet = EnumSet.copyOf(newSort);
Expand Down

0 comments on commit 2aed5ef

Please sign in to comment.