refactor(runtime): own combat and magic intent
Move attack build/repeat state, combat-mode policy, authoritative auto-target transitions, and spell-cast intent beneath RuntimeActionState. Keep App as the input, world-query, DAT-policy, transport, and presentation adapter while preserving retail request and busy ordering. Add direct/graphical parity, reset, failure, and instance-isolation coverage. Co-authored-by: Codex <noreply@openai.com>
This commit is contained in:
parent
81b31857c6
commit
20df9d155d
49 changed files with 1949 additions and 634 deletions
|
|
@ -0,0 +1,117 @@
|
|||
using AcDream.Core.Combat;
|
||||
using AcDream.Core.Physics;
|
||||
using AcDream.Core.Selection;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
|
||||
namespace AcDream.Runtime.Tests.Gameplay;
|
||||
|
||||
public sealed class RuntimeCombatTargetStateTests
|
||||
{
|
||||
[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 RuntimeCombatTargetState(
|
||||
combat,
|
||||
selection,
|
||||
new Operations(true, () =>
|
||||
{
|
||||
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 RuntimeCombatTargetState(
|
||||
combat,
|
||||
selection,
|
||||
new Operations(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 RuntimeCombatTargetState(
|
||||
combat,
|
||||
selection,
|
||||
new Operations(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 RuntimeCombatTargetState(
|
||||
combat,
|
||||
selection,
|
||||
new Operations(
|
||||
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 RuntimeCombatTargetState(
|
||||
combat,
|
||||
selection,
|
||||
new Operations(true, () => { calls++; return null; }));
|
||||
|
||||
selection.Reset();
|
||||
|
||||
Assert.Equal(0, calls);
|
||||
Assert.Null(selection.SelectedObjectId);
|
||||
}
|
||||
|
||||
private sealed class Operations(
|
||||
bool autoTarget,
|
||||
Func<uint?> selectClosestTarget)
|
||||
: IRuntimeCombatTargetOperations
|
||||
{
|
||||
public bool AutoTarget { get; } = autoTarget;
|
||||
public uint? SelectClosestTarget() => selectClosestTarget();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue