Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Seiryu nm verified #126

Merged
merged 1 commit into from
Jun 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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