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
|
|
@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||
using System.Linq;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Spells;
|
||||
using AcDream.Runtime.Gameplay;
|
||||
using DatReaderWriter;
|
||||
using AcDream.Content;
|
||||
using DatReaderWriter.DBObjs;
|
||||
|
|
@ -158,25 +159,29 @@ public sealed class MagicCatalog
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// App-layer owner for the live retail magic request path. The server owns
|
||||
/// turning, motion, fizzle, mana/component consumption, effects, and results.
|
||||
/// App content/policy projection for Runtime's live retail magic request
|
||||
/// owner. The server owns turning, motion, fizzle, mana/component consumption,
|
||||
/// effects, and results.
|
||||
/// </summary>
|
||||
public sealed class MagicRuntime
|
||||
public sealed class MagicRuntime : IDisposable
|
||||
{
|
||||
private readonly SpellComponentRequirementService _requirements;
|
||||
private IDisposable? _castOperationsBinding;
|
||||
|
||||
private MagicRuntime(
|
||||
MagicCatalog catalog,
|
||||
SpellCastingController casting,
|
||||
SpellComponentRequirementService requirements)
|
||||
RuntimeSpellCastState casting,
|
||||
SpellComponentRequirementService requirements,
|
||||
IDisposable castOperationsBinding)
|
||||
{
|
||||
Catalog = catalog;
|
||||
Casting = casting;
|
||||
_requirements = requirements;
|
||||
_castOperationsBinding = castOperationsBinding;
|
||||
}
|
||||
|
||||
public MagicCatalog Catalog { get; }
|
||||
public SpellCastingController Casting { get; }
|
||||
public RuntimeSpellCastState Casting { get; }
|
||||
|
||||
public IReadOnlyList<SpellExamineComponent> GetExamineComponents(uint spellId)
|
||||
{
|
||||
|
|
@ -200,11 +205,11 @@ public sealed class MagicRuntime
|
|||
return result;
|
||||
}
|
||||
|
||||
public static MagicRuntime Create(
|
||||
internal static MagicRuntime Create(
|
||||
MagicCatalog catalog,
|
||||
Spellbook spellbook,
|
||||
RuntimeSpellCastState casting,
|
||||
RuntimeSpellCastOperationsSlot operationsSlot,
|
||||
ClientObjectTable objects,
|
||||
Func<uint?> selectedObject,
|
||||
Func<uint> localPlayerId,
|
||||
Func<string> accountName,
|
||||
Action stopCompletely,
|
||||
|
|
@ -215,39 +220,29 @@ public sealed class MagicRuntime
|
|||
Func<bool> canSend)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(catalog);
|
||||
ArgumentNullException.ThrowIfNull(spellbook);
|
||||
ArgumentNullException.ThrowIfNull(casting);
|
||||
ArgumentNullException.ThrowIfNull(operationsSlot);
|
||||
ArgumentNullException.ThrowIfNull(objects);
|
||||
ArgumentNullException.ThrowIfNull(displayMessage);
|
||||
|
||||
SpellComponentRequirementService requirements = catalog.CreateRequirementService(
|
||||
objects, localPlayerId, accountName);
|
||||
bool TargetCompatible(uint target, SpellMetadata spell, bool showMessage)
|
||||
{
|
||||
if (objects.Get(target) is not { } targetObject)
|
||||
return false;
|
||||
|
||||
SpellTargetPolicyResult result = RetailSpellTargetPolicy.Evaluate(
|
||||
localPlayerId(), targetObject, spell);
|
||||
if (showMessage && !result.Allowed && result.Message is { } message)
|
||||
displayMessage(message);
|
||||
return result.Allowed;
|
||||
}
|
||||
|
||||
var casting = new SpellCastingController(
|
||||
spellbook,
|
||||
selectedObject,
|
||||
var operations = new LiveSpellCastOperations(
|
||||
requirements,
|
||||
objects,
|
||||
localPlayerId,
|
||||
stopCompletely,
|
||||
sendUntargeted,
|
||||
sendTargeted,
|
||||
displayMessage,
|
||||
targetCompatible: (target, spell) => TargetCompatible(target, spell, true),
|
||||
hasRequiredComponents: requirements.HasRequiredComponents,
|
||||
incrementBusy: incrementBusy,
|
||||
canSend: canSend,
|
||||
targetCompatibleSilent: (target, spell) => TargetCompatible(target, spell, false));
|
||||
return new MagicRuntime(catalog, casting, requirements);
|
||||
incrementBusy,
|
||||
canSend);
|
||||
IDisposable binding = operationsSlot.BindOwned(operations);
|
||||
return new MagicRuntime(catalog, casting, requirements, binding);
|
||||
}
|
||||
|
||||
public void Reset() => Casting.Reset();
|
||||
|
||||
public void Dispose() =>
|
||||
Interlocked.Exchange(ref _castOperationsBinding, null)?.Dispose();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue