Skip to content

Commit

Permalink
New dungeon.
Browse files Browse the repository at this point in the history
  • Loading branch information
awgil committed Nov 25, 2024
1 parent a4958ea commit 1b3eb7c
Show file tree
Hide file tree
Showing 10 changed files with 623 additions and 41 deletions.
18 changes: 16 additions & 2 deletions BossMod/Components/Adds.cs
Original file line number Diff line number Diff line change
@@ -1,24 +1,38 @@
namespace BossMod.Components;

// generic component used for drawing adds
public class Adds(BossModule module, uint oid) : BossComponent(module)
public class Adds(BossModule module, uint oid, int priority = 0) : BossComponent(module)
{
public readonly IReadOnlyList<Actor> Actors = module.Enemies(oid);
public IEnumerable<Actor> ActiveActors => Actors.Where(a => a.IsTargetable && !a.IsDead);

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (priority > 0)
hints.PrioritizeTargetsByOID(oid, priority);
}

public override void DrawArenaForeground(int pcSlot, Actor pc)
{
Arena.Actors(Actors, ArenaColor.Enemy);
}
}

// generic component used for drawing multiple adds with multiple oids, when it's not useful to distinguish between them
public class AddsMulti(BossModule module, uint[] oids) : BossComponent(module)
public class AddsMulti(BossModule module, uint[] oids, int priority = 0) : BossComponent(module)
{
public readonly uint[] OIDs = oids;
public readonly List<Actor> Actors = [];
public IEnumerable<Actor> ActiveActors => Actors.Where(a => a.IsTargetable && !a.IsDead);

public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
if (priority > 0)
foreach (var e in hints.PotentialTargets)
if (OIDs.Contains(e.Actor.OID))
e.Priority = Math.Max(priority, e.Priority);
}

