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
94
src/AcDream.Runtime/Gameplay/RuntimeCombatModeState.cs
Normal file
94
src/AcDream.Runtime/Gameplay/RuntimeCombatModeState.cs
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
using AcDream.Core.Combat;
|
||||
using AcDream.Core.Items;
|
||||
|
||||
namespace AcDream.Runtime.Gameplay;
|
||||
|
||||
public enum RuntimeCombatModeRequestStatus
|
||||
{
|
||||
Inactive,
|
||||
Rejected,
|
||||
Sent,
|
||||
}
|
||||
|
||||
public readonly record struct RuntimeCombatModeRequestResult(
|
||||
RuntimeCombatModeRequestStatus Status,
|
||||
CombatMode Mode,
|
||||
string? Notice = null);
|
||||
|
||||
public interface IRuntimeCombatModeOperations
|
||||
{
|
||||
bool IsInWorld { get; }
|
||||
IReadOnlyList<ClientObject> GetOrderedEquipment();
|
||||
void NotifyExplicitCombatModeRequest();
|
||||
void SendChangeCombatMode(CombatMode mode);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Presentation-independent owner for retail's explicit combat-mode command.
|
||||
/// App supplies ordered equipment, transport, and the auto-wield cancellation
|
||||
/// edge; Runtime owns the exact default selection and local state transition.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Port of <c>ClientCombatSystem::GetDefaultCombatMode @ 0x0056B310</c>
|
||||
/// and <c>ClientCombatSystem::ToggleCombatMode @ 0x0056C8C0</c>.
|
||||
/// </remarks>
|
||||
public sealed class RuntimeCombatModeState
|
||||
{
|
||||
private readonly CombatState _combat;
|
||||
private readonly IRuntimeCombatModeOperations _operations;
|
||||
|
||||
public RuntimeCombatModeState(
|
||||
CombatState combat,
|
||||
IRuntimeCombatModeOperations operations)
|
||||
{
|
||||
_combat = combat ?? throw new ArgumentNullException(nameof(combat));
|
||||
_operations = operations
|
||||
?? throw new ArgumentNullException(nameof(operations));
|
||||
}
|
||||
|
||||
public RuntimeCombatModeRequestResult Toggle()
|
||||
{
|
||||
if (!_operations.IsInWorld)
|
||||
{
|
||||
return new RuntimeCombatModeRequestResult(
|
||||
RuntimeCombatModeRequestStatus.Inactive,
|
||||
_combat.CurrentMode);
|
||||
}
|
||||
|
||||
// Every explicit user request supersedes auto-wield settlement,
|
||||
// including a request GetDefaultCombatMode later rejects.
|
||||
_operations.NotifyExplicitCombatModeRequest();
|
||||
|
||||
CombatMode currentMode = _combat.CurrentMode;
|
||||
CombatMode nextMode;
|
||||
if (currentMode != CombatMode.NonCombat)
|
||||
{
|
||||
nextMode = CombatMode.NonCombat;
|
||||
}
|
||||
else
|
||||
{
|
||||
DefaultCombatModeDecision decision =
|
||||
CombatInputPlanner.GetDefaultCombatModeDecision(
|
||||
_operations.GetOrderedEquipment());
|
||||
if (decision.IncompatibleHeldItem is { } held)
|
||||
{
|
||||
string notice =
|
||||
$"You can't enter combat mode while wielding the {held.GetAppropriateName()}";
|
||||
return new RuntimeCombatModeRequestResult(
|
||||
RuntimeCombatModeRequestStatus.Rejected,
|
||||
currentMode,
|
||||
notice);
|
||||
}
|
||||
|
||||
nextMode = CombatInputPlanner.ToggleMode(
|
||||
currentMode,
|
||||
decision.Mode);
|
||||
}
|
||||
|
||||
_operations.SendChangeCombatMode(nextMode);
|
||||
_combat.SetCombatMode(nextMode);
|
||||
return new RuntimeCombatModeRequestResult(
|
||||
RuntimeCombatModeRequestStatus.Sent,
|
||||
nextMode);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue