Skip to content

Commit

Permalink
Merge pull request #216 from FFXIV-CombatReborn/mergeWIP2
Browse files Browse the repository at this point in the history
S rank sansheya module, DT ex1 improvements, replay path changeable, new AI slash commands
  • Loading branch information
CarnifexOptimus authored Jul 25, 2024
2 parents cc51533 + 28fe4ef commit 9440973
Show file tree
Hide file tree
Showing 16 changed files with 423 additions and 63 deletions.
134 changes: 108 additions & 26 deletions BossMod/AI/AIManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,19 +184,19 @@ private void OnCommand(string cmd, string message)
configModified = ToggleDebugMenu();
break;
case "FORBIDACTIONS":
configModified = ToggleForbidActions();
configModified = ToggleForbidActions(messageData);
break;
case "FORDBIDMOVEMENT":
configModified = ToggleForbidMovement();
configModified = ToggleForbidMovement(messageData);
break;
case "FOLLOWOUTOFCOMBAT":
configModified = ToggleFollowOutOfCombat();
configModified = ToggleFollowOutOfCombat(messageData);
break;
case "FOLLOWCOMBAT":
configModified = ToggleFollowCombat();
configModified = ToggleFollowCombat(messageData);
break;
case "FOLLOWMODULE":
configModified = ToggleFollowModule();
configModified = ToggleFollowModule(messageData);
break;
case "FOLLOWTARGET":
configModified = ToggleFollowTarget(messageData);
Expand Down Expand Up @@ -283,53 +283,135 @@ private bool ToggleDebugMenu()
return true;
}

private bool ToggleForbidActions()
private bool ToggleForbidActions(string[] messageData)
{
_config.ForbidActions = !_config.ForbidActions;
if (messageData.Length == 1)
_config.ForbidActions = !_config.ForbidActions;
else
{
switch (messageData[1].ToUpperInvariant())
{
case "ON":
_config.ForbidActions = true;
break;
case "OFF":
_config.ForbidActions = false;
break;
default:
Service.Log($"[AI] Unknown forbid actions command: {messageData[1]}");
return _config.ForbidActions;
}
}
Service.Log($"[AI] Forbid actions is now {(_config.ForbidActions ? "enabled" : "disabled")}");
return true;
return _config.ForbidActions;
}

private bool ToggleForbidMovement()
private bool ToggleForbidMovement(string[] messageData)
{
_config.ForbidMovement = !_config.ForbidMovement;
if (messageData.Length == 1)
_config.ForbidMovement = !_config.ForbidMovement;
else
{
switch (messageData[1].ToUpperInvariant())
{
case "ON":
_config.ForbidMovement = true;
break;
case "OFF":
_config.ForbidMovement = false;
break;
default:
Service.Log($"[AI] Unknown forbid movement command: {messageData[1]}");
return _config.ForbidMovement;
}
}
Service.Log($"[AI] Forbid movement is now {(_config.ForbidMovement ? "enabled" : "disabled")}");
return true;
return _config.ForbidMovement;
}

private bool ToggleFollowOutOfCombat()
private bool ToggleFollowOutOfCombat(string[] messageData)
{
_config.FollowOutOfCombat = !_config.FollowOutOfCombat;
if (messageData.Length == 1)
_config.FollowOutOfCombat = !_config.FollowOutOfCombat;
else
{
switch (messageData[1].ToUpperInvariant())
{
case "ON":
_config.FollowOutOfCombat = true;
break;
case "OFF":
_config.FollowOutOfCombat = false;
break;
default:
Service.Log($"[AI] Unknown follow out of combat command: {messageData[1]}");
return _config.FollowOutOfCombat;
}
}
Service.Log($"[AI] Follow out of combat is now {(_config.FollowOutOfCombat ? "enabled" : "disabled")}");
return true;
return _config.FollowOutOfCombat;
}

private bool ToggleFollowCombat()
private bool ToggleFollowCombat(string[] messageData)
{
if (_config.FollowDuringCombat)
if (messageData.Length == 1)
{
_config.FollowDuringCombat = false;
_config.FollowDuringActiveBossModule = false;
if (_config.FollowDuringCombat)
{
_config.FollowDuringCombat = false;
_config.FollowDuringActiveBossModule = false;
}
else
_config.FollowDuringCombat = true;
}
else
_config.FollowDuringCombat = true;
{
switch (messageData[1].ToUpperInvariant())
{
case "ON":
_config.FollowDuringCombat = true;
break;
case "OFF":
_config.FollowDuringCombat = false;
_config.FollowDuringActiveBossModule = false;
break;
default:
Service.Log($"[AI] Unknown follow during combat command: {messageData[1]}");
return _config.FollowDuringCombat;
}
}
Service.Log($"[AI] Follow during combat is now {(_config.FollowDuringCombat ? "enabled" : "disabled")}");
Service.Log($"[AI] Follow during active boss module is now {(_config.FollowDuringActiveBossModule ? "enabled" : "disabled")}");
return true;
return _config.FollowDuringCombat;
}