public override void DrawArenaForeground(int pcSlot, Actor pc)
{
Arena.Actors(Actors, ArenaColor.Enemy);
Expand Down
3 changes: 2 additions & 1 deletion BossMod/Components/StackSpread.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ public record struct Spread(
public bool AlwaysShowSpreads = alwaysShowSpreads; // if false, we only shown own spread radius for spread targets - this reduces visual clutter
public bool RaidwideOnResolve = raidwideOnResolve; // if true, assume even if mechanic is correctly resolved everyone will still take damage
public bool IncludeDeadTargets = includeDeadTargets; // if false, stacks & spreads with dead targets are ignored
public int ExtraAISpreadThreshold = 1;
public List<Stack> Stacks = [];
public List<Spread> Spreads = [];

Expand Down Expand Up @@ -89,7 +90,7 @@ public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignme
// TODO: think how to improve this, current implementation works, but isn't particularly good - e.g. nearby players tend to move to same spot, turn around, etc.
// ideally we should provide per-mechanic spread spots, but for simple cases we should try to let melee spread close and healers/rdd spread far from main target...
foreach (var spreadFrom in ActiveSpreads.Where(s => s.Target != actor))
hints.AddForbiddenZone(ShapeDistance.Circle(spreadFrom.Target.Position, spreadFrom.Radius + 1), spreadFrom.Activation);
hints.AddForbiddenZone(ShapeDistance.Circle(spreadFrom.Target.Position, spreadFrom.Radius + ExtraAISpreadThreshold), spreadFrom.Activation);

foreach (var avoid in ActiveStacks.Where(s => s.Target != actor && s.ForbiddenPlayers[slot]))
hints.AddForbiddenZone(ShapeDistance.Circle(avoid.Target.Position, avoid.Radius), avoid.Activation);
Expand Down
44 changes: 22 additions & 22 deletions BossMod/Debug/DebugInput.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,13 @@ internal sealed unsafe class DebugInput : IDisposable

private readonly UITree _tree = new();
private readonly WorldState _ws;
//private readonly ActionManagerEx _amex;
private readonly AIHints _hints;
private readonly MovementOverride _move;
//private readonly AI.AIController _navi;
private Vector2 _dest;
private Vector3 _prevPos;
private float _prevSpeed;
private bool _jump;
private bool _wannaMove;
private float _moveDir;
private bool _gamepadAxisOverrideEnable;
private float _gamepadAxisOverrideAngle;
private int _gamepadButtonOverride = -1;
Expand All @@ -106,11 +107,13 @@ internal sealed unsafe class DebugInput : IDisposable
//private float _pmcCameraSpeedH;
//private float _pmcCameraSpeedV;

public DebugInput(RotationModuleManager autorot)
public DebugInput(RotationModuleManager autorot, MovementOverride move)
{
_convertVirtualKey = Service.KeyState.GetType().GetMethod("ConvertVirtualKey", BindingFlags.NonPublic | BindingFlags.Instance)!.CreateDelegate<ConvertVirtualKeyDelegate>(Service.KeyState);
_getKeyRef = Service.KeyState.GetType().GetMethod("GetRefValue", BindingFlags.NonPublic | BindingFlags.Instance)!.CreateDelegate<GetRefValueDelegate>(Service.KeyState);
_ws = autorot.Bossmods.WorldState;
_hints = autorot.Hints;
_move = move;
//_amex = autorot.ActionManager;
//_navi = new(_amex);

Expand Down Expand Up @@ -149,29 +152,26 @@ public void Draw()
ImGui.TextUnformatted($"Speed={speedAbs:f3}, SpeedH={speed.XZ().Length():f3}, SpeedV={speed.Y:f3}, Accel={accel:f3}, Azimuth={Angle.FromDirection(new(speed.XZ()))}, Altitude={Angle.FromDirection(new(speed.Y, speed.XZ().Length()))}");
//Service.Log($"Speed: {speedAbs:f3}, accel: {accel:f3}");

ImGui.Checkbox("Jump!", ref _jump);
ImGui.InputFloat2("Destination", ref _dest);
ImGui.SliderFloat("Move direction", ref _moveDir, -180, 180);
ImGui.SameLine();
if (ImGui.Button("Move!"))
if (ImGui.Button(_wannaMove ? "Cancel move" : "Move!"))
{
//_navi.NaviTargetPos = new(_dest);

////var toTarget = _navi.NaviTargetPos.Value - curPos;
////_navi.NaviTargetRot = toTarget.Normalized();

////var cameraFacing = _navi.CameraFacing;
////var dot = cameraFacing.Dot(_navi.NaviTargetRot.Value);
////if (dot < -0.707107f)
//// _navi.NaviTargetRot = -_navi.NaviTargetRot.Value;
////else if (dot < 0.707107f)
//// _navi.NaviTargetRot = cameraFacing.OrthoL().Dot(_navi.NaviTargetRot.Value) > 0 ? _navi.NaviTargetRot.Value.OrthoR() : _navi.NaviTargetRot.Value.OrthoL();

_wannaMove ^= true;
//var toTarget = _navi.NaviTargetPos.Value - curPos;
//_navi.NaviTargetRot = toTarget.Normalized();

//var cameraFacing = _navi.CameraFacing;
//var dot = cameraFacing.Dot(_navi.NaviTargetRot.Value);
//if (dot < -0.707107f)
// _navi.NaviTargetRot = -_navi.NaviTargetRot.Value;
//else if (dot < 0.707107f)
// _navi.NaviTargetRot = cameraFacing.OrthoL().Dot(_navi.NaviTargetRot.Value) > 0 ? _navi.NaviTargetRot.Value.OrthoR() : _navi.NaviTargetRot.Value.OrthoL();
//_navi.WantJump = _jump;
}
ImGui.SameLine();
if (ImGui.Button("Cancel move"))
if (_wannaMove)
{
//_navi.Clear();
var dir = _moveDir.Degrees().ToDirection();
_move.DesiredDirection = new(dir.X, 0, dir.Z);
}
//_navi.Update(player);

Expand Down
4 changes: 2 additions & 2 deletions BossMod/Debug/MainDebugWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

namespace BossMod;

class MainDebugWindow(WorldState ws, RotationModuleManager autorot, ZoneModuleManager zmm, ActionManagerEx amex, AIHintsBuilder hintBuilder, IDalamudPluginInterface dalamud) : UIWindow("Boss mod debug UI", false, new(300, 200))
class MainDebugWindow(WorldState ws, RotationModuleManager autorot, ZoneModuleManager zmm, ActionManagerEx amex, MovementOverride move, AIHintsBuilder hintBuilder, IDalamudPluginInterface dalamud) : UIWindow("Boss mod debug UI", false, new(300, 200))
{
private readonly DebugObstacles _debugObstacles = new(hintBuilder.Obstacles, dalamud);
private readonly DebugObjects _debugObjects = new();
Expand All @@ -18,7 +18,7 @@ namespace BossMod;
private readonly DebugGraphics _debugGraphics = new();
private readonly DebugAction _debugAction = new(ws, amex);
private readonly DebugHate _debugHate = new();
private readonly DebugInput _debugInput = new(autorot);
private readonly DebugInput _debugInput = new(autorot, move);
private readonly DebugAutorotation _debugAutorot = new(autorot);
private readonly DebugAddon _debugAddon = new();
private readonly DebugTiming _debugTiming = new();
Expand Down
37 changes: 25 additions & 12 deletions BossMod/Framework/MovementOverride.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public sealed unsafe class MovementOverride : IDisposable

private readonly ActionTweaksConfig _tweaksConfig = Service.Config.Get<ActionTweaksConfig>();
private bool _movementBlocked;
private bool _controlBlocked;
private bool? _forcedControlState;
private bool _legacyMode;

public bool IsMoving() => ActualMoveLeft != 0 || ActualMoveUp != 0;
Expand Down Expand Up @@ -100,7 +100,7 @@ public void Dispose()

private void RMIWalkDetour(void* self, float* sumLeft, float* sumForward, float* sumTurnLeft, byte* haveBackwardOrStrafe, byte* a6, byte bAdditiveUnk)
{
_controlBlocked = false;
_forcedControlState = null;
_rmiWalkHook.Original(self, sumLeft, sumForward, sumTurnLeft, haveBackwardOrStrafe, a6, bAdditiveUnk);
UserMoveLeft = *sumLeft;
UserMoveUp = *sumForward;
Expand All @@ -123,13 +123,29 @@ private void RMIWalkDetour(void* self, float* sumLeft, float* sumForward, float*

if (_tweaksConfig.MisdirectionThreshold < 180 && PlayerHasMisdirection())
{
var currentDir = Angle.FromDirection(new(*sumLeft, *sumForward)) + ForwardMovementDirection();
var dirDelta = currentDir - ForcedMovementDirection->Radians();
if (dirDelta.Normalized().Abs().Deg > _tweaksConfig.MisdirectionThreshold)
if (!movementAllowed)
{
*sumLeft = *sumForward = 0;
_controlBlocked = true;
// we are already moving, see whether we need to force stop it
// unfortunately, the base implementation would not sample the input if movement is disabled - force it
float realLeft = 0, realForward = 0, realTurn = 0;
byte realStrafe = 0, realUnk = 0;
if (!MovementBlocked)
_rmiWalkHook.Original(self, &realLeft, &realForward, &realTurn, &realStrafe, &realUnk, 1);
var desiredRelDir = realLeft != 0 || realForward != 0 ? Angle.FromDirection(new(realLeft, realForward)) : DirectionToDestination(false)?.h;
_forcedControlState = desiredRelDir != null && (desiredRelDir.Value + ForwardMovementDirection() - ForcedMovementDirection->Radians()).Normalized().Abs().Deg <= _tweaksConfig.MisdirectionThreshold;
}
else if (*sumLeft != 0 || *sumForward != 0)
{
var currentDir = Angle.FromDirection(new(*sumLeft, *sumForward)) + ForwardMovementDirection();
var dirDelta = currentDir - ForcedMovementDirection->Radians();
_forcedControlState = dirDelta.Normalized().Abs().Deg <= _tweaksConfig.MisdirectionThreshold;
if (!_forcedControlState.Value)
{
// forbid movement for now
*sumLeft = *sumForward = 0;
}
}
// else: movement is allowed (so we're not already moving), but we don't want to move anywhere, do nothing
}

ActualMoveLeft = *sumLeft;
Expand All @@ -138,7 +154,7 @@ private void RMIWalkDetour(void* self, float* sumLeft, float* sumForward, float*

private void RMIFlyDetour(void* self, PlayerMoveControllerFlyInput* result)
{
_controlBlocked = false;
_forcedControlState = null;
_rmiFlyHook.Original(self, result);
// TODO: we really need to introduce some extra checks that PlayerMoveController::readInput does - sometimes it skips reading input, and returning something non-zero breaks stuff...
if (result->Forward == 0 && result->Left == 0 && result->Up == 0 && DirectionToDestination(true) is var relDir && relDir != null)
Expand All @@ -152,10 +168,7 @@ private void RMIFlyDetour(void* self, PlayerMoveControllerFlyInput* result)

private byte MCIsInputActiveDetour(void* self, byte inputSourceFlags)
{
var res = _mcIsInputActiveHook.Original(self, inputSourceFlags);
if (res != 0 && _controlBlocked)
res = 0;
return res;
return _forcedControlState != null ? (byte)(_forcedControlState.Value ? 1 : 0) : _mcIsInputActiveHook.Original(self, inputSourceFlags);
}

private (Angle h, Angle v)? DirectionToDestination(bool allowVertical)
Expand Down
2 changes: 1 addition & 1 deletion BossMod/Framework/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ public unsafe Plugin(IDalamudPluginInterface dalamud, ICommandManager commandMan
_wndReplay = new(_ws, _bossmod, _rotationDB, replayDir);
_wndRotation = new(_rotation, _amex, () => OpenConfigUI("Autorotation Presets"));
_wndAI = new(_ai);
_wndDebug = new(_ws, _rotation, _zonemod, _amex, _hintsBuilder, dalamud);
_wndDebug = new(_ws, _rotation, _zonemod, _amex, _movementOverride, _hintsBuilder, dalamud);

dalamud.UiBuilder.DisableAutomaticUiHide = true;
dalamud.UiBuilder.Draw += DrawUI;
Expand Down
108 changes: 108 additions & 0 deletions BossMod/Modules/Dawntrail/Dungeon/D09Yuweyawata/D091LindblumZaghnal.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
namespace BossMod.Dawntrail.Dungeon.D09Yuweyawata.D091LindblumZaghnal;

public enum OID : uint
{
Boss = 0x4641, // R9.000, x1
RawElectrope = 0x4642, // R1.000, x0 (spawn during fight)
Helper = 0x233C, // R0.500, x32, Helper type
}

public enum AID : uint
{
AutoAttack = 872, // Boss->player, no cast, single-target
Teleport = 40622, // Boss->location, no cast, single-target
ElectricalOverload = 40635, // Boss->self, 5.0s cast, range 40 circle, raidwide
GoreCaberToss = 41266, // Boss->self, 3.0s cast, single-target, visual (mechanic start)
CaberToss = 40624, // Boss->self, 19.0s cast, single-target, visual (line sequence)
LineVoltageNarrowLong = 40625, // Helper->self, 4.0s cast, range 50 width 5 rect
LineVoltageWideShort = 41121, // Helper->self, 3.3s cast, range 50 width 10 rect
LineVoltageWideLong = 40627, // Helper->self, 3.5s cast, range 50 width 10 rect
LineVoltageNarrowShort = 41122, // Helper->self, 3.0s cast, range 50 width 5 rect
CellShock = 40626, // Helper->self, 2.0s cast, range 26 circle
LightningStorm = 40636, // Boss->self, 4.5s cast, single-target, visual (spread)
LightningStormAOE = 40637, // Helper->player, 5.0s cast, range 5 circle spread
GoreSparkingFissure = 40630, // Boss->self, 3.0s cast, single-target, visual (mechanic start)
SparkingFissureStart = 41267, // Helper->self, 5.2s cast, range 40 circle, raidwide
SparkingFissureRepeat = 40631, // Helper->self, no cast, range 40 circle, raidwide
SparkingFissureResolve = 40632, // Boss->self, 13.0s cast, single-target, visual (mechanic end)
SparkingFissureResolveAOE = 41258, // Helper->self, 13.7s cast, range 40 circle, raidwide
LightningBolt = 40638, // Helper->location, 5.0s cast, range 6 circle puddle
Electrify = 40634, // RawElectrope->self, 16.0s cast, range 40 circle, ???
}

public enum IconID : uint
{
LightningStorm = 315, // player->self
}

class ElectricalOverload(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.ElectricalOverload));
class LineVoltageNarrowLong(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LineVoltageNarrowLong), new AOEShapeRect(50, 2.5f), 9);
class LineVoltageWideShort(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LineVoltageWideShort), new AOEShapeRect(50, 5));
class LineVoltageWideLong(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LineVoltageWideLong), new AOEShapeRect(50, 5));
class LineVoltageNarrowShort(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.LineVoltageNarrowShort), new AOEShapeRect(50, 2.5f), 6);

class CellShock(BossModule module) : Components.GenericAOEs(module, ActionID.MakeSpell(AID.CellShock))
{
private AOEInstance? _aoe;

private static readonly AOEShapeCircle _shape = new(26);

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

public override void OnEventEnvControl(byte index, uint state)
{
// potential origins are at intercardinals at distance 11.5 from center; there are 4 'tether sources', each can target one of 2 neighbouring intercardinals
var dir = index switch
{
13 => -135.Degrees(),
14 => 135.Degrees(),
15 => -45.Degrees(),
16 => 45.Degrees(),
_ => default
};
if (dir != default && state is 0x00020001 or 0x00200010)
{
dir += state == 0x00020001 ? -90.Degrees() : 90.Degrees();
_aoe = new(_shape, Module.Center + 11.5f * dir.ToDirection(), default, WorldState.FutureTime(8.1f));
}
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if (spell.Action == WatchedAction)
{
++NumCasts;
if (_aoe == null || !_aoe.Value.Origin.AlmostEqual(caster.Position, 1))
ReportError($"Unexpected resolve at {caster.Position}, expected {_aoe?.Origin}");
_aoe = null;
}
}
}

class LightningStorm(BossModule module) : Components.SpreadFromCastTargets(module, ActionID.MakeSpell(AID.LightningStormAOE), 5);
class SparkingFissureStart(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.SparkingFissureStart));
class SparkingFissureResolve(BossModule module) : Components.RaidwideCast(module, ActionID.MakeSpell(AID.SparkingFissureResolveAOE));
class RawElectrope(BossModule module) : Components.Adds(module, (uint)OID.RawElectrope, 1);
class LightningBolt(BossModule module) : Components.LocationTargetedAOEs(module, ActionID.MakeSpell(AID.LightningBolt), 6);

class D091LindblumZaghnalStates : StateMachineBuilder
{
public D091LindblumZaghnalStates(BossModule module) : base(module)
{
TrivialPhase()
.ActivateOnEnter<ElectricalOverload>()
.ActivateOnEnter<LineVoltageNarrowLong>()
.ActivateOnEnter<LineVoltageWideShort>()
.ActivateOnEnter<LineVoltageWideLong>()
.ActivateOnEnter<LineVoltageNarrowShort>()
.ActivateOnEnter<CellShock>()
.ActivateOnEnter<LightningStorm>()
.ActivateOnEnter<SparkingFissureStart>()
.ActivateOnEnter<SparkingFissureResolve>()
.ActivateOnEnter<RawElectrope>()
.ActivateOnEnter<LightningBolt>();
}
}

[ModuleInfo(BossModuleInfo.Maturity.Verified, GroupType = BossModuleInfo.GroupType.CFC, GroupID = 1008, NameID = 13623)]
public class D091LindblumZaghnal(WorldState ws, Actor primary) : BossModule(ws, primary, new(73, 277), new ArenaBoundsCircle(20));
Loading

0 comments on commit 1b3eb7c

Please sign in to comment.