feat: port retail magic lifecycle and retained spell UI
Complete the retail cast-intent, target, component, enchantment, and busy-state paths; mount the DAT-authored spell bar, spellbook, component book, effects panels, and shared panel lifecycle; and add scoped input plus conformance coverage. Co-Authored-By: Codex <noreply@openai.com>
This commit is contained in:
parent
7b7ffcd278
commit
07be994d97
84 changed files with 17822 additions and 1051 deletions
45
src/AcDream.App/Spells/RetailSpellTargetPolicy.cs
Normal file
45
src/AcDream.App/Spells/RetailSpellTargetPolicy.cs
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Spells;
|
||||
|
||||
namespace AcDream.App.Spells;
|
||||
|
||||
public readonly record struct SpellTargetPolicyResult(bool Allowed, string? Message)
|
||||
{
|
||||
public static SpellTargetPolicyResult Accept { get; } = new(true, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pure projection of ClientMagicSystem::ObjectCompatibleWithSpellTargetType
|
||||
/// (0x00567230). The caller owns target lookup and message presentation.
|
||||
/// </summary>
|
||||
public static class RetailSpellTargetPolicy
|
||||
{
|
||||
private const uint SpecialTargetMask = 0x00008107u;
|
||||
|
||||
public static SpellTargetPolicyResult Evaluate(
|
||||
uint localPlayerId,
|
||||
ClientObject target,
|
||||
SpellMetadata spell)
|
||||
{
|
||||
uint mask = spell.TargetMask;
|
||||
uint special = mask & SpecialTargetMask;
|
||||
if (target.ObjectId == localPlayerId && special == 0u)
|
||||
return new(false, "You cannot cast this spell upon yourself.");
|
||||
if (target.StackSize > 1)
|
||||
return new(false, "Cannot cast spell on a stack of items.");
|
||||
|
||||
if (((uint)target.Type & mask) == 0u && special == 0u)
|
||||
return new(false, $"This spell cannot be cast on {target.Name}.");
|
||||
|
||||
// Retail joins the ordinary type match and the 0x8107 special-mask
|
||||
// bypass before these two gates. IsPlayer is PWD bit 3; otherwise the
|
||||
// object must carry BF_ATTACKABLE. Pets are never legal spell targets.
|
||||
var flags = (PublicWeenieFlags)target.PublicWeenieBitfield.GetValueOrDefault();
|
||||
bool playerOrAttackable = (flags
|
||||
& (PublicWeenieFlags.Player | PublicWeenieFlags.Attackable)) != 0;
|
||||
if (!playerOrAttackable || target.PetOwnerId != 0u)
|
||||
return new(false, $"This spell cannot be cast on {target.Name}.");
|
||||
|
||||
return SpellTargetPolicyResult.Accept;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue