Fix overworld combat delay logic

This commit is contained in:
Liza 2024-09-21 23:29:27 +02:00
parent 0c36dd324f
commit 411b121f62
Signed by: liza
GPG Key ID: 7199F8D727D55F67

View File

@ -35,6 +35,7 @@ internal sealed class CombatController : IDisposable
private CurrentFight? _currentFight; private CurrentFight? _currentFight;
private bool _wasInCombat; private bool _wasInCombat;
private ulong? _lastTargetId;
public CombatController( public CombatController(
IEnumerable<ICombatModule> combatModules, IEnumerable<ICombatModule> combatModules,
@ -92,6 +93,33 @@ internal sealed class CombatController : IDisposable
_movementController.MovementStartedAt > DateTime.Now.AddSeconds(-1)) _movementController.MovementStartedAt > DateTime.Now.AddSeconds(-1))
return EStatus.Moving; return EStatus.Moving;
// Overworld enemies typically means that if we want to kill 3 enemies, we could have anywhere from 0 to 20
// enemies in the area (0 if someone else killed them before, like can happen with bots in Fools' Falls in
// La Noscea).
//
// For all 'normal' types, e.g. auto-spawning on entering an area, there's a fixed number of enemies that you're
// fighting with, and the enemies in the overworld aren't relevant.
if (_currentFight.Data.SpawnType is EEnemySpawnType.OverworldEnemies)
{
if (_targetManager.Target != null)
_lastTargetId = _targetManager.Target?.GameObjectId;
else
{
if (_lastTargetId != null)
{
IGameObject? lastTarget = _objectTable.FirstOrDefault(x => x.GameObjectId == _lastTargetId);
if (lastTarget != null)
{
// wait until the game cleans up the target
if (lastTarget.IsDead)
return EStatus.InCombat;
}
else
_lastTargetId = null;
}
}
}
var target = _targetManager.Target; var target = _targetManager.Target;
if (target != null) if (target != null)
{ {
@ -192,45 +220,37 @@ internal sealed class CombatController : IDisposable
public unsafe int GetKillPriority(IGameObject gameObject) public unsafe int GetKillPriority(IGameObject gameObject)
{ {
if (_currentFight == null)
return 0;
if (gameObject is IBattleNpc battleNpc) if (gameObject is IBattleNpc battleNpc)
{ {
if (_currentFight != null && !_currentFight.Module.CanAttack(battleNpc)) if (!_currentFight.Module.CanAttack(battleNpc))
return 0; return 0;
// TODO this works as somewhat of a delay between killing enemies if certain items/flags are checked if (battleNpc.IsDead)
// but also delays killing the next enemy a little return 0;
if (_currentFight == null ||
_currentFight.Data.SpawnType == EEnemySpawnType.OverworldEnemies ||
_currentFight.Data.SpawnType == EEnemySpawnType.FateEnemies ||
_currentFight.Data.KillEnemyDataIds.Count > 0)
{
if (battleNpc.IsDead)
return 0;
}
if (!battleNpc.IsTargetable) if (!battleNpc.IsTargetable)
return 0; return 0;
if (_currentFight != null) var complexCombatData = _currentFight.Data.ComplexCombatDatas;
if (complexCombatData.Count >= 0)
{ {
var complexCombatData = _currentFight.Data.ComplexCombatDatas; for (int i = 0; i < complexCombatData.Count; ++i)
if (complexCombatData.Count >= 0)
{ {
for (int i = 0; i < complexCombatData.Count; ++i) if (_currentFight.Data.CompletedComplexDatas.Contains(i))
{ continue;
if (_currentFight.Data.CompletedComplexDatas.Contains(i))
continue;
if (complexCombatData[i].DataId == battleNpc.DataId) if (complexCombatData[i].DataId == battleNpc.DataId)
return 100; return 100;
}
}
else
{
if (_currentFight.Data.KillEnemyDataIds.Contains(battleNpc.DataId))
return 90;
} }
} }
else
{
if (_currentFight.Data.KillEnemyDataIds.Contains(battleNpc.DataId))
return 90;
}
// enemies that we have aggro on // enemies that we have aggro on
if (battleNpc.BattleNpcKind is BattleNpcSubKind.BattleNpcPart or BattleNpcSubKind.Enemy) if (battleNpc.BattleNpcKind is BattleNpcSubKind.BattleNpcPart or BattleNpcSubKind.Enemy)
@ -241,8 +261,7 @@ internal sealed class CombatController : IDisposable
if (gameObjectStruct->NamePlateIconId is 60093 or 60732) if (gameObjectStruct->NamePlateIconId is 60093 or 60732)
return 0; return 0;
var enemyData = var enemyData = _currentFight.Data.ComplexCombatDatas.FirstOrDefault(x => x.DataId == battleNpc.DataId);
_currentFight?.Data.ComplexCombatDatas.FirstOrDefault(x => x.DataId == battleNpc.DataId);
if (enemyData is { IgnoreQuestMarker: true }) if (enemyData is { IgnoreQuestMarker: true })
{ {
if (battleNpc.StatusFlags.HasFlag(StatusFlags.InCombat)) if (battleNpc.StatusFlags.HasFlag(StatusFlags.InCombat))
@ -255,7 +274,8 @@ internal sealed class CombatController : IDisposable
// for enemies that are very far away, their nameplate doesn't render but they're in the object table // for enemies that are very far away, their nameplate doesn't render but they're in the object table
if (_currentFight?.Data.SpawnType == EEnemySpawnType.OverworldEnemies && if (_currentFight?.Data.SpawnType == EEnemySpawnType.OverworldEnemies &&
Vector3.Distance(_clientState.LocalPlayer?.Position ?? Vector3.Zero, battleNpc.Position) > MaxNameplateRange) Vector3.Distance(_clientState.LocalPlayer?.Position ?? Vector3.Zero, battleNpc.Position) >
MaxNameplateRange)
return 25; return 25;
} }
else else
@ -298,12 +318,14 @@ internal sealed class CombatController : IDisposable
} }
else if (Vector3.Distance(_clientState.LocalPlayer!.Position, target.Position) > MaxTargetRange) else if (Vector3.Distance(_clientState.LocalPlayer!.Position, target.Position) > MaxTargetRange)
{ {
_logger.LogInformation("Moving to target, distance: {Distance:N2}", Vector3.Distance(_clientState.LocalPlayer!.Position, target.Position)); _logger.LogInformation("Moving to target, distance: {Distance:N2}",
Vector3.Distance(_clientState.LocalPlayer!.Position, target.Position));
_currentFight!.Module.MoveToTarget(target); _currentFight!.Module.MoveToTarget(target);
} }
else else
{ {
_logger.LogInformation("Setting target to {TargetName} ({TargetId:X8})", target.Name.ToString(), target.GameObjectId); _logger.LogInformation("Setting target to {TargetName} ({TargetId:X8})", target.Name.ToString(),
target.GameObjectId);
_targetManager.Target = target; _targetManager.Target = target;
_currentFight!.Module.MoveToTarget(target); _currentFight!.Module.MoveToTarget(target);
} }