Skip to content

Commit

Permalink
Merge pull request #126 from FFXIV-CombatReborn/mergeWIP2
Browse files Browse the repository at this point in the history
Seiryu nm verified
  • Loading branch information
CarnifexOptimus authored Jun 7, 2024
2 parents 0554fe9 + 43fbfa9 commit 735f995
Show file tree
Hide file tree
Showing 10 changed files with 282 additions and 96 deletions.
9 changes: 5 additions & 4 deletions BossMod/Components/BaitAway.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,13 @@ 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) : GenericBaitAway(module, aid)
public class BaitAwayTethers(BossModule module, AOEShape shape, uint tetherID, ActionID aid = default, uint enemyOID = default, float activationDelay = default) : GenericBaitAway(module, aid)
{
public AOEShape Shape = shape;
public uint TID = tetherID;
public bool DrawTethers = true;
public readonly IReadOnlyList<Actor> _enemies = module.Enemies(enemyOID);
public float ActivationDelay = activationDelay;

public override void DrawArenaForeground(int pcSlot, Actor pc)
{
Expand All @@ -108,9 +110,8 @@ public override void OnTethered(Actor source, ActorTetherInfo tether)
{
var (player, enemy) = DetermineTetherSides(source, tether);
if (player != null && enemy != null)
{
CurrentBaits.Add(new(enemy, player, Shape));
}
if (enemyOID == default || _enemies.Contains(source))
CurrentBaits.Add(new(enemy, player, Shape, WorldState.FutureTime(ActivationDelay)));
}

public override void OnUntethered(Actor source, ActorTetherInfo tether)
Expand Down
3 changes: 2 additions & 1 deletion BossMod/Components/Towers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@

public class GenericTowers(BossModule module, ActionID aid = default) : CastCounter(module, aid)
{
public struct Tower(WPos position, float radius, int minSoakers = 1, int maxSoakers = 1, BitMask forbiddenSoakers = default)
public struct Tower(WPos position, float radius, int minSoakers = 1, int maxSoakers = 1, BitMask forbiddenSoakers = default, DateTime activation = default)
{
public WPos Position = position;
public float Radius = radius;
public int MinSoakers = minSoakers;
public int MaxSoakers = maxSoakers;
public BitMask ForbiddenSoakers = forbiddenSoakers;
public DateTime Activation = activation;

public readonly bool IsInside(WPos pos) => pos.InCircle(Position, Radius);
public readonly bool IsInside(Actor actor) => IsInside(actor.Position);
Expand Down
43 changes: 43 additions & 0 deletions BossMod/Modules/Stormblood/Trial/T09Seiryu/BlueBolt.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace BossMod.Stormblood.Trial.T09Seiryu;

// TODO: create and use generic 'line stack' component
class BlueBolt(BossModule module) : Components.GenericBaitAway(module)
{
private Actor? target;

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID == AID.BlueBoltMarker)
{
target = WorldState.Actors.Find(spell.MainTargetID);
CurrentBaits.Add(new(Module.PrimaryActor, target!, new AOEShapeRect(83, 2.5f), Module.WorldState.FutureTime(5.9f)));
}
if ((AID)spell.Action.ID == AID.BlueBolt)
CurrentBaits.Clear();
}

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (CurrentBaits.Count > 0 && actor != target && !Module.FindComponent<RedRush>()!.CurrentBaits.Any(x => x.Target == actor))
hints.AddForbiddenZone(ShapeDistance.InvertedRect(Module.PrimaryActor.Position, (target!.Position - Module.PrimaryActor.Position).Normalized(), 83, default, 2.5f));
}

public override void AddHints(int slot, Actor actor, TextHints hints)
{
if (CurrentBaits.Count > 0 && !Module.FindComponent<RedRush>()!.CurrentBaits.Any(x => x.Target == actor))
{
if (!actor.Position.InRect(Module.PrimaryActor.Position, (target!.Position - Module.PrimaryActor.Position).Normalized(), 83, default, 2.5f))
hints.Add("Stack!");
else
hints.Add("Stack!", false);
}
}

public override void DrawArenaBackground(int pcSlot, Actor pc)
{
foreach (var bait in CurrentBaits)
bait.Shape.Draw(Arena, BaitOrigin(bait), bait.Rotation, ArenaColor.SafeFromAOE);
}

public override void DrawArenaForeground(int pcSlot, Actor pc) { }
}
49 changes: 49 additions & 0 deletions BossMod/Modules/Stormblood/Trial/T09Seiryu/ForbiddenArts.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
namespace BossMod.Stormblood.Trial.T09Seiryu;

// TODO: create and use generic 'line stack' component
class ForbiddenArts(BossModule module) : Components.GenericBaitAway(module)
{
private Actor? target;

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID == AID.ForbiddenArtsMarker)
{
target = WorldState.Actors.Find(spell.MainTargetID);
CurrentBaits.Add(new(Module.PrimaryActor, target!, new AOEShapeRect(84.4f, 4), Module.WorldState.FutureTime(5.2f)));
}
if ((AID)spell.Action.ID is AID.ForbiddenArts1 or AID.ForbiddenArts2)
{
if (++NumCasts == 2)
{
NumCasts = 0;
CurrentBaits.Clear();
}
}
}

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (CurrentBaits.Count > 0 && actor != target)
hints.AddForbiddenZone(ShapeDistance.InvertedRect(Module.PrimaryActor.Position, (target!.Position - Module.PrimaryActor.Position).Normalized(), 84.4f, default, 4));
}

