Skip to content

Commit

Permalink
Merge pull request #436 from FFXIV-CombatReborn/mergeWIP
Browse files Browse the repository at this point in the history
merge vbm (sphene ex WIP)
  • Loading branch information
CarnifexOptimus authored Nov 15, 2024
2 parents 4ada446 + 30a3cc8 commit f5278bb
Show file tree
Hide file tree
Showing 165 changed files with 1,478 additions and 208 deletions.
2 changes: 1 addition & 1 deletion BossMod/BossModule/BossComponent.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public virtual void OnUntethered(Actor source, ActorTetherInfo tether) { }
public virtual void OnCastStarted(Actor caster, ActorCastInfo spell) { } // note: action is always a spell; not called for player spells
public virtual void OnCastFinished(Actor caster, ActorCastInfo spell) { } // note: action is always a spell; not called for player spells
public virtual void OnEventCast(Actor caster, ActorCastEvent spell) { } // note: action is always a spell; not called for player spells
public virtual void OnEventIcon(Actor actor, uint iconID) { }
public virtual void OnEventIcon(Actor actor, uint iconID, ulong targetID) { }
public virtual void OnActorEState(Actor actor, ushort state) { }
public virtual void OnActorEAnim(Actor actor, uint state) { }
public virtual void OnActorPlayActionTimelineEvent(Actor actor, ushort id) { }
Expand Down
4 changes: 2 additions & 2 deletions BossMod/BossModule/BossModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -422,10 +422,10 @@ private void OnActorStatusLose(Actor actor, int index)
comp.OnStatusLose(actor, actor.Statuses[index]);
}

private void OnActorIcon(Actor actor, uint iconID)
private void OnActorIcon(Actor actor, uint iconID, ulong targetID)
{
foreach (var comp in _components)
comp.OnEventIcon(actor, iconID);
comp.OnEventIcon(actor, iconID, targetID);
}

private void OnActorCastEvent(Actor actor, ActorCastEvent cast)
Expand Down
4 changes: 2 additions & 2 deletions BossMod/Components/BaitAway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ public BaitAwayEveryone(BossModule module, Actor? source, AOEShape shape, Action
}

