Skip to content

Commit

Permalink
M2S
Browse files Browse the repository at this point in the history
  • Loading branch information
awgil committed Aug 5, 2024
1 parent 45c5580 commit adaebcd
Show file tree
Hide file tree
Showing 10 changed files with 800 additions and 0 deletions.
25 changes: 25 additions & 0 deletions BossMod/Components/SharedTankbuster.cs
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,28 @@ public override void OnCastFinished(Actor caster, ActorCastInfo spell)
Source = Target = null;
}
}

// shared tankbuster at icon
public class IconSharedTankbuster(BossModule module, uint iconId, ActionID aid, AOEShape shape, float activationDelay = 5.1f, bool originAtTarget = false) : GenericSharedTankbuster(module, aid, shape, originAtTarget)
{
public IconSharedTankbuster(BossModule module, uint iconId, ActionID aid, float radius, float activationDelay = 5.1f) : this(module, iconId, aid, new AOEShapeCircle(radius), activationDelay, true) { }

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

public override void OnEventIcon(Actor actor, uint iconID)
{
if (iconID == iconId)
{
Source = BaitSource(actor);
Target = actor;
Activation = WorldState.FutureTime(activationDelay);
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
base.OnEventCast(caster, spell);
if (spell.Action == WatchedAction)
Source = Target = null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
namespace BossMod.Dawntrail.Savage.RM02SHoneyBLovely;

class DropSplashOfVenom(BossModule module) : Components.UniformStackSpread(module, 6, 6, 2, 2, alwaysShowSpreads: true)
{
public enum Mechanic { None, Pairs, Spread }

private Mechanic NextMechanic;

public override void AddGlobalHints(GlobalHints hints)
{
if (NextMechanic != Mechanic.None)
hints.Add(NextMechanic.ToString());
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
switch ((AID)spell.Action.ID)
{
case AID.SplashOfVenom:
case AID.SpreadLove:
NextMechanic = Mechanic.Spread;
break;
case AID.DropOfVenom:
case AID.DropOfLove:
NextMechanic = Mechanic.Pairs;
break;
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
switch ((AID)spell.Action.ID)
{
case AID.TemptingTwistAOE:
case AID.HoneyBeelineAOE:
case AID.TemptingTwistBeatAOE:
case AID.HoneyBeelineBeatAOE:
switch (NextMechanic)
{
case Mechanic.Pairs:
// note: it's random whether dd or supports are hit, select supports arbitrarily
AddStacks(Raid.WithoutSlot(true).Where(p => p.Class.IsSupport()), WorldState.FutureTime(4.5f));
break;
case Mechanic.Spread:
AddSpreads(Raid.WithoutSlot(true), WorldState.FutureTime(4.5f));
break;
}
break;
case AID.SplashOfVenomAOE:
case AID.DropOfVenomAOE:
case AID.SpreadLoveAOE:
case AID.DropOfLoveAOE:
Spreads.Clear();
Stacks.Clear();
NextMechanic = Mechanic.None;
break;
}
}
}

class TemptingTwist(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TemptingTwistAOE), new AOEShapeDonut(6, 30)); // TODO: verify inner radius
class TemptingTwistBeat(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.TemptingTwistBeatAOE), new AOEShapeDonut(6, 30)); // TODO: verify inner radius
class HoneyBeeline(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HoneyBeelineAOE), new AOEShapeRect(30, 7, 30));
class HoneyBeelineBeat(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.HoneyBeelineBeatAOE), new AOEShapeRect(30, 7, 30));
class PoisonCloudSplinter(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.PoisonCloudSplinter), new AOEShapeCircle(8));
class SweetheartSplinter(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.SweetheartSplinter), new AOEShapeCircle(8));
187 changes: 187 additions & 0 deletions BossMod/Modules/Dawntrail/Savage/RM02SHoneyBLovely/HoneyBLiveBeat.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
namespace BossMod.Dawntrail.Savage.RM02SHoneyBLovely;

