Give enemies kill priorities (defined inquest → aggro'd), occasionally check if we're stll in range, reduce cast distance
This commit is contained in:
parent
89b3110a28
commit
33f3147e42
@ -9,6 +9,7 @@ using Dalamud.Game.ClientState.Objects.Types;
|
|||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game;
|
using FFXIVClientStructs.FFXIV.Client.Game;
|
||||||
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
using FFXIVClientStructs.FFXIV.Client.Game.Object;
|
||||||
|
using FFXIVClientStructs.FFXIV.Common.Math;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Questionable.Controller.CombatModules;
|
using Questionable.Controller.CombatModules;
|
||||||
using Questionable.Controller.Utils;
|
using Questionable.Controller.Utils;
|
||||||
@ -75,11 +76,15 @@ internal sealed class CombatController : IDisposable
|
|||||||
var target = _targetManager.Target;
|
var target = _targetManager.Target;
|
||||||
if (target != null)
|
if (target != null)
|
||||||
{
|
{
|
||||||
if (IsEnemyToKill(target))
|
if (GetKillPriority(target) is >= 50)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
var nextTarget = FindNextTarget();
|
var nextTarget = FindNextTarget();
|
||||||
if (nextTarget != null)
|
if (nextTarget != null && nextTarget.Equals(target))
|
||||||
|
{
|
||||||
|
_currentFight.Module.Update(target);
|
||||||
|
}
|
||||||
|
else if (nextTarget != null)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Changing next target to {TargetName} ({TargetId:X8})",
|
_logger.LogInformation("Changing next target to {TargetName} ({TargetId:X8})",
|
||||||
nextTarget.Name.ToString(), nextTarget.GameObjectId);
|
nextTarget.Name.ToString(), nextTarget.GameObjectId);
|
||||||
@ -154,10 +159,15 @@ internal sealed class CombatController : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return _objectTable.Where(IsEnemyToKill).MinBy(x => (x.Position - _clientState.LocalPlayer!.Position).Length());
|
return _objectTable.Select(x => (GameObject: x, Priority: GetKillPriority(x)))
|
||||||
|
.Where(x => x.Priority != null)
|
||||||
|
.OrderByDescending(x => x.Priority!.Value)
|
||||||
|
.ThenByDescending(x => Vector3.Distance(x.GameObject.Position, _clientState.LocalPlayer!.Position))
|
||||||
|
.Select(x => x.GameObject)
|
||||||
|
.FirstOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
private unsafe bool IsEnemyToKill(IGameObject gameObject)
|
private unsafe int? GetKillPriority(IGameObject gameObject)
|
||||||
{
|
{
|
||||||
if (gameObject is IBattleNpc battleNpc)
|
if (gameObject is IBattleNpc battleNpc)
|
||||||
{
|
{
|
||||||
@ -167,14 +177,11 @@ internal sealed class CombatController : IDisposable
|
|||||||
_currentFight.Data.ComplexCombatDatas.Count == 0)
|
_currentFight.Data.ComplexCombatDatas.Count == 0)
|
||||||
{
|
{
|
||||||
if (battleNpc.IsDead)
|
if (battleNpc.IsDead)
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!battleNpc.IsTargetable)
|
if (!battleNpc.IsTargetable)
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
if (battleNpc.TargetObjectId == _clientState.LocalPlayer?.GameObjectId)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (_currentFight != null)
|
if (_currentFight != null)
|
||||||
{
|
{
|
||||||
@ -187,33 +194,37 @@ internal sealed class CombatController : IDisposable
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (complexCombatData[i].DataId == battleNpc.DataId)
|
if (complexCombatData[i].DataId == battleNpc.DataId)
|
||||||
return true;
|
return 100;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_currentFight.Data.KillEnemyDataIds.Contains(battleNpc.DataId))
|
if (_currentFight.Data.KillEnemyDataIds.Contains(battleNpc.DataId))
|
||||||
return true;
|
return 90;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// enemies that we have aggro on
|
||||||
if (battleNpc.BattleNpcKind is BattleNpcSubKind.BattleNpcPart or BattleNpcSubKind.Enemy)
|
if (battleNpc.BattleNpcKind is BattleNpcSubKind.BattleNpcPart or BattleNpcSubKind.Enemy)
|
||||||
{
|
{
|
||||||
var gameObjectStruct = (GameObject*)gameObject.Address;
|
var gameObjectStruct = (GameObject*)gameObject.Address;
|
||||||
if (gameObjectStruct->NamePlateIconId is 60093 or 60732) // npc that starts a fate or does turn-ins
|
if (gameObjectStruct->NamePlateIconId is 60093 or 60732) // npc that starts a fate or does turn-ins
|
||||||
return false;
|
return null;
|
||||||
|
|
||||||
var enemyData = _currentFight?.Data.ComplexCombatDatas.FirstOrDefault(x => x.DataId == battleNpc.DataId);
|
var enemyData = _currentFight?.Data.ComplexCombatDatas.FirstOrDefault(x => x.DataId == battleNpc.DataId);
|
||||||
if (enemyData is { IgnoreQuestMarker: true })
|
if (enemyData is { IgnoreQuestMarker: true })
|
||||||
return battleNpc.StatusFlags.HasFlag(StatusFlags.InCombat);
|
return battleNpc.StatusFlags.HasFlag(StatusFlags.InCombat) ? 20 : null;
|
||||||
else
|
else
|
||||||
return gameObjectStruct->NamePlateIconId != 0;
|
return gameObjectStruct->NamePlateIconId != 0 ? 30 : null;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return false;
|
// stuff trying to kill us
|
||||||
|
if (battleNpc.TargetObjectId == _clientState.LocalPlayer?.GameObjectId)
|
||||||
|
return 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
return false;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Stop()
|
public void Stop()
|
||||||
|
@ -10,5 +10,7 @@ internal interface ICombatModule
|
|||||||
|
|
||||||
bool Stop();
|
bool Stop();
|
||||||
|
|
||||||
|
void Update(IGameObject nextTarget);
|
||||||
|
|
||||||
void SetTarget(IGameObject nextTarget);
|
void SetTarget(IGameObject nextTarget);
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,8 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable
|
|||||||
private readonly ICallGateSubscriber<string, object> _test;
|
private readonly ICallGateSubscriber<string, object> _test;
|
||||||
private readonly ICallGateSubscriber<StateCommandType, object> _changeOperationMode;
|
private readonly ICallGateSubscriber<StateCommandType, object> _changeOperationMode;
|
||||||
|
|
||||||
|
private DateTime _lastDistanceCheck = DateTime.MinValue;
|
||||||
|
|
||||||
public RotationSolverRebornModule(ILogger<RotationSolverRebornModule> logger, MovementController movementController,
|
public RotationSolverRebornModule(ILogger<RotationSolverRebornModule> logger, MovementController movementController,
|
||||||
IClientState clientState, IDalamudPluginInterface pluginInterface)
|
IClientState clientState, IDalamudPluginInterface pluginInterface)
|
||||||
{
|
{
|
||||||
@ -51,6 +53,7 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
_changeOperationMode.InvokeAction(StateCommandType.Manual);
|
_changeOperationMode.InvokeAction(StateCommandType.Manual);
|
||||||
|
_lastDistanceCheck = DateTime.Now;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (IpcError e)
|
catch (IpcError e)
|
||||||
@ -82,10 +85,38 @@ internal sealed class RotationSolverRebornModule : ICombatModule, IDisposable
|
|||||||
|
|
||||||
float hitboxOffset = player.HitboxRadius + gameObject.HitboxRadius;
|
float hitboxOffset = player.HitboxRadius + gameObject.HitboxRadius;
|
||||||
float actualDistance = Vector3.Distance(player.Position, gameObject.Position);
|
float actualDistance = Vector3.Distance(player.Position, gameObject.Position);
|
||||||
float maxDistance = player.ClassJob.GameData?.Role is 3 or 4 ? 25f : 3f;
|
float maxDistance = player.ClassJob.GameData?.Role is 3 or 4 ? 20f : 3f;
|
||||||
if (actualDistance - hitboxOffset > maxDistance)
|
if (actualDistance - hitboxOffset >= maxDistance)
|
||||||
_movementController.NavigateTo(EMovementType.Combat, null, [gameObject.Position], false, false,
|
{
|
||||||
maxDistance + hitboxOffset - 0.25f, true);
|
if (actualDistance - hitboxOffset <= 5)
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Moving to {TargetName} ({DataId}) to attack", gameObject.Name,
|
||||||
|
gameObject.DataId);
|
||||||
|
_movementController.NavigateTo(EMovementType.Combat, null, [gameObject.Position], false, false,
|
||||||
|
maxDistance + hitboxOffset - 0.25f, true);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.LogInformation("Moving to {TargetName} ({DataId}) to attack (with navmesh)", gameObject.Name,
|
||||||
|
gameObject.DataId);
|
||||||
|
_movementController.NavigateTo(EMovementType.Combat, null, gameObject.Position, false, false,
|
||||||
|
maxDistance + hitboxOffset - 0.25f, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_lastDistanceCheck = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(IGameObject gameObject)
|
||||||
|
{
|
||||||
|
if (_movementController.IsPathfinding || _movementController.IsPathRunning)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (DateTime.Now > _lastDistanceCheck.AddSeconds(10))
|
||||||
|
{
|
||||||
|
SetTarget(gameObject);
|
||||||
|
_lastDistanceCheck = DateTime.Now;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() => Stop();
|
public void Dispose() => Stop();
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Game.ClientState.Keys;
|
using Dalamud.Game.ClientState.Keys;
|
||||||
using Dalamud.Plugin.Services;
|
using Dalamud.Plugin.Services;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
@ -22,6 +23,7 @@ internal sealed class QuestController
|
|||||||
private readonly QuestRegistry _questRegistry;
|
private readonly QuestRegistry _questRegistry;
|
||||||
private readonly IKeyState _keyState;
|
private readonly IKeyState _keyState;
|
||||||
private readonly IChatGui _chatGui;
|
private readonly IChatGui _chatGui;
|
||||||
|
private readonly ICondition _condition;
|
||||||
private readonly Configuration _configuration;
|
private readonly Configuration _configuration;
|
||||||
private readonly YesAlreadyIpc _yesAlreadyIpc;
|
private readonly YesAlreadyIpc _yesAlreadyIpc;
|
||||||
private readonly IReadOnlyList<ITaskFactory> _taskFactories;
|
private readonly IReadOnlyList<ITaskFactory> _taskFactories;
|
||||||
@ -44,6 +46,7 @@ internal sealed class QuestController
|
|||||||
QuestRegistry questRegistry,
|
QuestRegistry questRegistry,
|
||||||
IKeyState keyState,
|
IKeyState keyState,
|
||||||
IChatGui chatGui,
|
IChatGui chatGui,
|
||||||
|
ICondition condition,
|
||||||
Configuration configuration,
|
Configuration configuration,
|
||||||
YesAlreadyIpc yesAlreadyIpc,
|
YesAlreadyIpc yesAlreadyIpc,
|
||||||
IEnumerable<ITaskFactory> taskFactories)
|
IEnumerable<ITaskFactory> taskFactories)
|
||||||
@ -56,6 +59,7 @@ internal sealed class QuestController
|
|||||||
_questRegistry = questRegistry;
|
_questRegistry = questRegistry;
|
||||||
_keyState = keyState;
|
_keyState = keyState;
|
||||||
_chatGui = chatGui;
|
_chatGui = chatGui;
|
||||||
|
_condition = condition;
|
||||||
_configuration = configuration;
|
_configuration = configuration;
|
||||||
_yesAlreadyIpc = yesAlreadyIpc;
|
_yesAlreadyIpc = yesAlreadyIpc;
|
||||||
_taskFactories = taskFactories.ToList().AsReadOnly();
|
_taskFactories = taskFactories.ToList().AsReadOnly();
|
||||||
@ -104,7 +108,15 @@ internal sealed class QuestController
|
|||||||
{
|
{
|
||||||
UpdateCurrentQuest();
|
UpdateCurrentQuest();
|
||||||
|
|
||||||
if (_keyState[VirtualKey.ESCAPE])
|
if (!_clientState.IsLoggedIn || _condition[ConditionFlag.Unconscious])
|
||||||
|
{
|
||||||
|
if (_currentTask != null || _taskQueue.Count > 0)
|
||||||
|
{
|
||||||
|
Stop("HP = 0");
|
||||||
|
_movementController.Stop();
|
||||||
|
_combatController.Stop();
|
||||||
|
}
|
||||||
|
} else if (_keyState[VirtualKey.ESCAPE])
|
||||||
{
|
{
|
||||||
if (_currentTask != null || _taskQueue.Count > 0)
|
if (_currentTask != null || _taskQueue.Count > 0)
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user