public override void AddHints(int slot, Actor actor, TextHints hints)
{
if (CurrentBaits.Count > 0)
{
if (!actor.Position.InRect(Module.PrimaryActor.Position, (target!.Position - Module.PrimaryActor.Position).Normalized(), 84.4f, default, 4))
hints.Add("Stack!");
else
hints.Add("Stack!", false);
}
}

public override void DrawArenaBackground(int pcSlot, Actor pc)
{
foreach (var bait in CurrentBaits)
bait.Shape.Draw(Arena, BaitOrigin(bait), bait.Rotation, ArenaColor.SafeFromAOE);
}

public override void DrawArenaForeground(int pcSlot, Actor pc) { }
}
25 changes: 25 additions & 0 deletions BossMod/Modules/Stormblood/Trial/T09Seiryu/OnmyoSerpentEyeSigil.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace BossMod.Stormblood.Trial.T09Seiryu;

class OnmyoSerpentEyeSigil(BossModule module) : Components.GenericAOEs(module)
{
private AOEInstance? _aoe;
private static readonly AOEShapeDonut donut = new(6.95f, 30.05f); //adjusted sizes slightly since cast is done by helper with non identical position
private static readonly AOEShapeCircle circle = new(12.05f);

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

public override void OnActorModelStateChange(Actor actor, byte modelState, byte animState1, byte animState2)
{
var activation = Module.WorldState.FutureTime(5.6f);
if (modelState == 32)
_aoe = new(circle, Module.PrimaryActor.Position, default, activation);
if (modelState == 33)
_aoe = new(donut, Module.PrimaryActor.Position, default, activation);
}

public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID is AID.OnmyoSigil2 or AID.SerpentEyeSigil2)
_aoe = null;
}
}
25 changes: 25 additions & 0 deletions BossMod/Modules/Stormblood/Trial/T09Seiryu/SerpentsAscending.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
namespace BossMod.Stormblood.Trial.T09Seiryu;

class SerpentAscending(BossModule module) : Components.GenericTowers(module)
{
public override void OnActorCreated(Actor actor)
{
if ((OID)actor.OID == OID.Towers)
Towers.Add(new(actor.Position, 3, 1, 1, activation: Module.WorldState.FutureTime(7.8f)));
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID is AID.SerpentsFang or AID.SerpentsJaws)
Towers.Clear();
}

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
var forbidden = new List<Func<WPos, float>>();
foreach (var t in Towers.Where(x => !Raid.WithoutSlot().InRadius(x.Position, 3).Any() || actor.Position.InCircle(x.Position, 3)))
forbidden.Add(ShapeDistance.InvertedCircle(t.Position, 3));
if (forbidden.Count > 0)
hints.AddForbiddenZone(p => forbidden.Select(f => f(p)).Max(), Towers[0].Activation);
}
}
96 changes: 75 additions & 21 deletions BossMod/Modules/Stormblood/Trial/T09Seiryu/T09Seiryu.cs
Original file line number Diff line number Diff line change
@@ -1,35 +1,89 @@
namespace BossMod.Stormblood.Trial.T09Seiryu;

class HundredTonzeSwing(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HundredTonzeSwing), new AOEShapeCircle(16));
class CoursingRiverCircleAOE(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.CoursingRiverCircleAOE), 25, kind: Kind.DirForward);
class CoursingRiverRectAOE(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.CoursingRiverRectAOE));
class DragonsWake2(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.DragonsWake2));
class CoursingRiver(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.CoursingRiverAOE), 25, true, kind: Kind.DirForward)
{
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (!Module.FindComponent<Handprint>()!.ActiveAOEs(slot, actor).Any())
foreach (var c in Casters)
hints.AddForbiddenZone(ShapeDistance.Rect(c.CastInfo!.Rotation.AlmostEqual(90.Degrees(), Helpers.RadianConversion) ? c.Position - new WDir(12.5f, 0) : c.Position - new WDir(-12.5f, 0), c.Rotation, 50, default, 20), c.CastInfo!.NPCFinishAt);
}
}

class DragonsWake(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.DragonsWake2));
class FifthElement(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.FifthElement));
class FortuneBladeSigil(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.FortuneBladeSigil), new AOEShapeRect(50, 2, 50));

class GreatTyphoon28(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GreatTyphoon28), new AOEShapeDonut(20, 28));
class GreatTyphoon34(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GreatTyphoon34), new AOEShapeDonut(20, 34));
class GreatTyphoon40(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.GreatTyphoon40), new AOEShapeDonut(20, 40));

