using System.Buffers.Binary; namespace ICRebuild.Core; public enum DashFilterMode { AllLocal, BlockListed, AllowListed } public static class DashPolicy { // Audited native consumer reads word [data], then flags at [data+2] and [data+3]. public static ushort Timeline(ReadOnlySpan data) => data.Length>=4 ? BinaryPrimitives.ReadUInt16LittleEndian(data) : (ushort)0; public static bool Block(bool ready,nint owner,nint local,ushort timeline,DashFilterMode mode,IReadOnlySet list) { if(!ready || owner==0 || owner!=local || timeline==0) return false; return mode switch { DashFilterMode.AllLocal=>true, DashFilterMode.BlockListed=>list.Contains(timeline), DashFilterMode.AllowListed=>list.Count>0 && !list.Contains(timeline), _=>false }; } } public sealed record DashObservation(ushort Timeline,byte Flags2,byte Flags3,byte MoveType,bool Blocked); public sealed record MovementSkill(uint ActionId,string Name,uint Timeline,bool PlayerAction,bool AffectsPosition,bool PvP); public static class MovementSkillCatalog { public static MovementSkill[] Select(IEnumerable skills) => skills .Where(s=>s.PlayerAction && s.AffectsPosition).DistinctBy(s=>s.ActionId).OrderBy(s=>s.ActionId).ToArray(); public static bool Mappable(MovementSkill skill) => skill.Timeline is >0 and <=ushort.MaxValue; }