private bool ToggleFollowModule()
private bool ToggleFollowModule(string[] messageData)
{
if (_config.FollowDuringActiveBossModule)
_config.FollowDuringActiveBossModule = false;
if (messageData.Length == 1)
{
_config.FollowDuringActiveBossModule = !_config.FollowDuringActiveBossModule;
if (!_config.FollowDuringCombat)
_config.FollowDuringCombat = true;
}
else
{
_config.FollowDuringActiveBossModule = true;
_config.FollowDuringCombat = true;
switch (messageData[1].ToUpperInvariant())
{
case "ON":
_config.FollowDuringActiveBossModule = true;
_config.FollowDuringCombat = true;
break;
case "OFF":
_config.FollowDuringActiveBossModule = false;
break;
default:
Service.Log($"[AI] Unknown follow during active boss module command: {messageData[1]}");
return _config.FollowDuringActiveBossModule;
}
}
Service.Log($"[AI] Follow during active boss module is now {(_config.FollowDuringActiveBossModule ? "enabled" : "disabled")}");
Service.Log($"[AI] Follow during combat is now {(_config.FollowDuringCombat ? "enabled" : "disabled")}");
return true;
return _config.FollowDuringActiveBossModule;
}

private bool ToggleFollowTarget(string[] messageData)
Expand Down
5 changes: 5 additions & 0 deletions BossMod/Config/ConfigUI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,10 +90,15 @@ public void Draw()
{ "follow name", "Follows the specified party member by name." },
{ "ui", "Toggles the AI menu." },
{ "forbidactions", "Toggles the forbidding of actions. (only for autorotation)" },
{ "forbidactions on/off", "Sets forbid actions to on or off. (only for autorotation)" },
{ "forbidmovement", "Toggles the forbidding of movement." },
{ "forbidmovement on/off", "Sets forbid movement to on or off." },
{ "followcombat", "Toggles following during combat." },
{ "followcombat on/off", "Sets following following during combat to on or off." },
{ "followmodule", "Toggles following during active boss module." },
{ "followmodule on/off", "Sets following following during active boss module to on or off." },
{ "followoutofcombat", "Toggles following during out of combat." },
{ "followoutofcombat on/off", "Sets following target out of combat to on or off." },
{ "followtarget", "Toggles following targets during combat." },
{ "followtarget on/off", "Sets following target during combat to on or off." },
{ "positional X", "Switch to positional when following targets. (any, rear, flank, front)" },
Expand Down
8 changes: 6 additions & 2 deletions BossMod/Framework/ActionManagerEx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,11 @@ public bool IsRecastTimerActive(ActionID action)
public int GetRecastGroup(ActionID action)
=> _inst->GetRecastGroup((int)action.Type, action.ID);

// see ActionEffectHandler.Receive - there are a few hardcoded actions here
private bool ExpectAnimationLockUpdate(ActionEffectHandler.Header* header)
=> header->SourceSequence != 0 && !(header->ActionType == CSActionType.Action && (NIN.AID)header->ActionId is NIN.AID.Ten1 or NIN.AID.Chi1 or NIN.AID.Jin1 or NIN.AID.Ten2 or NIN.AID.Chi2 or NIN.AID.Jin2)
|| header->ForceAnimationLock;

