From f98aaf26a9fa769a78d9107dddc396e9d92371a9 Mon Sep 17 00:00:00 2001 From: Andrew Gilewsky Date: Thu, 2 Jan 2025 20:31:49 +0000 Subject: [PATCH] Chaotic improvements. --- .../Ch01CloudOfDarkness.cs | 2 +- .../Ch01CloudOfDarknessEnums.cs | 8 +- .../Ch01CloudOfDarknessStates.cs | 12 +- .../Ch01CloudOfDarkness/DelugeOfDarkness.cs | 143 +++++++++++++++--- BossMod/Replay/Visualization/EventList.cs | 9 +- 5 files changed, 139 insertions(+), 35 deletions(-) diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarkness.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarkness.cs index bf237c14e0..511b8792c3 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarkness.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarkness.cs @@ -10,7 +10,7 @@ class Atomos(BossModule module) : Components.Adds(module, (uint)OID.Atomos); class LoomingChaos(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.LoomingChaosAOE)); // TODO: tankswap hints component for phase1 -// TODO: phase 2 squares, break timer, teleport zones, outer ring safety +// TODO: phase 2 teleport zones? // TODO: grim embrace / curse of darkness prevent turning [ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1010, NameID = 13624)] public class Ch01CloudOfDarkness(WorldState ws, Actor primary) : BossModule(ws, primary, DefaultCenter, InitialBounds) diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessEnums.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessEnums.cs index 123d9e9d39..e7def47810 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessEnums.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessEnums.cs @@ -10,12 +10,10 @@ public enum OID : uint StygianTendrils = 0x4622, // R1.200, x0 (spawn during fight), evil seed CloudletOfDarkness = 0x4623, // R3.000, x0 (spawn during fight), criss-cross source BallOfNaught = 0x4624, // R1.500, x1, (en)death sphere - //_Gen_DreadGale = 0x4625, // R1.200, x1 + //DreadGale = 0x4625, // R1.200, x1, ??? SinisterEye = 0x4626, // R2.800, x2, break gaze source AtomosSpawnPoint = 0x1EBD7B, // R0.500, x0 (spawn during fight), EventObj type EvilSeed = 0x1E9B3B, // R0.500, x0 (spawn during fight), EventObj type - //_Gen_Actor1e8536 = 0x1E8536, // R2.000, x1, EventObj type - //_Gen_Exit = 0x1E850B, // R0.500, x1, EventObj type } public enum AID : uint @@ -156,11 +154,11 @@ public enum SID : uint //_Gen_StabWound = 3062, // none->player, extra=0x0 //_Gen_ThornyVine = 445, // none->player, extra=0x0 //_Gen_ForwardWithThee = 2240, // none->player, extra=0x33F - //_Gen_Stun = 149, // none->player, extra=0x0 //_Gen_BackWithThee = 2241, // none->player, extra=0x340 //_Gen_LeftWithThee = 2242, // none->player, extra=0x341 - //_Gen_Stun = 2656, // none->player, extra=0x0 //_Gen_RightWithThee = 2243, // none->player, extra=0x342 + //_Gen_Stun = 149, // none->player, extra=0x0 + //_Gen_Stun = 2656, // none->player, extra=0x0 } public enum IconID : uint diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessStates.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessStates.cs index 9443f0c0de..c688c9c976 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessStates.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/Ch01CloudOfDarknessStates.cs @@ -72,8 +72,8 @@ private void Subphase1Variant2End(uint id, float delay) private void Subphase2(uint id, float delay) { DelugeOfDarkness2(id, delay); - DarkDominion(id + 0x10000, 9.3f); - ThirdArtOfDarknessParticleConcentration(id + 0x20000, 4); + DarkDominion(id + 0x10000, 9.3f); // note: 1s after cast ends, outer ring becomes dangerous + ThirdArtOfDarknessParticleConcentration(id + 0x20000, 4); // note: 3s after towers resolve, outer ring becomes normal GhastlyGloom(id + 0x30000, 12.3f); CurseOfDarkness(id + 0x40000, 8.3f); EvilSeedChaosCondensedDiffusiveForceParticleBeam(id + 0x50000, 9.9f); @@ -82,8 +82,8 @@ private void Subphase2(uint id, float delay) CurseOfDarkness(id + 0x100000, 11.9f); ParticleConcentrationPhaser(id + 0x110000, 4.2f); - DarkDominion(id + 0x120000, 1); - FeintParticleBeamThirdActOfDarkness(id + 0x130000, 3.1f); + DarkDominion(id + 0x120000, 1); // note: 1s after cast ends, outer ring becomes dangerous + FeintParticleBeamThirdActOfDarkness(id + 0x130000, 3.1f); // note: 2.5s after act of darkness resolves, outer ring becomes normal GhastlyGloom(id + 0x140000, 11.4f); PhaserChaosCondensedDiffusiveForceParticleBeam(id + 0x150000, 3.4f); FloodOfDarknessAdds(id + 0x160000, 3); @@ -222,6 +222,8 @@ private void DelugeOfDarkness2(uint id, float delay) ComponentCondition(id + 0x10, 4.2f, comp => comp.ActiveActors.Any(), "Platform adds") .ActivateOnEnter() .ActivateOnEnter() + .ActivateOnEnter() + .ActivateOnEnter() .ActivateOnEnter(); // overlaps with multiple mechanics } @@ -387,6 +389,8 @@ private void FloodOfDarkness2(uint id, float delay) CastStart(id, AID.FloodOfDarkness2, delay, "Adds disappear") .DeactivateOnExit() .DeactivateOnExit() + .DeactivateOnExit() + .DeactivateOnExit() .DeactivateOnExit(); CastEnd(id + 1, 7, "Raidwide + arena transition") .OnExit(() => Module.Arena.Bounds = Ch01CloudOfDarkness.InitialBounds) diff --git a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/DelugeOfDarkness.cs b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/DelugeOfDarkness.cs index c0e0dfe56a..f3735d9345 100644 --- a/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/DelugeOfDarkness.cs +++ b/BossMod/Modules/Dawntrail/Chaotic/Ch01CloudOfDarkness/DelugeOfDarkness.cs @@ -1,5 +1,10 @@ namespace BossMod.Dawntrail.Chaotic.Ch01CloudOfDarkness; +// envcontrols: +// 00 = main bounds telegraph +// - 00200010 - phase 1 +// - 00020001 - phase 2 +// - 00040004 - remove telegraph (note that actual bounds are controlled by something else!) class DelugeOfDarkness1(BossModule module) : Components.GenericAOEs(module) { private AOEInstance? _aoe; @@ -15,30 +20,6 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) } } -// envcontrols: -// 00 = main bounds telegraph -// - 00200010 - phase 1 -// - 00020001 - phase 2 -// - 00040004 - remove telegraph (note that actual bounds are controlled by something else!) -// 02 = outer ring -// - 00020001 - become dangerous -// - 00080004 - restore to normal -// 03-1E = mid squares -// - 08000001 - init -// - 00200010 - become occupied -// - 02000001 - become free -// - 00800040 - player is standing for too long, will break soon -// - 00080004 - break -// - 00020001 - repair -// - arrangement: -// 04 0B -// 03 05 06 07 0E 0D 0C 0A -// 08 0F -// 09 10 -// 17 1E -// 16 1D -// 11 13 14 15 1C 1B 1A 18 -// 12 19 class DelugeOfDarkness2(BossModule module) : Components.GenericAOEs(module) { private AOEInstance? _aoe; @@ -53,3 +34,117 @@ public override void OnCastStarted(Actor caster, ActorCastInfo spell) _aoe = new(_shape, Module.Center, default, Module.CastFinishAt(spell)); } } + +class Phase2OuterRing(BossModule module) : Components.GenericAOEs(module) +{ + public bool Dangerous; + private AOEInstance? _aoe; + + private static readonly AOEShapeDonut _shape = new(34, 40); + + public override IEnumerable ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(_aoe); + + public override void OnCastStarted(Actor caster, ActorCastInfo spell) + { + if ((AID)spell.Action.ID == AID.DarkDominion) + _aoe = new(_shape, Module.Center, default, Module.CastFinishAt(spell, 1.1f)); + } + + public override void OnEventEnvControl(byte index, uint state) + { + if (index != 2) + return; + switch (state) + { + case 0x00020001: + Dangerous = true; + break; + case 0x00080004: + Dangerous = false; + _aoe = null; + break; + default: + ReportError($"Unexpected envcontrol {state:X8}"); + break; + } + } +} + +class Phase2InnerCells(BossModule module) : BossComponent(module) +{ + private readonly DateTime[] _breakTime = new DateTime[28]; + + public override void AddHints(int slot, Actor actor, TextHints hints) + { + var cell = CellIndex(actor.Position - Module.Center) - 3; + var breakTime = cell >= 0 && cell < _breakTime.Length ? _breakTime[cell] : default; + if (breakTime != default) + { + var remaining = Math.Max(0, (breakTime - WorldState.CurrentTime).TotalSeconds); + hints.Add($"Cell breaks in {remaining:f1}s", remaining < 10); + } + } + + public override void OnEventEnvControl(byte index, uint state) + { + // 03-1E = mid squares + // - 08000001 - init + // - 00200010 - become occupied + // - 02000001 - become free + // - 00800040 - player is standing for too long (38s), will break soon (in 6s) + // - 00080004 - break + // - 00020001 - repair + // - arrangement: + // 04 0B + // 03 05 06 07 0E 0D 0C 0A + // 08 0F + // 09 10 + // 17 1E + // 16 1D + // 11 13 14 15 1C 1B 1A 18 + // 12 19 + if (index is < 3 or > 30) + return; + _breakTime[index - 3] = state switch + { + 0x00200010 => WorldState.FutureTime(44), + 0x00800040 => WorldState.FutureTime(6), + _ => default, + }; + } + + private int CoordinateToCell(float c) => (int)Math.Floor(c / 6); + private int CellIndex(WDir offset) => CellIndex(CoordinateToCell(offset.X), CoordinateToCell(offset.Z)); + private int CellIndex(int x, int y) => (x, y) switch + { + (-4, -3) => 3, + (-3, -4) => 4, + (-3, -3) => 5, + (-2, -3) => 6, + (-1, -3) => 7, + (-3, -2) => 8, + (-3, -1) => 9, + (+3, -3) => 10, + (+2, -4) => 11, + (+2, -3) => 12, + (+1, -3) => 13, + (+0, -3) => 14, + (+2, -2) => 15, + (+2, -1) => 16, + (-4, +2) => 17, + (-3, +3) => 18, + (-3, +2) => 19, + (-2, +2) => 20, + (-1, +2) => 21, + (-3, +1) => 22, + (-3, +0) => 23, + (+3, +2) => 24, + (+2, +3) => 25, + (+2, +2) => 26, + (+1, +2) => 27, + (+0, +2) => 28, + (+2, +1) => 29, + (+2, +0) => 30, + _ => -1 + }; +} diff --git a/BossMod/Replay/Visualization/EventList.cs b/BossMod/Replay/Visualization/EventList.cs index 760bc5734c..968c6bd5d6 100644 --- a/BossMod/Replay/Visualization/EventList.cs +++ b/BossMod/Replay/Visualization/EventList.cs @@ -124,7 +124,14 @@ private void DrawContents(Replay.Encounter? filter, BossModuleRegistry.Info? mod foreach (var n in _tree.Node("EnvControls", !envControls.Any())) { - _tree.LeafNodes(envControls, ec => $"{tp(ec.Timestamp)}: {ec.Index:X2} = {ec.State:X8}"); + foreach (var n2 in _tree.Node("All")) + { + _tree.LeafNodes(envControls, ec => $"{tp(ec.Timestamp)}: {ec.Index:X2} = {ec.State:X8}"); + } + foreach (var index in _tree.Nodes(new SortedSet(envControls.Select(ec => ec.Index)), index => new($"Index {index:X2}"))) + { + _tree.LeafNodes(envControls.Where(ec => ec.Index == index), ec => $"{tp(ec.Timestamp)}: {ec.Index:X2} = {ec.State:X8}"); + } } }