forked from liza/Questionable
Optimize combat for overworld enemies
This commit is contained in:
parent
97cbeada2a
commit
a111a4f75b
@ -2,6 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
using Dalamud.Game.ClientState.Conditions;
|
using Dalamud.Game.ClientState.Conditions;
|
||||||
using Dalamud.Game.ClientState.Objects;
|
using Dalamud.Game.ClientState.Objects;
|
||||||
using Dalamud.Game.ClientState.Objects.Enums;
|
using Dalamud.Game.ClientState.Objects.Enums;
|
||||||
@ -10,7 +11,8 @@ 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.Client.Game.UI;
|
using FFXIVClientStructs.FFXIV.Client.Game.UI;
|
||||||
using FFXIVClientStructs.FFXIV.Common.Math;
|
using FFXIVClientStructs.FFXIV.Client.System.Framework;
|
||||||
|
using FFXIVClientStructs.FFXIV.Common.Component.BGCollision;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
using Questionable.Controller.CombatModules;
|
using Questionable.Controller.CombatModules;
|
||||||
using Questionable.Controller.Steps;
|
using Questionable.Controller.Steps;
|
||||||
@ -38,6 +40,7 @@ internal sealed class CombatController : IDisposable
|
|||||||
private CurrentFight? _currentFight;
|
private CurrentFight? _currentFight;
|
||||||
private bool _wasInCombat;
|
private bool _wasInCombat;
|
||||||
private ulong? _lastTargetId;
|
private ulong? _lastTargetId;
|
||||||
|
private List<byte>? _previousQuestVariables;
|
||||||
|
|
||||||
public CombatController(
|
public CombatController(
|
||||||
IEnumerable<ICombatModule> combatModules,
|
IEnumerable<ICombatModule> combatModules,
|
||||||
@ -79,7 +82,9 @@ internal sealed class CombatController : IDisposable
|
|||||||
Data = combatData,
|
Data = combatData,
|
||||||
LastDistanceCheck = DateTime.Now,
|
LastDistanceCheck = DateTime.Now,
|
||||||
};
|
};
|
||||||
_wasInCombat = combatData.SpawnType is EEnemySpawnType.QuestInterruption or EEnemySpawnType.FinishCombatIfAny;
|
_wasInCombat =
|
||||||
|
combatData.SpawnType is EEnemySpawnType.QuestInterruption or EEnemySpawnType.FinishCombatIfAny;
|
||||||
|
UpdateLastTargetAndQuestVariables(null);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -115,7 +120,31 @@ internal sealed class CombatController : IDisposable
|
|||||||
{
|
{
|
||||||
// wait until the game cleans up the target
|
// wait until the game cleans up the target
|
||||||
if (lastTarget.IsDead)
|
if (lastTarget.IsDead)
|
||||||
return EStatus.InCombat;
|
{
|
||||||
|
ElementId? elementId = _currentFight.Data.ElementId;
|
||||||
|
QuestProgressInfo? questProgressInfo = elementId != null
|
||||||
|
? _questFunctions.GetQuestProgressInfo(elementId)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (questProgressInfo != null &&
|
||||||
|
questProgressInfo.Sequence == _currentFight.Data.Sequence &&
|
||||||
|
QuestWorkUtils.HasCompletionFlags(_currentFight.Data.CompletionQuestVariablesFlags) &&
|
||||||
|
QuestWorkUtils.MatchesQuestWork(_currentFight.Data.CompletionQuestVariablesFlags,
|
||||||
|
questProgressInfo))
|
||||||
|
{
|
||||||
|
// would be the final enemy of the bunch
|
||||||
|
return EStatus.InCombat;
|
||||||
|
}
|
||||||
|
else if (questProgressInfo != null &&
|
||||||
|
questProgressInfo.Sequence == _currentFight.Data.Sequence &&
|
||||||
|
_previousQuestVariables != null &&
|
||||||
|
!questProgressInfo.Variables.SequenceEqual(_previousQuestVariables))
|
||||||
|
{
|
||||||
|
UpdateLastTargetAndQuestVariables(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return EStatus.InCombat;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
_lastTargetId = null;
|
_lastTargetId = null;
|
||||||
@ -372,9 +401,18 @@ internal sealed class CombatController : 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.ValueNullable?.Role is 3 or 4 ? 20f : 2.9f;
|
float maxDistance = player.ClassJob.ValueNullable?.Role is 3 or 4 ? 20f : 2.9f;
|
||||||
if (actualDistance - hitboxOffset >= maxDistance)
|
bool outOfRange = actualDistance - hitboxOffset >= maxDistance;
|
||||||
|
bool isInLineOfSight = IsInLineOfSight(gameObject);
|
||||||
|
if (outOfRange || !isInLineOfSight)
|
||||||
{
|
{
|
||||||
if (actualDistance - hitboxOffset <= 5)
|
bool useNavmesh = actualDistance - hitboxOffset > 5f;
|
||||||
|
if (!outOfRange && !isInLineOfSight)
|
||||||
|
{
|
||||||
|
maxDistance = Math.Min(maxDistance, actualDistance) / 2;
|
||||||
|
useNavmesh = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!useNavmesh)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Moving to {TargetName} ({DataId}) to attack", gameObject.Name,
|
_logger.LogInformation("Moving to {TargetName} ({DataId}) to attack", gameObject.Name,
|
||||||
gameObject.DataId);
|
gameObject.DataId);
|
||||||
@ -391,6 +429,44 @@ internal sealed class CombatController : IDisposable
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal unsafe bool IsInLineOfSight(IGameObject target)
|
||||||
|
{
|
||||||
|
Vector3 sourcePos = _clientState.LocalPlayer!.Position;
|
||||||
|
sourcePos.Y += 2;
|
||||||
|
|
||||||
|
Vector3 targetPos = target.Position;
|
||||||
|
targetPos.Y += 2;
|
||||||
|
|
||||||
|
Vector3 direction = targetPos - sourcePos;
|
||||||
|
float distance = direction.Length();
|
||||||
|
|
||||||
|
direction = Vector3.Normalize(direction);
|
||||||
|
|
||||||
|
Vector3 originVect = new Vector3(sourcePos.X, sourcePos.Y, sourcePos.Z);
|
||||||
|
Vector3 directionVect = new Vector3(direction.X, direction.Y, direction.Z);
|
||||||
|
|
||||||
|
RaycastHit hit;
|
||||||
|
var flags = stackalloc int[] { 0x4000, 0, 0x4000, 0 };
|
||||||
|
var isLoSBlocked =
|
||||||
|
Framework.Instance()->BGCollisionModule->RaycastMaterialFilter(&hit, &originVect, &directionVect, distance,
|
||||||
|
1, flags);
|
||||||
|
|
||||||
|
return isLoSBlocked == false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateLastTargetAndQuestVariables(IGameObject? target)
|
||||||
|
{
|
||||||
|
_lastTargetId = target?.GameObjectId;
|
||||||
|
_previousQuestVariables = _currentFight!.Data.ElementId != null
|
||||||
|
? _questFunctions.GetQuestProgressInfo(_currentFight.Data.ElementId)?.Variables
|
||||||
|
: null;
|
||||||
|
/*
|
||||||
|
_logger.LogTrace("UpdateTargetData: {TargetId}; {QuestVariables}",
|
||||||
|
target?.GameObjectId.ToString("X8", CultureInfo.InvariantCulture) ?? "null",
|
||||||
|
_previousQuestVariables != null ? string.Join(", ", _previousQuestVariables) : "null");
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
public void Stop(string label)
|
public void Stop(string label)
|
||||||
{
|
{
|
||||||
using var scope = _logger.BeginScope(label);
|
using var scope = _logger.BeginScope(label);
|
||||||
@ -422,6 +498,8 @@ internal sealed class CombatController : IDisposable
|
|||||||
public sealed class CombatData
|
public sealed class CombatData
|
||||||
{
|
{
|
||||||
public required ElementId? ElementId { get; init; }
|
public required ElementId? ElementId { get; init; }
|
||||||
|
public required int Sequence { get; init; }
|
||||||
|
public required IList<QuestWorkValue?> CompletionQuestVariablesFlags { get; init; }
|
||||||
public required EEnemySpawnType SpawnType { get; init; }
|
public required EEnemySpawnType SpawnType { get; init; }
|
||||||
public required List<uint> KillEnemyDataIds { get; init; }
|
public required List<uint> KillEnemyDataIds { get; init; }
|
||||||
public required List<ComplexCombatData> ComplexCombatDatas { get; init; }
|
public required List<ComplexCombatData> ComplexCombatDatas { get; init; }
|
||||||
|
@ -173,7 +173,7 @@ internal abstract class MiniTaskController<T>
|
|||||||
if (_condition[ConditionFlag.Mounted])
|
if (_condition[ConditionFlag.Mounted])
|
||||||
tasks.Add(new Mount.UnmountTask());
|
tasks.Add(new Mount.UnmountTask());
|
||||||
|
|
||||||
tasks.Add(Combat.Factory.CreateTask(null, false, EEnemySpawnType.QuestInterruption, [], [], [], null));
|
tasks.Add(Combat.Factory.CreateTask(null, -1, false, EEnemySpawnType.QuestInterruption, [], [], [], null));
|
||||||
tasks.Add(new WaitAtEnd.WaitDelay());
|
tasks.Add(new WaitAtEnd.WaitDelay());
|
||||||
_taskQueue.InterruptWith(tasks);
|
_taskQueue.InterruptWith(tasks);
|
||||||
}
|
}
|
||||||
|
@ -102,17 +102,30 @@ internal static class Combat
|
|||||||
ArgumentNullException.ThrowIfNull(step.EnemySpawnType);
|
ArgumentNullException.ThrowIfNull(step.EnemySpawnType);
|
||||||
|
|
||||||
bool isLastStep = sequence.Steps.Last() == step;
|
bool isLastStep = sequence.Steps.Last() == step;
|
||||||
return CreateTask(quest.Id, isLastStep, step.EnemySpawnType.Value, step.KillEnemyDataIds,
|
return CreateTask(quest.Id,
|
||||||
step.CompletionQuestVariablesFlags, step.ComplexCombatData, step.CombatItemUse);
|
sequence.Sequence,
|
||||||
|
isLastStep,
|
||||||
|
step.EnemySpawnType.Value,
|
||||||
|
step.KillEnemyDataIds,
|
||||||
|
step.CompletionQuestVariablesFlags,
|
||||||
|
step.ComplexCombatData,
|
||||||
|
step.CombatItemUse);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static Task CreateTask(ElementId? elementId, bool isLastStep, EEnemySpawnType enemySpawnType,
|
internal static Task CreateTask(ElementId? elementId,
|
||||||
IList<uint> killEnemyDataIds, IList<QuestWorkValue?> completionQuestVariablesFlags,
|
int sequence,
|
||||||
IList<ComplexCombatData> complexCombatData, CombatItemUse? combatItemUse)
|
bool isLastStep,
|
||||||
|
EEnemySpawnType enemySpawnType,
|
||||||
|
IList<uint> killEnemyDataIds,
|
||||||
|
IList<QuestWorkValue?> completionQuestVariablesFlags,
|
||||||
|
IList<ComplexCombatData> complexCombatData,
|
||||||
|
CombatItemUse? combatItemUse)
|
||||||
{
|
{
|
||||||
return new Task(new CombatController.CombatData
|
return new Task(new CombatController.CombatData
|
||||||
{
|
{
|
||||||
ElementId = elementId,
|
ElementId = elementId,
|
||||||
|
Sequence = sequence,
|
||||||
|
CompletionQuestVariablesFlags = completionQuestVariablesFlags,
|
||||||
SpawnType = enemySpawnType,
|
SpawnType = enemySpawnType,
|
||||||
KillEnemyDataIds = killEnemyDataIds.ToList(),
|
KillEnemyDataIds = killEnemyDataIds.ToList(),
|
||||||
ComplexCombatDatas = complexCombatData.ToList(),
|
ComplexCombatDatas = complexCombatData.ToList(),
|
||||||
|
Loading…
Reference in New Issue
Block a user