// component for mechanics requiring tether targets to bait their aoe away from raid
public class BaitAwayTethers(BossModule module, AOEShape shape, uint tetherID, ActionID aid = default, uint enemyOID = default, float activationDelay = default) : GenericBaitAway(module, aid)
public class BaitAwayTethers(BossModule module, AOEShape shape, uint tetherID, ActionID aid = default, uint enemyOID = default, float activationDelay = default, bool centerAtTarget = false) : GenericBaitAway(module, aid, centerAtTarget: centerAtTarget)
{
public AOEShape Shape = shape;
public uint TID = tetherID;
Expand Down Expand Up @@ -186,7 +186,7 @@ public class BaitAwayIcon(BossModule module, AOEShape shape, uint iconID, Action

public virtual Actor? BaitSource(Actor target) => source ?? Module.PrimaryActor;

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (iconID == IID && BaitSource(actor) is var source && source != null)
CurrentBaits.Add(new(source, actor, Shape, WorldState.FutureTime(ActivationDelay)));
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Components/ChasingAOEs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell)
}
}

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (iconID == Icon)
{
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Components/SharedTankbuster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public class IconSharedTankbuster(BossModule module, uint iconId, ActionID aid,

public virtual Actor? BaitSource(Actor target) => Module.PrimaryActor;

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (iconID == iconId)
{
Expand Down
4 changes: 2 additions & 2 deletions BossMod/Components/StackSpread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,7 @@ public class IconStackSpread(BossModule module, uint stackIcon, uint spreadIcon,
public int MaxCasts { get; init; } = maxCasts; // for stacks where the final AID hits multiple times
private int castCounter;

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (iconID == StackIcon)
{
Expand Down Expand Up @@ -435,7 +435,7 @@ public class DonutStack(BossModule module, ActionID aid, uint icon, float innerR
public uint Icon { get; init; } = icon;
public ActionID Aid { get; init; } = aid;

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (iconID == Icon)
AddStack(actor, WorldState.FutureTime(ActivationDelay));
Expand Down
28 changes: 9 additions & 19 deletions BossMod/Data/ActionEffect.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,30 +30,20 @@ public enum ActionEffectType : byte
EnmityAmountUp = 25, // 0x19 - ? summons
EnmityAmountDown = 26, // 0x1A
StartActionCombo = 27, // 0x1B
Retaliation = 29, // 0x1D - 'vengeance' has value = 7, 'arms length' has value = 0
Knockback = 32, // 0x20
Attract1 = 33, // 0x21
Attract2 = 34, // 0x22
AttractCustom1 = 35, // 0x23
AttractCustom2 = 36, // 0x24
AttractCustom3 = 37, // 0x25
//Unknown_27 = 39, // 0x27
Mount = 40, // 0x28
//unknown_30 = 48, // 0x30
//unknown_31 = 49, // 0x31
//Unknown_32 = 50, // 0x32
ReviveLB = 51, // 0x33 - heal lb3 revive with full hp; seen value == 1
//Unknown_34 = 52, // 0x34
Retaliation = 28, // 0x1C - 'vengeance' has value = 7, 'arms length' has value = 0
Knockback = 31, // 0x1F
Attract1 = 32, // 0x20
Attract2 = 33, // 0x21
AttractCustom1 = 34, // 0x22
AttractCustom2 = 35, // 0x23
AttractCustom3 = 36, // 0x24
Mount = 39, // 0x27
ReviveLB = 50, // 0x32 - heal lb3 revive with full hp; seen value == 1
FullResistStatus = 55, // 0x37 - full resist status (e.g. 9 = resist 'arms length' slow, 2 = resist 'low blow' stun)
//Unknown_38 = 56, // 0x38
//Unknown_39 = 57, // 0x39 - 'you have been sentenced to death!' message
VFX = 59, // 0x3B
//Unknown_3D = 61, // 0x3D - was called 'gauge', but i think it's incorrect
Resource = 62, // 0x3E - value 0x34 = gain war gauge (amount == hitSeverity)
//Unknown_41 = 65, // 0x41
//Unknown_43 = 67, // 0x43
//Unknown_47 = 71, // 0x47
//Unknown_48 = 72, // 0x48
SetModelState = 73, // 0x49 - value == model state
SetHP = 74, // 0x4A - e.g. zodiark's kokytos
PartialInvulnerable = 75, // 0x4B
Expand Down
8 changes: 4 additions & 4 deletions BossMod/Data/ActorState.cs
Original file line number Diff line number Diff line change
Expand Up @@ -372,11 +372,11 @@ public override void Write(ReplayRecorder.Output output)
}

// TODO: this should really be an actor field, but I have no idea what triggers icon clear...
public Event<Actor, uint> IconAppeared = new();
public sealed record class OpIcon(ulong InstanceID, uint IconID) : Operation(InstanceID)
public Event<Actor, uint, ulong> IconAppeared = new();
public sealed record class OpIcon(ulong InstanceID, uint IconID, ulong TargetID) : Operation(InstanceID)
{
protected override void ExecActor(WorldState ws, Actor actor) => ws.Actors.IconAppeared.Fire(actor, IconID);
public override void Write(ReplayRecorder.Output output) => output.EmitFourCC("ICON"u8).EmitActor(InstanceID).Emit(IconID);
protected override void ExecActor(WorldState ws, Actor actor) => ws.Actors.IconAppeared.Fire(actor, IconID, TargetID);
public override void Write(ReplayRecorder.Output output) => output.EmitFourCC("ICON"u8).EmitActor(InstanceID).Emit(IconID).EmitActor(TargetID);
}

// TODO: this should be an actor field (?)
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Framework/WorldStateGameSync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ private void ProcessPacketActorControlDetour(uint actorID, uint category, uint p
switch ((Network.ServerIPC.ActorControlCategory)category)
{
case Network.ServerIPC.ActorControlCategory.TargetIcon:
_actorOps.GetOrAdd(actorID).Add(new ActorState.OpIcon(actorID, p1 - Network.IDScramble.Delta));
_actorOps.GetOrAdd(actorID).Add(new ActorState.OpIcon(actorID, p1 - Network.IDScramble.Delta, p2));
break;
case Network.ServerIPC.ActorControlCategory.Tether:
_actorOps.GetOrAdd(actorID).Add(new ActorState.OpTether(actorID, new(p2, p3)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public int RemainingCasts()
return max;
}

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (iconID == (uint)IconID.CalamitysInferno)
{
Expand Down Expand Up @@ -57,7 +57,7 @@ public override void OnStatusLose(Actor actor, ActorStatus status)
--NumActiveFreezes;
}

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (iconID == (uint)IconID.CalamitysChill && Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0)
{
Expand All @@ -79,7 +79,7 @@ class ThunderScourgeOfIceThunder(BossModule module) : Components.UniformStackSpr
public int NumCasts;
private readonly ThunderPlatform? _platform = module.FindComponent<ThunderPlatform>();

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if ((IconID)iconID is IconID.CalamitysBolt or IconID.CalamitysChill)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public override void Update()

public override PlayerPriority CalcPriority(int pcSlot, Actor pc, int playerSlot, Actor player, ref uint customColor) => PlayerPriority.Normal;

public override void OnEventIcon(Actor actor, uint iconID)
public override void OnEventIcon(Actor actor, uint iconID, ulong targetID)
{
if (iconID == (uint)IconID.ChasmOfVollok)
{
Expand Down
62 changes: 62 additions & 0 deletions BossMod/Modules/Dawntrail/Extreme/Ex3Sphene/AbsoluteAuthority.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace BossMod.Dawntrail.Extreme.Ex3Sphene;

class AbsoluteAuthorityPuddles(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.AbsoluteAuthorityPuddlesAOE), 8);

class AbsoluteAuthorityExpansionBoot(BossModule module) : Components.UniformStackSpread(module, 6, 15, 4, alwaysShowSpreads: true) // TODO: verify falloff
{
public int NumCasts;
private readonly Ex3SpheneConfig _config = Service.Config.Get<Ex3SpheneConfig>();

public override void OnStatusGain(Actor actor, ActorStatus status)
{
switch ((SID)status.ID)
{
case SID.AuthoritysExpansion:
if (!_config.AbsoluteAuthorityIgnoreFlares)
AddSpread(actor, status.ExpireAt);
break;
case SID.AuthoritysBoot:
AddStack(actor, status.ExpireAt);
break;
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.AbsoluteAuthorityExpansion or AID.AbsoluteAuthorityBoot)
{
++NumCasts;
Spreads.Clear();
Stacks.Clear();
}
}
}

class AbsoluteAuthorityHeel(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.AbsoluteAuthorityHeel))
{
private BitMask _targets;

private const float Radius = 3; // TODO: verify

public override void AddHints(int slot, Actor actor, TextHints hints)
{
if (_targets[slot] && !Raid.WithoutSlot().InRadiusExcluding(actor, Radius).Any())
hints.Add("Stack with someone!");
}

public override void DrawArenaForeground(int pcSlot, Actor pc)
{
if (_targets[pcSlot])
Arena.AddCircle(pc.Position, Radius, Colors.Safe);
}

public override void OnStatusGain(Actor actor, ActorStatus status)
{
if ((SID)status.ID == SID.AuthoritysHeel)
{
_targets.Set(Raid.FindSlot(actor.InstanceID));
}
}
}

class AbsoluteAuthorityKnockback(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.AbsoluteAuthorityKnockback), 30, kind: Kind.DirForward);
46 changes: 46 additions & 0 deletions BossMod/Modules/Dawntrail/Extreme/Ex3Sphene/Aethertithe.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace BossMod.Dawntrail.Extreme.Ex3Sphene;

class Aethertithe(BossModule module) : Components.GenericAOEs(module)
{
public AOEInstance? AOE;

private static readonly AOEShapeCone _shape = new(100, 30.Degrees()); // TODO: verify angle

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(AOE);

public override void OnEventEnvControl(byte index, uint state)
{
if (index != 0)
return;
Angle? dir = state switch
{
0x04000100 => -60.Degrees(),
0x08000100 => 0.Degrees(),
0x10000100 => 60.Degrees(),
_ => null
};
if (dir != null)
{
AOE = new(_shape, Module.PrimaryActor.Position, dir.Value, WorldState.FutureTime(5.1f));
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.AethertitheAOER or AID.AethertitheAOEC or AID.AethertitheAOEL)
{
AOE = null;
++NumCasts;
}
}
}

class Retribute : Components.GenericWildCharge
{
public Retribute(BossModule module) : base(module, 4, ActionID.MakeSpell(AID.RetributeAOE), 60)
{
Source = module.PrimaryActor;
foreach (var (i, p) in module.Raid.WithSlot(true))
PlayerRoles[i] = p.Role == Role.Healer ? PlayerRole.Target : PlayerRole.Share;
}
}
65 changes: 65 additions & 0 deletions BossMod/Modules/Dawntrail/Extreme/Ex3Sphene/Coronation.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
namespace BossMod.Dawntrail.Extreme.Ex3Sphene;

class Coronation(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.RuthlessRegalia))
{
public struct Group
{
public required Actor Source;
public Actor? LeftPartner;
public Actor? RightPartner;

public readonly bool Contains(Actor player) => LeftPartner == player || RightPartner == player;
}

public readonly List<Group> Groups = [];
private DateTime _activation;

private static readonly AOEShapeRect _shape = new(100, 6);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Groups.Select(g => new AOEInstance(_shape, g.Source.Position, g.Source.Rotation, _activation));

public override PlayerPriority CalcPriority(int pcSlot, Actor pc, int playerSlot, Actor player, ref uint customColor)
{
var index = Groups.FindIndex(g => g.Contains(pc));
return index >= 0 && Groups[index].Contains(player) ? PlayerPriority.Interesting : PlayerPriority.Irrelevant;
}

public override void DrawArenaForeground(int pcSlot, Actor pc)
{
foreach (ref var g in Groups.AsSpan())
{
Arena.Actor(g.Source, Colors.Object, true);
if (g.Contains(pc))
{
if (g.LeftPartner != null)
Arena.AddLine(g.LeftPartner.Position, g.Source.Position, Colors.Danger);
if (g.RightPartner != null)
Arena.AddLine(g.RightPartner.Position, g.Source.Position, Colors.Danger);
}
}
}

public override void OnTethered(Actor source, ActorTetherInfo tether)
{
if ((TetherID)tether.ID is TetherID.CoronationL or TetherID.CoronationR)
{
_activation = WorldState.FutureTime(10.1f);
var index = Groups.FindIndex(g => g.Source.InstanceID == tether.Target);
if (index < 0 && WorldState.Actors.Find(tether.Target) is var target && target != null)
{
index = Groups.Count;
Groups.Add(new() { Source = target });
}
if (index >= 0)
{
ref var group = ref Groups.Ref(index);
ref var partner = ref (TetherID)tether.ID == TetherID.CoronationL ? ref group.LeftPartner : ref group.RightPartner;
if (partner != null)
ReportError($"Both {source} and {partner} have identical tether");
partner = source;
}
}
}
}

class AtomicRay(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.AtomicRayAOE), 16, false);
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace BossMod.Dawntrail.Extreme.Ex3Sphene;

class DimensionalDistortion(BossModule module) : Components.Exaflare(module, 6)
{
public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID is AID.DimensionalDistortionFirst)
{
Lines.Add(new() { Next = caster.Position, Advance = 8.5f * spell.Rotation.ToDirection(), NextExplosion = Module.CastFinishAt(spell), TimeToMove = 1.1f, ExplosionsLeft = 5, MaxShownExplosions = 2 });
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.DimensionalDistortionFirst or AID.DimensionalDistortionRest)
{
++NumCasts;
int index = Lines.FindIndex(item => item.Next.AlmostEqual(caster.Position, 1));
if (index == -1)
{
ReportError($"Failed to find entry for {caster.InstanceID:X}");
return;
}

AdvanceLine(Lines[index], caster.Position);
if (Lines[index].ExplosionsLeft == 0)
Lines.RemoveAt(index);
}
}
}
Loading

0 comments on commit f5278bb

Please sign in to comment.