class InfirmSoul(BossModule module) : Components.SingleTargetCast(module, ActionID.MakeSpell(AID.InfirmSoul));
class InfirmSoulSpread(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.InfirmSoul), 4);
class InfirmSoul(BossModule module) : Components.BaitAwayCast(module, ActionID.MakeSpell(AID.InfirmSoul), new AOEShapeCircle(4), true)
{
public override void AddGlobalHints(GlobalHints hints)
{
if (CurrentBaits.Count > 0)
hints.Add("Tankbuster cleave");
}
}

class Kanabo1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Kanabo1), new AOEShapeCone(45, 30.Degrees()));
class OnmyoSigil2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.OnmyoSigil2), new AOEShapeCircle(12));
class SerpentDescending(BossModule module) : Components.SpreadFromIcon(module, (uint)IconID.Spreadmarker, ActionID.MakeSpell(AID.SerpentDescending), 5, 6);
class YamaKagura(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.YamaKagura), new AOEShapeRect(60, 3));
class Handprint(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Handprint4), new AOEShapeCone(40, 90.Degrees()));

class SerpentDescending(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.SerpentDescending), 5);
class ForceOfNature1(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.ForceOfNature1), 10)
{
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (Sources(slot, actor).Any())
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 10), Sources(slot, actor).FirstOrDefault().Activation);
}
}
class ForceOfNature2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ForceOfNature2), new AOEShapeCircle(5));
class KanaboBait(BossModule module) : Components.BaitAwayTethers(module, new AOEShapeCone(45, 30.Degrees()), (uint)TetherID.BaitAway, ActionID.MakeSpell(AID.Kanabo1), (uint)OID.IwaNoShiki, 5.9f)
{
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
base.AddAIHints(slot, actor, assignment, hints);
if (CurrentBaits.Any(x => x.Target == actor))
hints.AddForbiddenZone(ShapeDistance.Circle(Module.Center, 19), Module.WorldState.FutureTime(ActivationDelay));
}
}

class SerpentEyeSigil2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SerpentEyeSigil2), new AOEShapeDonut(6, 30));
class KanaboAOE(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Kanabo1), new AOEShapeCone(45, 30.Degrees()));

class YamaKagura(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.YamaKagura), new AOEShapeRect(60, 3));
class RedRush(BossModule module) : Components.BaitAwayTethers(module, new AOEShapeRect(82.6f, 2.5f), (uint)TetherID.BaitAway, ActionID.MakeSpell(AID.RedRush), (uint)OID.AkaNoShiki, 6)
{
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
base.AddAIHints(slot, actor, assignment, hints);
var activation = Module.WorldState.FutureTime(ActivationDelay);
if (CurrentBaits.Any(x => x.Target == actor) && Module.Bounds == T09Seiryu.phase2Bounds)
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 5), activation);
else if (CurrentBaits.Any(x => x.Target == actor) && Module.Bounds == T09Seiryu.phase1Bounds)
hints.AddForbiddenZone(ShapeDistance.Circle(Module.Center, 18.5f), activation);
}
}

class Handprint3(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Handprint3), new AOEShapeCone(20, 90.Degrees()));
class Handprint4(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Handprint4), new AOEShapeCone(40, 90.Degrees()));
class ArenaChange(BossModule module) : BossComponent(module)
{

class ForceOfNature1(BossModule module) : Components.KnockbackFromCastTarget(module, ActionID.MakeSpell(AID.ForceOfNature1), 10, stopAtWall: true, kind: Kind.AwayFromOrigin);
class ForceOfNature2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.ForceOfNature2), new AOEShapeCircle(5));
public override void OnCastFinished(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.StrengthOfSpirit) // in phase 2 the arena no longer got a wall and we need to add back the player hitboxradius
Module.Arena.Bounds = T09Seiryu.phase2Bounds;
}
}

class KanaboTether(BossModule module) : Components.BaitAwayTethers(module, new AOEShapeCone(45, 30.Degrees()), (uint)TetherID.BaitAway, ActionID.MakeSpell(AID.Kanabo1));
class StayInBounds(BossModule module) : BossComponent(module)
{
public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (!Module.InBounds(actor.Position))
hints.AddForbiddenZone(ShapeDistance.InvertedCircle(Module.Center, 3));
}
}

[ModuleInfo(BossModuleInfo.Maturity.WIP, Contributors = "The Combat Reborn Team", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 637, NameID = 7922)]
public class T09Seiryu(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(20));
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "The Combat Reborn Team (Malediktus, LTS)", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 637, NameID = 7922)]
public class T09Seiryu(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), phase1Bounds)
{
public static readonly ArenaBounds phase1Bounds = new ArenaBoundsCircle(19.5f);
public static readonly ArenaBounds phase2Bounds = new ArenaBoundsCircle(20);
}
Loading

0 comments on commit 735f995

Please sign in to comment.