diff --git a/BossMod/ActionTweaks/OutOfCombatActionsTweak.cs b/BossMod/ActionTweaks/OutOfCombatActionsTweak.cs index cdf724bfb2..41f49ebed3 100644 --- a/BossMod/ActionTweaks/OutOfCombatActionsTweak.cs +++ b/BossMod/ActionTweaks/OutOfCombatActionsTweak.cs @@ -6,8 +6,9 @@ class OutOfCombatActionsConfig : ConfigNode [PropertyDisplay("Enable the feature")] public bool Enabled = false; - [PropertyDisplay("Auto use Peloton when moving out of combat")] - public bool AutoPeloton = true; + // changed name so that it's treated as a new option (that's set to false) since everyone hates this feature and it's extremely hard to find in the settings + [PropertyDisplay("Auto use Peloton when moving out of combat", since: "0.0.0.294")] + public bool AutoPeloton2 = false; } // Tweak to automatically use out-of-combat convenience actions (peloton, pet summoning, etc). @@ -38,7 +39,7 @@ public void FillActions(Actor player, AIHints hints) if (!_config.Enabled || player.InCombat || _ws.Client.CountdownRemaining != null || player.MountId != 0 || player.Statuses.Any(s => s.ID is 418 or 2648)) // note: in overworld content, you leave combat on death... return; - if (_config.AutoPeloton && player.ClassCategory == ClassCategory.PhysRanged && _ws.CurrentTime >= _nextAutoPeloton) + if (_config.AutoPeloton2 && player.ClassCategory == ClassCategory.PhysRanged && _ws.CurrentTime >= _nextAutoPeloton) { var movementThreshold = 5 * _ws.Frame.Duration; if (player.LastFrameMovement.LengthSq() >= movementThreshold * movementThreshold) diff --git a/BossMod/Components/DirectionalParry.cs b/BossMod/Components/DirectionalParry.cs index d87fff8c8a..a5bfdd1809 100644 --- a/BossMod/Components/DirectionalParry.cs +++ b/BossMod/Components/DirectionalParry.cs @@ -35,15 +35,52 @@ public override void AddHints(int slot, Actor actor, TextHints hints) _ => forbiddenSides.HasFlag(attackDir.Dot(facing.OrthoL()) > 0 ? Side.Left : Side.Right) }; if (attackingFromForbidden) - { hints.Add("Attack target from unshielded side!"); + } + } + + public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) + { + foreach (var (id, targetState) in _actorStates) + { + var target = WorldState.Actors.Find(id); + if (target == null) + continue; + + void forbidDirection(Angle offset) => hints.AddForbiddenZone(new AOEShapeCone(100, 45.Degrees()), target.Position, target.Rotation + offset, DateTime.MaxValue, target.InstanceID); + + var forbiddenSides = ActiveSides(targetState); + var attackDir = (actor.Position - target.Position).Normalized(); + var facing = target.Rotation.ToDirection(); + bool attackingFromForbidden = attackDir.Dot(facing) switch + { + > 0.7071067f => forbiddenSides.HasFlag(Side.Front), + < -0.7071067f => forbiddenSides.HasFlag(Side.Back), + _ => forbiddenSides.HasFlag(attackDir.Dot(facing.OrthoL()) > 0 ? Side.Left : Side.Right) + }; + + if (attackingFromForbidden) + { + hints.SetPriority(target, AIHints.Enemy.PriorityForbidden); + + // make AI move to an area where it can attack target safely + if (actor.TargetID == id) + { + if (forbiddenSides.HasFlag(Side.Front)) + forbidDirection(default); + if (forbiddenSides.HasFlag(Side.Left)) + forbidDirection(90.Degrees()); + if (forbiddenSides.HasFlag(Side.Back)) + forbidDirection(180.Degrees()); + if (forbiddenSides.HasFlag(Side.Right)) + forbidDirection(270.Degrees()); + } } } } public override void DrawArenaForeground(int pcSlot, Actor pc) { - base.DrawArenaForeground(pcSlot, pc); foreach (var a in ActiveActors) { if (_actorStates.TryGetValue(a.InstanceID, out var aState)) diff --git a/BossMod/Modules/Shadowbringers/Quest/DeathUntoDawn/P2LunarOdin.cs b/BossMod/Modules/Shadowbringers/Quest/DeathUntoDawn/P2LunarOdin.cs index ebc0e28f31..672d67f7ea 100644 --- a/BossMod/Modules/Shadowbringers/Quest/DeathUntoDawn/P2LunarOdin.cs +++ b/BossMod/Modules/Shadowbringers/Quest/DeathUntoDawn/P2LunarOdin.cs @@ -74,7 +74,7 @@ class Zantetsuken(BossModule module) : Components.GenericAOEs(module) { private readonly List Casters = []; - public override IEnumerable ActiveAOEs(int slot, Actor actor) => Casters.Select(c => new AOEInstance(new AOEShapeRect(70, 19.5f), actor.CastInfo!.LocXZ, actor.CastInfo!.Rotation, Module.CastFinishAt(actor.CastInfo))); + public override IEnumerable ActiveAOEs(int slot, Actor actor) => Casters.Select(c => new AOEInstance(new AOEShapeRect(70, 19.5f), c.CastInfo!.LocXZ, c.CastInfo!.Rotation, Module.CastFinishAt(c.CastInfo))); public override void OnCastStarted(Actor caster, ActorCastInfo spell) { @@ -107,4 +107,7 @@ public LunarOdinStates(BossModule module) : base(module) } [ModuleInfo(BossModuleInfo.Maturity.Contributed, GroupType = BossModuleInfo.GroupType.Quest, GroupID = 69602, NameID = 10034)] -public class LunarOdin(WorldState ws, Actor primary) : BossModule(ws, primary, new(146.5f, 84.5f), new ArenaBoundsCircle(20)); +public class LunarOdin(WorldState ws, Actor primary) : BossModule(ws, primary, new(146.5f, 84.5f), new ArenaBoundsCircle(20)) +{ + protected override bool CheckPull() => true; +} diff --git a/BossMod/Modules/Shadowbringers/Quest/DeathUntoDawn/P3LunarRavana.cs b/BossMod/Modules/Shadowbringers/Quest/DeathUntoDawn/P3LunarRavana.cs index f6bdd8e466..03e2590858 100644 --- a/BossMod/Modules/Shadowbringers/Quest/DeathUntoDawn/P3LunarRavana.cs +++ b/BossMod/Modules/Shadowbringers/Quest/DeathUntoDawn/P3LunarRavana.cs @@ -72,16 +72,7 @@ protected override void Exec(Actor? primaryTarget) } class AutoGraha(BossModule module) : RotationModule(module); -class DirectionalParry(BossModule module) : Components.DirectionalParry(module, 0x3201) -{ - public override void AddAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints) - { - if (Module.PrimaryActor.FindStatus(680) != null) - { - hints.AddForbiddenZone(new AOEShapeCone(100, 45.Degrees()), Module.PrimaryActor.Position, Module.PrimaryActor.Rotation, WorldState.FutureTime(10)); - } - } -} +class DirectionalParry(BossModule module) : Components.DirectionalParry(module, 0x3201); class Explosion(BossModule module) : Components.SelfTargetedAOEs(module, ActionID.MakeSpell(AID.Explosion), new AOEShapeCross(80, 5), maxCasts: 2); class LunarRavanaStates : StateMachineBuilder @@ -104,6 +95,9 @@ protected override void CalculateModuleAIHints(int slot, Actor actor, PartyRoles { base.CalculateModuleAIHints(slot, actor, assignment, hints); foreach (var h in hints.PotentialTargets) - h.Priority = h.Actor.FindStatus(SID.Invincibility) == null ? 1 : 0; + if (h.Actor.FindStatus(SID.Invincibility) != null) + h.Priority = AIHints.Enemy.PriorityInvincible; } + + protected override bool CheckPull() => true; } diff --git a/BossMod/Pathfinding/ObstacleMaps/938.777.1.bmp b/BossMod/Pathfinding/ObstacleMaps/938.777.1.bmp index f2c0237c38..efb06e91e7 100644 Binary files a/BossMod/Pathfinding/ObstacleMaps/938.777.1.bmp and b/BossMod/Pathfinding/ObstacleMaps/938.777.1.bmp differ diff --git a/BossMod/QuestBattle/QuestBattle.cs b/BossMod/QuestBattle/QuestBattle.cs index 692af93384..fb23b9daca 100644 --- a/BossMod/QuestBattle/QuestBattle.cs +++ b/BossMod/QuestBattle/QuestBattle.cs @@ -323,6 +323,9 @@ protected override void Dispose(bool disposing) public override void Update() { + if (!_config.EnableQuestBattles) + return; + if (!_playerLoaded) { var player = World.Party.Player(); @@ -343,6 +346,9 @@ public override void Update() public override void CalculateAIHints(int playerSlot, Actor player, AIHints hints) { + if (!_config.EnableQuestBattles) + return; + var restartPathfind = false; // update combat flag (TODO: the name is not great...) @@ -390,7 +396,7 @@ public override void CalculateAIHints(int playerSlot, Actor player, AIHints hint TryPathfind(player.PosRot.XYZ(), curObjective.Connections[CurrentObjectiveNavigationProgress..]); } - if (_config.EnableQuestBattles && !Paused && !World.Party.Members[playerSlot].InCutscene) + if (!Paused && !World.Party.Members[playerSlot].InCutscene) { MoveNext(player, curObjective, hints); } diff --git a/BossMod/QuestBattle/Shadowbringers/MSQ/DeathUntoDawn.cs b/BossMod/QuestBattle/Shadowbringers/MSQ/DeathUntoDawn.cs index 94c13e9f8d..424a636441 100644 --- a/BossMod/QuestBattle/Shadowbringers/MSQ/DeathUntoDawn.cs +++ b/BossMod/QuestBattle/Shadowbringers/MSQ/DeathUntoDawn.cs @@ -1,5 +1,4 @@ -using BossMod.Autorotation; -using RID = BossMod.Roleplay.AID; +using RID = BossMod.Roleplay.AID; namespace BossMod.QuestBattle.Shadowbringers.MSQ; @@ -86,7 +85,7 @@ public override List DefineObjectives(WorldState ws) => [ hints.PathfindMapCenter = new(0, -180); hints.PathfindMapBounds = new ArenaBoundsCircle(20); if (!player.InCombat) - hints.PrioritizeAll(); + hints.ForcedTarget = hints.PotentialTargets.MinBy(t => player.DistanceToPoint(t.Actor.Position))?.Actor; _ai.Execute(player, hints); }; })