refactor(input): extract the gameplay frame
This commit is contained in:
parent
0bc9fda9de
commit
c557038353
24 changed files with 2433 additions and 559 deletions
116
src/AcDream.App/Combat/CombatAttackTargetSource.cs
Normal file
116
src/AcDream.App/Combat/CombatAttackTargetSource.cs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
using System.Numerics;
|
||||
using AcDream.App.Input;
|
||||
using AcDream.App.World;
|
||||
using AcDream.Core.Combat;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Core.World;
|
||||
|
||||
namespace AcDream.App.Combat;
|
||||
|
||||
/// <summary>
|
||||
/// Focused automatic-attack target owner. It shares canonical selection and
|
||||
/// live-entity state without retaining the broader interaction controller.
|
||||
/// </summary>
|
||||
internal sealed class CombatAttackTargetSource : ICombatAttackTargetSource
|
||||
{
|
||||
private readonly SelectionState _selection;
|
||||
private readonly LiveEntityRuntime _liveEntities;
|
||||
private readonly ClientObjectTable _objects;
|
||||
private readonly ILocalPlayerIdentitySource _player;
|
||||
|
||||
public CombatAttackTargetSource(
|
||||
SelectionState selection,
|
||||
LiveEntityRuntime liveEntities,
|
||||
ClientObjectTable objects,
|
||||
ILocalPlayerIdentitySource player)
|
||||
{
|
||||
_selection = selection ?? throw new ArgumentNullException(nameof(selection));
|
||||
_liveEntities = liveEntities
|
||||
?? throw new ArgumentNullException(nameof(liveEntities));
|
||||
_objects = objects ?? throw new ArgumentNullException(nameof(objects));
|
||||
_player = player ?? throw new ArgumentNullException(nameof(player));
|
||||
}
|
||||
|
||||
public uint? SelectedObjectId => _selection.SelectedObjectId;
|
||||
|
||||
public uint? GetSelectedOrClosestCombatTarget(bool autoTarget)
|
||||
{
|
||||
if (_selection.SelectedObjectId is { } selected
|
||||
&& IsHostileMonster(selected))
|
||||
{
|
||||
return selected;
|
||||
}
|
||||
|
||||
if (!autoTarget)
|
||||
return null;
|
||||
|
||||
(uint Guid, float DistanceSquared)? closest = FindClosestHostileMonster();
|
||||
if (closest is not { } best)
|
||||
{
|
||||
_selection.Clear(SelectionChangeSource.Keyboard);
|
||||
return null;
|
||||
}
|
||||
|
||||
_selection.Select(best.Guid, SelectionChangeSource.Keyboard);
|
||||
string? name = _objects.Get(best.Guid)?.Name;
|
||||
string label = string.IsNullOrWhiteSpace(name)
|
||||
? $"0x{best.Guid:X8}"
|
||||
: name;
|
||||
Console.WriteLine(
|
||||
$"combat: selected target 0x{best.Guid:X8} {label} dist={MathF.Sqrt(best.DistanceSquared):F1}");
|
||||
return best.Guid;
|
||||
}
|
||||
|
||||
private (uint Guid, float DistanceSquared)? FindClosestHostileMonster()
|
||||
{
|
||||
if (!_liveEntities.TryGetWorldEntity(
|
||||
_player.ServerGuid,
|
||||
out WorldEntity playerEntity))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
(uint Guid, float DistanceSquared)? best = null;
|
||||
foreach ((uint guid, WorldEntity entity) in _liveEntities.WorldEntities)
|
||||
{
|
||||
if (!IsHostileMonster(guid))
|
||||
continue;
|
||||
|
||||
float distanceSquared = Vector3.DistanceSquared(
|
||||
entity.Position,
|
||||
playerEntity.Position);
|
||||
if (best is null || distanceSquared < best.Value.DistanceSquared)
|
||||
best = (guid, distanceSquared);
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
private bool IsHostileMonster(uint serverGuid)
|
||||
{
|
||||
uint playerGuid = _player.ServerGuid;
|
||||
if (serverGuid == playerGuid
|
||||
|| !_liveEntities.TryGetInteractionEligibleRecord(
|
||||
serverGuid,
|
||||
out LiveEntityRecord record)
|
||||
|| record.WorldEntity is not { } entity)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_liveEntities.TryGetAnimationRuntime(entity.Id, out var animation)
|
||||
&& animation.CurrentMotion == MotionCommand.Dead)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ClientObject? candidate = _objects.Get(serverGuid);
|
||||
return candidate is not null
|
||||
&& (candidate.Type & ItemType.Creature) != 0
|
||||
&& CombatTargetPolicy.IsHostileMonster(
|
||||
playerGuid,
|
||||
_objects.Get(playerGuid),
|
||||
candidate);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue