feat(headless): complete deterministic bot command parity
This commit is contained in:
parent
7e8acb74dd
commit
38e83640d9
37 changed files with 2805 additions and 295 deletions
50
src/AcDream.Core/Spells/RetailSpellTargetPolicy.cs
Normal file
50
src/AcDream.Core/Spells/RetailSpellTargetPolicy.cs
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
using AcDream.Core.Items;
|
||||
|
||||
namespace AcDream.Core.Spells;
|
||||
|
||||
public readonly record struct SpellTargetPolicyResult(
|
||||
bool Allowed,
|
||||
string? Message)
|
||||
{
|
||||
public static SpellTargetPolicyResult Accept { get; } =
|
||||
new(true, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Pure projection of
|
||||
/// <c>ClientMagicSystem::ObjectCompatibleWithSpellTargetType</c>
|
||||
/// (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