// perform some action transformations to simplify implementation of queueing; UseActionLocation expects some normalization to be already done
private ActionID NormalizeActionForQueue(ActionID action)
{
Expand Down Expand Up @@ -393,11 +398,10 @@ private void ProcessPacketActionEffectDetour(uint casterID, Character* casterObj
_processPacketActionEffectHook.Original(casterID, casterObj, targetPos, header, effects, targets);
var currAnimLock = _inst->AnimationLock;

if (casterID != UIState.Instance()->PlayerState.EntityId || header->SourceSequence == 0 && !header->ForceAnimationLock)
if (casterID != UIState.Instance()->PlayerState.EntityId || !ExpectAnimationLockUpdate(header))
{
// this action is either executed by non-player, or is non-player-initiated
// TODO: reconsider the condition:
// - some actions with SourceSequence != 0 are special-cased in code (NIN's ten/chi/jin) and apparently don't trigger anim-lock, verify
// - do we want to do non-anim-lock related things (eg unblock movement override) when we get action with 'force anim lock' flag?
if (currAnimLock != prevAnimLock)
Service.Log($"[AMEx] Animation lock updated by non-player-initiated action: #{header->SourceSequence} {casterID:X} {info.Action} {prevAnimLock:f3} -> {currAnimLock:f3}");
Expand Down
6 changes: 5 additions & 1 deletion BossMod/Framework/Plugin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using Dalamud.Game.Command;
using Dalamud.Plugin;
using Dalamud.Plugin.Services;
using System.IO;
using System.Reflection;

namespace BossMod;
Expand Down Expand Up @@ -87,7 +88,10 @@ public unsafe Plugin(IDalamudPluginInterface dalamud, ICommandManager commandMan
_configUI = new(Service.Config, _ws, _rotationDB);
_wndBossmod = new(_bossmod);
_wndBossmodHints = new(_bossmod);
_wndReplay = new(_ws, _rotationDB, new(dalamud.ConfigDirectory.FullName + "/replays"));
var config = Service.Config.Get<ReplayManagementConfig>();
var replayFolder = string.IsNullOrEmpty(config.ReplayFolder) ? dalamud.ConfigDirectory.FullName + "/replays" : config.ReplayFolder;
_wndReplay = new ReplayManagementWindow(_ws, _rotationDB, new DirectoryInfo(replayFolder));
config.Modified.ExecuteAndSubscribe(() => _wndReplay.UpdateLogDirectory());
_wndRotation = new(_rotation, _amex, () => OpenConfigUI("Presets"));
_wndDebug = new(_ws, _rotation, _amex);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,27 @@ class TulidisasterEnrage2(BossModule module) : Components.CastCounter(module, Ac
class TulidisasterEnrage3(BossModule module) : Components.CastCounter(module, ActionID.MakeSpell(AID.TulidisasterEnrageAOE3));

// TODO: investigate how exactly are omens drawn for northern cross & susurrant breath
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "veyn", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 833, NameID = 12854, PlanLevel = 100)]
public class Ex1Valigarmanda(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsRect(20, 15));
[ModuleInfo(BossModuleInfo.Maturity.Verified, Contributors = "veyn, Malediktus", GroupType = BossModuleInfo.GroupType.CFC, GroupID = 833, NameID = 12854, PlanLevel = 100)]
public class Ex1Valigarmanda(WorldState ws, Actor primary) : BossModule(ws, primary, new(100, 100), new ArenaBoundsRect(20, 15))
{
protected override void DrawEnemies(int pcSlot, Actor pc)
{
Arena.Actor(PrimaryActor);
Arena.Actors(Enemies(OID.IceBoulderJail));
}

public override void CalculateAIHints(int slot, Actor actor, PartyRolesConfig.Assignment assignment, AIHints hints)
{
base.CalculateAIHints(slot, actor, assignment, hints);
foreach (var e in hints.PotentialTargets)
{
e.Priority = (OID)e.Actor.OID switch
{
OID.IceBoulderJail => 2,
OID.Boss => 1,
_ => 0
};
}
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class MountainFireCone(BossModule module) : Components.GenericAOEs(module)
private readonly MountainFire? _tower = module.FindComponent<MountainFire>();
private AOEInstance? _aoe;

private static readonly AOEShapeCone _shape = new(40, 160.Degrees()); // TODO: verify angle
private static readonly AOEShapeCone _shape = new(40, 165.Degrees());

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ class NorthernCross(BossModule module) : Components.GenericAOEs(module)

private static readonly AOEShapeRect _shape = new(25, 30);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => Utils.ZeroOrOne(AOE);
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
var component = Module.FindComponent<ChillingCataclysm>(); // prevent NotherCross from hiding the safespot
return component == null || !component.ActiveAOEs(slot, actor).Any() ? Utils.ZeroOrOne(AOE) : ([]);
}

public override void OnEventEnvControl(byte index, uint state)
{
Expand All @@ -19,7 +23,7 @@ public override void OnEventEnvControl(byte index, uint state)
_ => default
};
if (offset != default)
AOE = new(_shape, Module.Center, -127.Degrees() + offset, WorldState.FutureTime(9.2f));
AOE = new(_shape, Module.Center, -126.875f.Degrees() + offset, WorldState.FutureTime(9.2f));
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
Expand Down
26 changes: 24 additions & 2 deletions BossMod/Modules/Dawntrail/Extreme/Ex1Valigarmanda/Spikesicle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ class Spikesicle(BossModule module) : Components.GenericAOEs(module)

private static readonly AOEShape[] _shapes = [new AOEShapeDonut(20, 25), new AOEShapeDonut(25, 30), new AOEShapeDonut(30, 35), new AOEShapeDonut(35, 40), new AOEShapeRect(40, 2.5f)]; // TODO: verify inner radius

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _aoes.Skip(NumCasts).Take(1);
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 0)
yield return _aoes[0] with { Color = ArenaColor.Danger };
if (_aoes.Count > 1)
yield return _aoes[1];
}

public override void OnEventEnvControl(byte index, uint state)
{
Expand Down Expand Up @@ -36,6 +42,7 @@ public override void OnEventCast(Actor caster, ActorCastEvent spell)
if ((AID)spell.Action.ID is AID.SpikesicleAOE1 or AID.SpikesicleAOE2 or AID.SpikesicleAOE3 or AID.SpikesicleAOE4 or AID.SpikesicleAOE5)
{
++NumCasts;
_aoes.RemoveAt(0);
}
}
}
Expand All @@ -46,11 +53,26 @@ class SphereShatter(BossModule module) : Components.GenericAOEs(module, ActionID

private static readonly AOEShapeCircle _shape = new(13);

public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor) => _aoes.Skip(NumCasts).Take(1);
public override IEnumerable<AOEInstance> ActiveAOEs(int slot, Actor actor)
{
if (_aoes.Count > 0)
yield return _aoes[0] with { Color = ArenaColor.Danger };
if (_aoes.Count > 1)
yield return _aoes[1];
}

public override void OnActorCreated(Actor actor)
{
if ((OID)actor.OID == OID.IceBoulder)
_aoes.Add(new(_shape, actor.Position, default, WorldState.FutureTime(6.5f)));
}

public override void OnEventCast(Actor caster, ActorCastEvent spell)
{
if ((AID)spell.Action.ID == AID.SphereShatter)
{
++NumCasts;
_aoes.RemoveAt(0);
}
}
}
8 changes: 4 additions & 4 deletions BossMod/Modules/Dawntrail/Extreme/Ex1Valigarmanda/Stance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ class Stance(BossModule module) : Components.GenericAOEs(module)
{
private AOEInstance? _aoe;

private static readonly AOEShapeCone _shapeCone = new(50, 45.Degrees()); // TODO: verify angle & origin
private static readonly AOEShapeCone _shapeCone = new(50, 40.Degrees()); // TODO: verify origin
private static readonly AOEShapeCone _shapeOut = new(24, 90.Degrees());
private static readonly AOEShapeDonut _shapeIn = new(8, 30);

Expand All @@ -14,7 +14,7 @@ class Stance(BossModule module) : Components.GenericAOEs(module)
public override void OnCastStarted(Actor caster, ActorCastInfo spell)
{
// TODO: origin should be spell.LocXZ (once it's fully fixed)
(AOEShape? shape, WPos origin) = (AID)spell.Action.ID switch
(var shape, var origin) = (AID)spell.Action.ID switch
{
AID.SusurrantBreathAOE => (_shapeCone, new(100, 75)),
AID.SlitheringStrikeAOE => (_shapeOut, caster.Position),
Expand Down Expand Up @@ -64,8 +64,8 @@ public override void OnActorCreated(Actor actor)
{
if ((OID)actor.OID == OID.ChillingCataclysmArcaneSphere)
{
_aoes.Add(new(_shape, actor.Position, 0.Degrees(), WorldState.FutureTime(5.6f)));
_aoes.Add(new(_shape, actor.Position, 45.Degrees(), WorldState.FutureTime(5.6f)));
_aoes.Add(new(_shape, actor.Position, -0.003f.Degrees(), WorldState.FutureTime(5.6f)));
_aoes.Add(new(_shape, actor.Position, 44.998f.Degrees(), WorldState.FutureTime(5.6f)));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ public override void DrawArenaBackground(int pcSlot, Actor pc)
if (RequireHint[pcSlot])
{
var highlightLevitate = RequireLevitating[pcSlot];
for (int x = 0; x < 2; ++x)
for (var x = 0; x < 2; ++x)
{
for (int z = 0; z < 3; ++z)
for (var z = 0; z < 3; ++z)
{
var cellLevitating = ((x ^ z) & 1) != 0;
if (cellLevitating != highlightLevitate)
Expand Down
Loading

0 comments on commit 9440973

Please sign in to comment.