class HoneyBLiveBeat1(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.HoneyBLiveBeat1AOE));
class HoneyBLiveBeat2(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.HoneyBLiveBeat2AOE));
class HoneyBLiveBeat3(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.HoneyBLiveBeat3AOE));

class HoneyBLiveHearts(BossModule module) : BossComponent(module)
{
public int[] Hearts = new int[PartyState.MaxPartySize];

public override void OnStatusGain(Actor actor, ActorStatus status)
{
var hearts = NumHearts((SID)status.ID);
if (hearts >= 0 && Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0)
Hearts[slot] = hearts;
}

public override void OnStatusLose(Actor actor, ActorStatus status)
{
var hearts = NumHearts((SID)status.ID);
if (hearts >= 0 && Raid.FindSlot(actor.InstanceID) is var slot && slot >= 0 && Hearts[slot] == hearts)
Hearts[slot] = 0;
}

private int NumHearts(SID sid) => sid switch
{
SID.Hearts0 => 0,
SID.Hearts1 => 1,
SID.Hearts2 => 2,
SID.Hearts3 => 3,
SID.Hearts4 => 4,
_ => -1
};
}

abstract class Fracture(BossModule module) : Components.CastTowers(module, ActionID.MakeSpell(AID.Fracture), 4)
{
protected abstract BitMask UpdateForbidden();

public override void Update()
{
var forbidden = UpdateForbidden();
foreach (ref var t in Towers.AsSpan())
t.ForbiddenSoakers = forbidden;
}
}

class Fracture1(BossModule module) : Fracture(module)
{
private readonly HoneyBLiveHearts? _hearts = module.FindComponent<HoneyBLiveHearts>();

protected override BitMask UpdateForbidden()
{
var forbidden = new BitMask();
if (_hearts != null)
for (int i = 0; i < _hearts.Hearts.Length; ++i)
if (_hearts.Hearts[i] == 3)
forbidden.Set(i);
return forbidden;
}
}

class Fracture2(BossModule module) : Fracture(module)
{
private BitMask _spreads;
private readonly HoneyBLiveHearts? _hearts = module.FindComponent<HoneyBLiveHearts>();

protected override BitMask UpdateForbidden()
{
var forbidden = _spreads;
if (_hearts != null)
for (int i = 0; i < _hearts.Hearts.Length; ++i)
if (_hearts.Hearts[i] > 1)
forbidden.Set(i);
return forbidden;
}

public override void OnEventIcon(Actor actor, uint iconID)
{
// spread targets should never take towers
if (iconID == (uint)IconID.Heartsore)
_spreads.Set(Raid.FindSlot(actor.InstanceID));
}
}

class Fracture3 : Fracture
{
private BitMask _defamations;

public Fracture3(BossModule module) : base(module)
{
var bigBurst = module.FindComponent<HoneyBLiveBeat3BigBurst>();
if (bigBurst != null)
{
var order = bigBurst.NumCasts == 0 ? 1 : 2;
_defamations = Raid.WithSlot(true).WhereSlot(i => bigBurst.Order[i] == order).Mask();
}
}

protected override BitMask UpdateForbidden() => _defamations;
}

class Loveseeker(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LoveseekerAOE), new AOEShapeCircle(10));
class HeartStruck(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.HeartStruck), 6);
class Heartsore(BossModule module) : Components.SpreadFromIcon(module, (uint)IconID.Heartsore, ActionID.MakeSpell(AID.Heartsore), 6, 7.1f);

class Sweetheart(BossModule module) : Components.GenericAOEs(module)
{
private readonly List<Actor> _adds = [];

private static readonly AOEShapeCircle _shape = new(2);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _adds.Where(a => !a.IsDestroyed).Select(a => new AOEInstance(_shape, a.Position));

public override void OnActorPlayActionTimelineEvent(Actor actor, ushort id)
{
if ((OID)actor.OID == OID.Sweetheart && id == 0x11D3)
_adds.Add(actor);
}
}

