Skip to content

Commit

Permalink
Added ion cluster tower location
Browse files Browse the repository at this point in the history
  • Loading branch information
xpdota committed Aug 3, 2024
1 parent f84ef2d commit 017451f
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import gg.xp.xivsupport.events.triggers.support.PlayerStatusCallout;
import gg.xp.xivsupport.models.ArenaPos;
import gg.xp.xivsupport.models.ArenaSector;
import gg.xp.xivsupport.models.Position;
import gg.xp.xivsupport.models.XivCombatant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
Expand All @@ -42,6 +43,7 @@
import java.util.Optional;
import java.util.Queue;
import java.util.Set;
import java.util.stream.Collectors;

@CalloutRepo(name = "M4S", duty = KnownDuty.M4S)
public class M4S extends AutoChildEventHandler implements FilteredEventHandler {
Expand Down Expand Up @@ -817,9 +819,9 @@ If wings, go into the first active set ((all cardinals or all intercardinals fir
private final ModifiableCallout<BuffApplied> ionCluster2longNeg = ModifiableCallout.<BuffApplied>durationBasedCall("Ion Cluster 2: Long Negatron", "Long Negatron").autoIcon();

private final ModifiableCallout<BuffApplied> ionCluster2baitFirstSet = ModifiableCallout.<BuffApplied>durationBasedCall("Ion Cluster 2: Bait First Set", "Bait {baitLocations}").autoIcon();
private final ModifiableCallout<BuffApplied> ionCluster2avoidFirstSet = ModifiableCallout.<BuffApplied>durationBasedCall("Ion Cluster 2: Take First Tower", "Soak Tower").autoIcon();
private final ModifiableCallout<?> ionCluster2avoidFirstSet = new ModifiableCallout<>("Ion Cluster 2: Take First Tower", "Soak {towers} Towers");
private final ModifiableCallout<BuffApplied> ionCluster2baitSecondSet = ModifiableCallout.<BuffApplied>durationBasedCall("Ion Cluster 2: Bait Second Set", "Bait {baitLocations}").autoIcon();
private final ModifiableCallout<BuffApplied> ionCluster2avoidSecondSet = ModifiableCallout.<BuffApplied>durationBasedCall("Ion Cluster 2: Take Second Tower", "Soak Tower").autoIcon();
private final ModifiableCallout<?> ionCluster2avoidSecondSet = new ModifiableCallout<>("Ion Cluster 2: Take Second Tower", "Soak {towers} Tower");

// Ion Cluster #2, aka Sunrise Sabbath
@AutoFeed
Expand All @@ -844,7 +846,40 @@ If wings, go into the first active set ((all cardinals or all intercardinals fir
// First round
var gunBuffs = s.waitEventsQuickSuccession(4, BuffApplied.class, ba -> ba.buffIdMatches(0xB9A));
if (playerLong) {
s.updateCall(ionCluster2avoidFirstSet, playerBuff);
s.waitThenRefreshCombatants(100);
List<XivCombatant> towerNpcs = state.npcsById(17323)
.stream()
.filter(cbt -> cbt.getWeaponId() == 28)
.toList();
if (towerNpcs.size() != 2) {
log.error("Tower npc fail! {}", towerNpcs);
}
else {
// The logic here is to directly compute where the tower would end up based on the facing
// angle of the mob casting it. Adding 21.21 makes it a near-perfect match for diagonal
// jumps, and ends up with the correct answer for cardinal jumps even though they don't
// actually swap positions in game.
// Example E->S jump: (115, 165) -> (100, 180) for a distance of 21.21
Set<ArenaSector> towerLocations = towerNpcs.stream()
.map(npc -> {
Position approxHitLocation = npc.getPos().translateRelative(0, 21.21);
return finalAp.forPosition(approxHitLocation);
}).collect(Collectors.toSet());
// This is collected and compared as a set, but then manually written as a list so that it
// always appears in a consistent order.
if (towerLocations.equals(Set.of(ArenaSector.WEST, ArenaSector.EAST))) {
s.setParam("towers", List.of(ArenaSector.WEST, ArenaSector.EAST));
}
else if (towerLocations.equals(Set.of(ArenaSector.NORTH, ArenaSector.SOUTH))) {
s.setParam("towers", List.of(ArenaSector.NORTH, ArenaSector.SOUTH));
}
else {
log.error("Bad tower locations! {}", towerLocations);
}
}
// Even if we errored, this call is better than nothing
s.updateCall(ionCluster2avoidFirstSet);

}
else {
List<ArenaSector> acceptableGuns = gunBuffs.stream()
Expand Down Expand Up @@ -876,7 +911,35 @@ If wings, go into the first active set ((all cardinals or all intercardinals fir
s.updateCall(ionCluster2baitSecondSet, playerBuff);
}
else {
s.updateCall(ionCluster2avoidSecondSet, playerBuff);
s.waitThenRefreshCombatants(100);
List<XivCombatant> towerNpcs = state.npcsById(17323)
.stream()
.filter(cbt -> cbt.getWeaponId() == 28)
.toList();
if (towerNpcs.size() != 2) {
log.error("Tower npc fail 2! {}", towerNpcs);
}
else {
// The logic here is to directly compute where the tower would end up based on the facing
// angle of the mob casting it. Adding 21.21 makes it a near-perfect match for diagonal
// jumps, and ends up with the correct answer for cardinal jumps even though they don't
// actually swap positions in game.
// Example E->S jump: (115, 165) -> (100, 180) for a distance of 21.21
Set<ArenaSector> towerLocations = towerNpcs.stream()
.map(npc -> {
Position approxHitLocation = npc.getPos().translateRelative(0, 21.21);
return finalAp.forPosition(approxHitLocation);
}).collect(Collectors.toSet());
if (towerLocations.equals(Set.of(ArenaSector.NORTH, ArenaSector.SOUTH))
|| towerLocations.equals(Set.of(ArenaSector.WEST, ArenaSector.EAST))) {
s.setParam("towers", towerLocations.stream().toList());
}
else {
log.error("Bad tower locations 2! {}", towerLocations);
}
}
// Even if we errored, this call is better than nothing
s.updateCall(ionCluster2avoidSecondSet);
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import java.util.List;

public class DTEx1SecondTest
public class M4sTest
extends CalloutVerificationTest {
@Override
protected String getFileName() {
Expand Down Expand Up @@ -80,7 +80,7 @@ protected List<CalloutInitialValues> getExpectedCalls() {
call(667810, "Later: Knockback West then East", "Later: Knockback West then East (6.7)"),
call(677966, "Raidwide", "Raidwide (4.7)"),
call(689905, "Long Negatron", "Long Negatron (38.0)"),
call(701487, "Soak Tower", "Soak Tower (26.4)"),
call(701753, "Soak West, East Towers", "Soak West, East Towers"),
call(710441, "Middle", "Middle (4.7)"),
call(718147, "Bait Northwest, Southwest", "Bait Northwest, Southwest (9.8)"),
call(726613, "Knockback West then East", "Knockback West then East (4.7)")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ protected Event convert(FieldMapper<Fields> fields, int lineNumber, ZonedDateTim
case "PosZ" -> pos.put(PosKeys.PosZ, Double.parseDouble(valueRaw));
case "Heading" -> pos.put(PosKeys.Heading, Double.parseDouble(valueRaw));
case "Radius" -> state.provideCombatantRadius(existing, Float.parseFloat(valueRaw));
case "WeaponId" -> state.provideWeaponId(existing, Short.parseShort(valueRaw));
}
}
if (pos.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -119,4 +119,6 @@ default List<XivCombatant> npcsById(long id) {
}

void provideTransformation(long entityId, short transformationId);

void provideWeaponId(XivCombatant existing, short weaponId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -150,4 +150,9 @@ public void provideCombatantRadius(XivCombatant cbt, float radius) {
public void provideTransformation(long entityId, short transformationId) {
throw new UnsupportedOperationException("not supported");
}

@Override
public void provideWeaponId(XivCombatant existing, short weaponId) {
throw new UnsupportedOperationException("not supported");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -481,6 +481,12 @@ public void provideTransformation(long entityId, short transformationId) {
dirtyOverrides = true;
}

@Override
public void provideWeaponId(XivCombatant existing, short weaponId) {
getOrCreateData(existing.getId()).setWeaponId(weaponId);
dirtyOverrides = true;
}

@Override
public @Nullable XivCombatant getCombatant(long id) {
CombatantData cbt = combatantData.get(id);
Expand Down Expand Up @@ -626,6 +632,7 @@ private final class CombatantData {
private XivCombatant owner;
private long shieldPercent;
private short tfId = -1;
private short weaponId = -1;

private CombatantData(long id) {
this.id = id;
Expand Down Expand Up @@ -714,6 +721,11 @@ public void setTransformationId(short tfId) {
dirty = true;
}

public void setWeaponId(short weaponId) {
this.weaponId = weaponId;
dirty = true;
}

public void setRadius(float radius) {
this.radius = radius;
}
Expand Down Expand Up @@ -767,7 +779,7 @@ private synchronized void recompute() {
boolean isPlayer = rawType == 1;
long shieldAmount = hp != null ? shieldPercent * hp.max() / 100 : 0;
short transformationId = tfId != -1 ? tfId : (raw != null ? raw.getTransformationId() : -1);
short weaponId = (raw != null ? raw.getWeaponId() : -1);
short weaponId = this.weaponId != -1 ? this.weaponId : (raw != null ? raw.getWeaponId() : -1);
float radius = this.radius >= 0 ? this.radius : ((raw != null) ? raw.getRadius() : 0);
if (isPlayer) {
computed = new XivPlayerCharacter(
Expand Down

0 comments on commit 017451f

Please sign in to comment.