acdream/tests/AcDream.App.Tests/Combat/CombatTargetControllerTests.cs
Erik 047a4c83b5 fix(interaction): bind selection to live incarnations
Carry local WorldEntity identity through render hits, lighting pulses, and deferred movement actions so GUID reuse cannot target a replacement. Reset all session-owned selection and ItemHolder state and prevent combat auto-target during teardown.
2026-07-21 07:07:58 +02:00

99 lines
3.3 KiB
C#

using AcDream.App.Combat;
using AcDream.Core.Combat;
using AcDream.Core.Physics;
using AcDream.Core.Selection;
namespace AcDream.App.Tests.Combat;
public sealed class CombatTargetControllerTests
{
[Fact]
public void DeadSelectedTarget_AutoTargetEnabled_SelectsClosestReplacement()
{
var combat = new CombatState();
combat.SetCombatMode(CombatMode.Melee);
var selection = new SelectionState();
selection.Select(0x50000001u, SelectionChangeSource.World);
int calls = 0;
using var controller = new CombatTargetController(
combat,
selection,
autoTarget: () => true,
selectClosestTarget: () =>
{
calls++;
selection.Select(0x50000002u, SelectionChangeSource.System);
return 0x50000002u;
});
controller.OnMotionApplied(0x50000001u, MotionCommand.Dead);
Assert.Equal(1, calls);
Assert.Equal(0x50000002u, selection.SelectedObjectId);
}
[Fact]
public void DeadSelectedTarget_AutoTargetDisabled_LeavesSelectionClear()
{
var combat = new CombatState();
combat.SetCombatMode(CombatMode.Melee);
var selection = new SelectionState();
selection.Select(0x50000001u, SelectionChangeSource.World);
int calls = 0;
using var controller = new CombatTargetController(
combat, selection, () => false, () => { calls++; return null; });
controller.OnMotionApplied(0x50000001u, MotionCommand.Dead);
Assert.Equal(0, calls);
Assert.Null(selection.SelectedObjectId);
}
[Fact]
public void DeadSelectedTarget_NonCombatMode_DoesNotAutoTarget()
{
var combat = new CombatState();
var selection = new SelectionState();
selection.Select(0x50000001u, SelectionChangeSource.World);
int calls = 0;
using var controller = new CombatTargetController(
combat, selection, () => true, () => { calls++; return null; });
controller.OnMotionApplied(0x50000001u, MotionCommand.Dead);
Assert.Equal(0, calls);
Assert.Null(selection.SelectedObjectId);
}
[Fact]
public void DeadUnselectedObject_DoesNotDisturbCurrentTarget()
{
var combat = new CombatState();
combat.SetCombatMode(CombatMode.Missile);
var selection = new SelectionState();
selection.Select(0x50000002u, SelectionChangeSource.World);
using var controller = new CombatTargetController(
combat, selection, () => true, () => throw new InvalidOperationException());
controller.OnMotionApplied(0x50000001u, MotionCommand.Dead);
Assert.Equal(0x50000002u, selection.SelectedObjectId);
}
[Fact]
public void SessionReset_DoesNotAcquireTargetFromDepartingWorld()
{
var combat = new CombatState();
combat.SetCombatMode(CombatMode.Missile);
var selection = new SelectionState();
selection.Select(0x50000002u, SelectionChangeSource.World);
int calls = 0;
using var controller = new CombatTargetController(
combat, selection, () => true, () => { calls++; return null; });
selection.Reset();
Assert.Equal(0, calls);
Assert.Null(selection.SelectedObjectId);
}
}