Files
IC.Rebuild/Core/StatusBlockPolicy.cs

33 lines
1.8 KiB
C#

namespace ICRebuild.Core;
// The audited SetStatus ABI uses a byte result/flag and a full 64-bit GameObjectId.
public delegate byte StatusSetter(nint manager, int index, ushort statusId, float remaining,
ushort param, ulong source, byte refreshFlags);
public static class StatusBlockPolicy
{
public const string ClientVersion = "2026.10.06.0000.0000";
public const string BaselineClientVersion = "2026.09.15.0000.0000";
public static bool IsAuditedVersion(string? version) => version is ClientVersion or BaselineClientVersion;
public const int ManagerOffset = 0x23B0;
public const int EntityIdOffset = 0x78;
public const uint DefaultStatusId = 1257;
public static bool IsLocal(GameFrame frame, nint manager, nint currentPlayer, uint currentEntityId) =>
frame.Ready && !frame.IsReplay && frame.LocalAddress != 0 &&
frame.LocalStatusManagerAddress == frame.LocalAddress + ManagerOffset &&
manager == frame.LocalStatusManagerAddress && currentPlayer == frame.LocalAddress &&
frame.LocalEntityId is not (0 or 0xE0000000) && currentEntityId == frame.LocalEntityId;
public static bool IsSelected(int index, ushort statusId, IReadOnlySet<uint> selected) =>
(uint)index < 60 && statusId != 0 && selected.Contains(statusId) &&
StatusCatalog.AuditedBlockCandidates.Contains(statusId);
// Never skip the setter: an empty slot must still replace its previous status and
// report native changes to the batch caller, which then refreshes the status flags.
public static byte Forward(StatusSetter original, bool block, nint manager, int index,
ushort statusId, float remaining, ushort param, ulong source, byte refreshFlags) =>
block ? original(manager, index, 0, 0, 0, 0, refreshFlags)
: original(manager, index, statusId, remaining, param, source, refreshFlags);
}