-
Notifications
You must be signed in to change notification settings - Fork 119
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #353 from sourpuh/lyon6
Add WIP Lyon duel 6 module
- Loading branch information
Showing
5 changed files
with
354 additions
and
0 deletions.
There are no files selected for viewing
42 changes: 42 additions & 0 deletions
42
BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/Duel6Lyon.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
namespace BossMod.Shadowbringers.Foray.Duel.Duel6Lyon; | ||
|
||
class Duel6LyonStates : StateMachineBuilder | ||
{ | ||
public Duel6LyonStates(BossModule module) : base(module) | ||
{ | ||
TrivialPhase() | ||
.ActivateOnEnter<OnFire>() | ||
.ActivateOnEnter<WildfiresFury>() | ||
.ActivateOnEnter<HeavenAndEarth>() | ||
.ActivateOnEnter<HeartOfNatureConcentric>() | ||
.ActivateOnEnter<TasteOfBloodAndDuelOrDie>() | ||
.ActivateOnEnter<FlamesMeet>() | ||
.ActivateOnEnter<WindsPeak>() | ||
.ActivateOnEnter<WindsPeakKB>() | ||
.ActivateOnEnter<SplittingRage>() | ||
.ActivateOnEnter<NaturesBlood>() | ||
.ActivateOnEnter<MoveMountains>() | ||
.ActivateOnEnter<WildfireCrucible>(); | ||
} | ||
} | ||
|
||
[ModuleInfo(BossModuleInfo.Maturity.WIP, Contributors = "SourP", GroupType = BossModuleInfo.GroupType.BozjaDuel, GroupID = 778, NameID = 31)] | ||
public class Duel6Lyon(WorldState ws, Actor primary) : BossModule(ws, primary, new(50f, -410f), new ArenaBoundsCircle(20)) | ||
{ | ||
protected override void DrawEnemies(int pcSlot, Actor pc) | ||
{ | ||
var tasteOfBlood = FindComponent<TasteOfBloodAndDuelOrDie>(); | ||
if (tasteOfBlood?.Casters.Count > 0) | ||
{ | ||
foreach (var caster in tasteOfBlood.Casters) | ||
{ | ||
bool isDueler = tasteOfBlood.Duelers.Contains(caster); | ||
Arena.Actor(caster, isDueler ? ArenaColor.Danger : ArenaColor.Enemy, true); | ||
} | ||
} | ||
else | ||
{ | ||
base.DrawEnemies(pcSlot, pc); | ||
} | ||
} | ||
} |
47 changes: 47 additions & 0 deletions
47
BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/Duel6LyonEnums.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
namespace BossMod.Shadowbringers.Foray.Duel.Duel6Lyon; | ||
|
||
public enum OID : uint | ||
{ | ||
Boss = 0x31C1, | ||
Helper = 0x233C, | ||
VermillionFlame = 0x2E8F, | ||
} | ||
|
||
public enum AID : uint | ||
{ | ||
AutoAttack = 6497, // Boss->player, no cast, single-target | ||
WildfiresFury = 0x5D39, // Damage | ||
HarnessFire = 0x5D38, // Boss->self, 3,0s cast, single-target | ||
HeartOfNature = 0x5D24, // Boss->self, 3,0s cast, range 80 circle | ||
CagedHeartOfNature = 0x5D1D, // Boss->self, 3,0s cast, range 10 circle | ||
NaturesPulse1 = 0x5D25, // Helper->self, 4,0s cast, range 10 circle | ||
NaturesPulse2 = 0x5D26, // Helper->self, 5,5s cast, range 10-20 donut | ||
NaturesPulse3 = 0x5D27, // Helper->self, 7,0s cast, range 20-30 donut | ||
TasteOfBlood = 0x5D23, // Boss->self, 4,0s cast, range 40 180-degree cone | ||
SoulAflame = 0x5D2C, | ||
FlamesMeet1 = 0x5D2D, // VermillionFlame->self, 6.5s cast; makes the orb light up | ||
FlamesMeet2 = 0x5D2E, // VermillionFlame->self, 11s cast; actual AOE | ||
HeavenAndEarthCW = 0x5D2F, | ||
HeavenAndEarthCCW = 0x5FEA, | ||
HeavenAndEarthRotate = 0x5D30, // Unused by module | ||
HeavenAndEarthStart = 0x5D31, | ||
HeavenAndEarthMove = 0x5D32, | ||
MoveMountains1 = 0x5D33, // Boss->self, 5s cast, first attack | ||
MoveMountains2 = 0x5D34, // Boss->self, no cast, second attack | ||
MoveMountains3 = 0x5D35, // Helper->self, 5s cast, first line | ||
MoveMountains4 = 0x5D36, // Helper->self, no cast, second line | ||
WindsPeak1 = 0x5D2A, // Boss->self, 3,0s cast, range 5 circle | ||
WindsPeak2 = 0x5D2B, // Helper->self, 4,0s cast, range 50 circle | ||
NaturesBlood1 = 0x5D28, // Helper->self, 7,5s cast, range 4 circle | ||
NaturesBlood2 = 0x5D29, // Helper->self, no cast, range 4 circle | ||
SplittingRage = 0x5D37, // Boss->self, 3,0s cast, range 50 circle | ||
DuelOrDie = 0x5D1C, | ||
WildfireCrucible = 0x5D3B, //enrage, 25s cast time | ||
} | ||
|
||
public enum SID : uint | ||
{ | ||
OnFire = 0x9F3, // Boss->Boss | ||
TemporaryMisdirection = 1422, // Boss->player, extra=0x2D0 | ||
DuelOrDie = 0x9F1, // Boss/Helper->player | ||
} |
178 changes: 178 additions & 0 deletions
178
BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/Duel6LyonGenericAttacks.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
namespace BossMod.Shadowbringers.Foray.Duel.Duel6Lyon; | ||
|
||
class OnFire(BossModule module) : BossComponent(module) | ||
{ | ||
private bool _hasBuff; | ||
private bool _isCasting; | ||
|
||
public override void AddHints(int slot, Actor actor, TextHints hints) | ||
{ | ||
if (_isCasting) | ||
hints.Add("Applies On Fire to Lyon. Use Dispell to remove it"); | ||
if (_hasBuff) | ||
hints.Add("Lyon has 'On Fire'. Use Dispell to remove it!"); | ||
} | ||
|
||
public override void OnStatusGain(Actor actor, ActorStatus status) | ||
{ | ||
if (actor == Module.PrimaryActor && (SID)status.ID == SID.OnFire) | ||
_hasBuff = true; | ||
} | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.HarnessFire) | ||
_isCasting = true; | ||
} | ||
|
||
public override void OnCastFinished(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.HarnessFire) | ||
_isCasting = false; | ||
} | ||
|
||
public override void OnStatusLose(Actor actor, ActorStatus status) | ||
{ | ||
if (actor == Module.PrimaryActor && (SID)status.ID == SID.OnFire) | ||
_hasBuff = false; | ||
} | ||
} | ||
|
||
class WildfiresFury(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.WildfiresFury)); | ||
|
||
class HeavenAndEarth(BossModule module) : Components.GenericRotatingAOE(module) | ||
{ | ||
private Angle _increment; | ||
|
||
private static readonly AOEShapeCone _shape = new(20, 15.Degrees()); | ||
|
||
private int _index; | ||
|
||
private void UpdateIncrement(Angle increment) | ||
{ | ||
_increment = increment; | ||
for (int i = 0; i < Sequences.Count; i++) | ||
{ | ||
var sequence = Sequences[i]; | ||
sequence.Increment = _increment; | ||
Sequences[i] = sequence; | ||
} | ||
} | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.HeavenAndEarthCW) | ||
UpdateIncrement(-30.Degrees()); | ||
else if ((AID)spell.Action.ID == AID.HeavenAndEarthCCW) | ||
UpdateIncrement(30.Degrees()); | ||
|
||
if ((AID)spell.Action.ID == AID.HeavenAndEarthStart) | ||
Sequences.Add(new(_shape, caster.Position, spell.Rotation, _increment, spell.NPCFinishAt, 1.2f, 4)); | ||
} | ||
|
||
public override void OnEventCast(Actor caster, ActorCastEvent spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.HeavenAndEarthMove && Sequences.Count > 0) | ||
{ | ||
AdvanceSequence(_index++ % Sequences.Count, WorldState.CurrentTime); | ||
} | ||
} | ||
} | ||
|
||
class HeartOfNatureConcentric(BossModule module) : Components.ConcentricAOEs(module, _shapes) | ||
{ | ||
private static readonly AOEShape[] _shapes = [new AOEShapeCircle(10), new AOEShapeDonut(10, 20), new AOEShapeDonut(20, 30)]; | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.NaturesPulse1) | ||
AddSequence(caster.Position, spell.NPCFinishAt); | ||
} | ||
|
||
public override void OnEventCast(Actor caster, ActorCastEvent spell) | ||
{ | ||
if (Sequences.Count > 0) | ||
{ | ||
var order = (AID)spell.Action.ID switch | ||
{ | ||
AID.NaturesPulse1 => 0, | ||
AID.NaturesPulse2 => 1, | ||
AID.NaturesPulse3 => 2, | ||
_ => -1 | ||
}; | ||
AdvanceSequence(order, caster.Position); | ||
} | ||
} | ||
} | ||
|
||
class CagedHeartOfNature(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.CagedHeartOfNature), new AOEShapeCircle(6)); | ||
|
||
class WindsPeak(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.WindsPeak1), new AOEShapeCircle(5)); | ||
|
||
class WindsPeakKB(BossModule module) : Components.Knockback(module) | ||
{ | ||
private DateTime _time; | ||
private bool _watched; | ||
private DateTime _activation; | ||
|
||
public override IEnumerable<Source> Sources(int slot, Actor actor) | ||
{ | ||
if (_watched && WorldState.CurrentTime < _time.AddSeconds(4.4f)) | ||
yield return new(Module.PrimaryActor.Position, 15, _activation); | ||
} | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.WindsPeak1) | ||
{ | ||
_watched = true; | ||
_time = WorldState.CurrentTime; | ||
_activation = spell.NPCFinishAt; | ||
} | ||
} | ||
} | ||
|
||
class SplittingRage(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.SplittingRage), "Applies temporary misdirection"); | ||
|
||
class NaturesBlood(BossModule module) : Components.Exaflare(module, 4) | ||
{ | ||
class LineWithActor : Line | ||
{ | ||
public Actor Caster; | ||
|
||
public LineWithActor(Actor caster) | ||
{ | ||
Next = caster.Position; | ||
Advance = 6 * caster.Rotation.ToDirection(); | ||
NextExplosion = caster.CastInfo!.NPCFinishAt; | ||
TimeToMove = 1.1f; | ||
ExplosionsLeft = 7; | ||
MaxShownExplosions = 3; | ||
Caster = caster; | ||
} | ||
} | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID is AID.NaturesBlood1) | ||
Lines.Add(new LineWithActor(caster)); | ||
} | ||
|
||
public override void OnEventCast(Actor caster, ActorCastEvent spell) | ||
{ | ||
if (Lines.Count > 0 && (AID)spell.Action.ID is AID.NaturesBlood1 or AID.NaturesBlood2) | ||
{ | ||
int index = Lines.FindIndex(item => ((LineWithActor)item).Caster == caster); | ||
AdvanceLine(Lines[index], caster.Position); | ||
if (Lines[index].ExplosionsLeft == 0) | ||
Lines.RemoveAt(index); | ||
} | ||
} | ||
} | ||
|
||
class MoveMountains(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.MoveMountains3), new AOEShapeRect(40, 3)) | ||
{ | ||
// TODO predict rotation | ||
} | ||
|
||
class WildfireCrucible(BossModule module) : Components.CastHint(module, ActionID.MakeSpell(AID.WildfireCrucible), "Enrage!", true); |
33 changes: 33 additions & 0 deletions
33
BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/FlamesMeet.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
| ||
namespace BossMod.Shadowbringers.Foray.Duel.Duel6Lyon; | ||
class FlamesMeet(BossModule module) : Components.GenericAOEs(module) | ||
{ | ||
private readonly List<AOEInstance> _aoes = []; | ||
private static readonly AOEShapeCross _shape = new(40, 7); | ||
|
||
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) | ||
{ | ||
for (int i = 0; i < _aoes.Count; i++) | ||
{ | ||
AOEInstance aoe = _aoes[i]; | ||
if (i == 0) | ||
aoe.Color = ArenaColor.Danger; | ||
yield return aoe; | ||
// Only show the first 2 so it's obvious which one to go to. | ||
if (i == 1) | ||
break; | ||
} | ||
} | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.FlamesMeet2) | ||
_aoes.Add(new(_shape, caster.Position)); | ||
} | ||
|
||
public override void OnCastFinished(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.FlamesMeet2 && _aoes.Count > 0) | ||
_aoes.RemoveAt(0); | ||
} | ||
} |
54 changes: 54 additions & 0 deletions
54
BossMod/Modules/Shadowbringers/Foray/Duel/Duel6Lyon/TasteOfBloodAndDuelOrDie.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
using BossMod.Components; | ||
|
||
namespace BossMod.Shadowbringers.Foray.Duel.Duel6Lyon; | ||
|
||
class TasteOfBloodAndDuelOrDie(BossModule module) : GenericAOEs(module) | ||
{ | ||
private readonly AOEShape _tasteOfBloodShape = new AOEShapeCone(40, 90.Degrees()); | ||
public readonly List<Actor> Casters = []; | ||
public readonly List<Actor> Duelers = []; | ||
|
||
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) | ||
{ | ||
foreach (var caster in Casters) | ||
{ | ||
// If the caster did Duel Or Die, the player must get hit by their attack. | ||
// This is represented by pointing the AOE behind the caster so their front is safe. | ||
Angle angle = Duelers.Contains(caster) ? caster.Rotation + 180.Degrees() : caster.Rotation; | ||
yield return new AOEInstance(_tasteOfBloodShape, caster.Position, angle); | ||
} | ||
} | ||
|
||
public override void OnCastStarted(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.TasteOfBlood) | ||
Casters.Add(caster); | ||
|
||
if ((AID)spell.Action.ID == AID.DuelOrDie) | ||
Duelers.Add(caster); | ||
} | ||
|
||
public override void OnCastFinished(Actor caster, ActorCastInfo spell) | ||
{ | ||
if ((AID)spell.Action.ID == AID.TasteOfBlood) | ||
{ | ||
Casters.Remove(caster); | ||
Duelers.Remove(caster); | ||
} | ||
} | ||
|
||
public override void DrawArenaForeground(int pcSlot, Actor pc) | ||
{ | ||
foreach (var caster in Casters) | ||
{ | ||
bool isDueler = Duelers.Contains(caster); | ||
Arena.Actor(caster, isDueler ? ArenaColor.Danger : ArenaColor.Enemy, true); | ||
} | ||
} | ||
|
||
public override void AddHints(int slot, Actor actor, TextHints hints) | ||
{ | ||
if (Duelers.Count > 0) | ||
hints.Add($"Get hit by {Duelers.Count} Duel or Die Taste Of Blood"); | ||
} | ||
} |