using System;
using System.Collections.Generic;
using AcDream.Core.Items;
using AcDream.Core.Spells;
using AcDream.Runtime.Gameplay;
using AcDream.Content;
namespace AcDream.App.Spells;
/// One resolved formula cell in retail's spell examination subview.
public readonly record struct SpellExamineComponent(
uint SpellComponentId,
SpellComponentDescriptor Descriptor,
bool Owned);
///
/// App content/policy projection for Runtime's live retail magic request
/// owner. The server owns turning, motion, fizzle, mana/component consumption,
/// effects, and results.
///
public sealed class MagicRuntime : IDisposable
{
private readonly SpellComponentRequirementService _requirements;
private IDisposable? _castOperationsBinding;
private MagicRuntime(
MagicCatalog catalog,
RuntimeSpellCastState casting,
SpellComponentRequirementService requirements,
IDisposable castOperationsBinding)
{
Catalog = catalog;
Casting = casting;
_requirements = requirements;
_castOperationsBinding = castOperationsBinding;
}
public MagicCatalog Catalog { get; }
public RuntimeSpellCastState Casting { get; }
public IReadOnlyList GetExamineComponents(uint spellId)
{
IReadOnlyList formula = _requirements.GetAppropriateFormula(spellId);
if (formula.Count == 0)
return [];
var result = new List(formula.Count);
foreach (uint spellComponentId in formula)
{
if (spellComponentId == 0u
|| !Catalog.TryGetComponentBySpellComponentId(
spellComponentId,
out SpellComponentDescriptor descriptor))
continue;
result.Add(new SpellExamineComponent(
spellComponentId,
descriptor,
_requirements.IsComponentOwned(spellComponentId)));
}
return result;
}
internal static MagicRuntime Create(
MagicCatalog catalog,
RuntimeSpellCastState casting,
RuntimeSpellCastOperationsSlot operationsSlot,
ClientObjectTable objects,
Func localPlayerId,
Func accountName,
Action stopCompletely,
Action sendUntargeted,
Action sendTargeted,
Action displayMessage,
Action incrementBusy,
Func canSend)
{
ArgumentNullException.ThrowIfNull(catalog);
ArgumentNullException.ThrowIfNull(casting);
ArgumentNullException.ThrowIfNull(operationsSlot);
ArgumentNullException.ThrowIfNull(objects);
ArgumentNullException.ThrowIfNull(displayMessage);
SpellComponentRequirementService requirements = catalog.CreateRequirementService(
objects, localPlayerId, accountName);
var operations = new LiveSpellCastOperations(
requirements,
objects,
localPlayerId,
stopCompletely,
sendUntargeted,
sendTargeted,
displayMessage,
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();
}