abstract class Heartsick(BossModule module, bool roles) : Components.StackWithIcon(module, (uint)IconID.Heartsick, ActionID.MakeSpell(AID.Heartsick), 6, 7, roles ? 2 : 4)
{
private readonly HoneyBLiveHearts? _hearts = module.FindComponent<HoneyBLiveHearts>();

public override void Update()
{
if (_hearts != null)
{
foreach (ref var stack in Stacks.AsSpan())
{
for (int i = 0; i < _hearts.Hearts.Length; ++i)
{
stack.ForbiddenPlayers[i] = roles
? (_hearts.Hearts[i] > 0 || stack.Target.Class.IsSupport() != Raid[i]?.Class.IsSupport())
: _hearts.Hearts[i] == 3;
}
}
}
}
}
class Heartsick1(BossModule module) : Heartsick(module, false);
class Heartsick2(BossModule module) : Heartsick(module, true);

class HoneyBLiveBeat3BigBurst(BossModule module) : Components.UniformStackSpread(module, 0, 14, alwaysShowSpreads: true)
{
public int NumCasts;
public int[] Order = new int[PartyState.MaxPartySize];
private readonly DateTime[] _activation = new DateTime[2];

public override void AddHints(int slot, Actor actor, TextHints hints)
{
if (Order[slot] != 0)
hints.Add($"Order: {Order[slot]}", false);
base.AddHints(slot, actor, hints);
}

public override void OnStatusGain(Actor actor, ActorStatus status)
{
if ((SID)status.ID == SID.PoisonNPop)
{
var order = (status.ExpireAt - WorldState.CurrentTime).TotalSeconds > 30 ? 1 : 0;
_activation[order] = status.ExpireAt;
var slot = Raid.FindSlot(actor.InstanceID);
if (slot >= 0)
Order[slot] = order + 1;
}
}

public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
if ((AID)spell.Action.ID == AID.Fracture && Spreads.Count == 0)
{
var order = NumCasts == 0 ? 1 : 2;
AddSpreads(Raid.WithSlot(true).WhereSlot(i => Order[i] == order).Actors(), _activation[order - 1]);
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID == AID.HoneyBLiveBeat3BigBurst)
{
++NumCasts;
Spreads.Clear();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace BossMod.Dawntrail.Savage.RM02SHoneyBLovely;

class StingingSlash(BossModule module) : Components.BaitAwayIcon(module, new AOEShapeCone(50, 45.Degrees()), (uint)IconID.StingingSlash, ActionID.MakeSpell(AID.StingingSlashAOE));
class KillerSting(BossModule module) : Components.IconSharedTankbuster(module, (uint)IconID.KillerSting, ActionID.MakeSpell(AID.KillerStingAOE), 6);
class BlindingLoveBait(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BlindingLoveBaitAOE), new AOEShapeRect(50, 4));
class BlindingLoveCharge1(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BlindingLoveCharge1AOE), new AOEShapeRect(45, 5));
class BlindingLoveCharge2(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.BlindingLoveCharge2AOE), new AOEShapeRect(45, 5));
class PoisonStingBait(BossModule module) : Components.BaitAwayCast(module, ActionID.MakeSpell(AID.PoisonStingAOE), new AOEShapeCircle(6), true);
class PoisonStingVoidzone(BossModule module) : Components.PersistentVoidzone(module, 6, m => m.Enemies(OID.PoisonStingVoidzone).Where(z => z.EventState != 7));
class BeeSting(BossModule module) : Components.StackWithCastTargets(module, ActionID.MakeSpell(AID.BeeStingAOE), 6, 4);

public class RM02SHoneyBLovely(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsCircle(20));
Loading

0 comments on commit adaebcd

Please sign in to comment.