using AcDream.Runtime.Gameplay;
namespace AcDream.App.Combat;
///
/// Early Runtime construction seam for the App-owned closest-hostile query.
/// The slot owns no target identity or selection state.
///
internal sealed class RuntimeCombatTargetOperationsSlot
: IRuntimeCombatTargetOperations
{
private IRuntimeCombatTargetOperations? _owner;
public IDisposable BindOwned(IRuntimeCombatTargetOperations owner)
{
ArgumentNullException.ThrowIfNull(owner);
if (_owner is not null)
throw new InvalidOperationException(
"Runtime combat-target operations are already bound.");
_owner = owner;
return new Binding(this, owner);
}
private void Unbind(IRuntimeCombatTargetOperations expected)
{
if (ReferenceEquals(_owner, expected))
_owner = null;
}
public bool AutoTarget => _owner?.AutoTarget == true;
public uint? SelectClosestTarget() => _owner?.SelectClosestTarget();
private sealed class Binding : IDisposable
{
private RuntimeCombatTargetOperationsSlot? _slot;
private readonly IRuntimeCombatTargetOperations _expected;
public Binding(
RuntimeCombatTargetOperationsSlot slot,
IRuntimeCombatTargetOperations expected)
{
_slot = slot;
_expected = expected;
}
public void Dispose() =>
Interlocked.Exchange(ref _slot, null)?.Unbind(_expected);
}
}
internal sealed class LiveCombatTargetOperations(
Func autoTarget,
Func selectClosestTarget)
: IRuntimeCombatTargetOperations
{
private readonly Func _autoTarget = autoTarget
?? throw new ArgumentNullException(nameof(autoTarget));
private readonly Func _selectClosestTarget = selectClosestTarget
?? throw new ArgumentNullException(nameof(selectClosestTarget));
public bool AutoTarget => _autoTarget();
public uint? SelectClosestTarget() => _selectClosestTarget();
}