using System; using System.Collections.Generic; namespace Oni.Particles { internal class Particle { #region Private data private ParticleFlags1 flags1; private ParticleFlags2 flags2; private SpriteType spriteType; private DisableDetailLevel disableDetailLevel; private Value lifetime; private Value collisionRadius; private float aiDodgeRadius; private float aiAlertRadius; private string flybySoundName; private Appearance appearance; private Attractor attractor; private List variables; private List emitters; private List events; #endregion #region private struct ActionRange private struct ActionRange { internal ActionRange(BinaryReader reader) { First = reader.ReadUInt16(); Last = reader.ReadUInt16(); } public bool IsEmpty => First == Last; public int First { get; set; } public int Last { get; set; } } #endregion public Particle() { lifetime = Value.FloatZero; collisionRadius = Value.FloatZero; appearance = new Appearance(); attractor = new Attractor(); variables = new List(); emitters = new List(); events = new List(); } public Particle(BinaryReader reader) : this() { appearance = new Appearance(); attractor = new Attractor(); reader.Skip(8); flags1 = (ParticleFlags1)reader.ReadInt32(); flags2 = (ParticleFlags2)reader.ReadInt32(); spriteType = (SpriteType)((int)(flags1 & ParticleFlags1.SpriteModeMask) >> 5); disableDetailLevel = (DisableDetailLevel)((int)(flags2 & ParticleFlags2.DisableLevelMask) >> 5); reader.Skip(4); int variableCount = reader.ReadUInt16(); int actionCount = reader.ReadUInt16(); int emitterCount = reader.ReadUInt16(); reader.Skip(2); variables = new List(variableCount); emitters = new List(actionCount); events = new List(emitterCount); ActionRange[] eventActions = new ActionRange[16]; for (int i = 0; i < 16; i++) eventActions[i] = new ActionRange(reader); lifetime = Value.Read(reader); collisionRadius = Value.Read(reader); aiDodgeRadius = reader.ReadSingle(); aiAlertRadius = reader.ReadSingle(); flybySoundName = reader.ReadString(16); appearance = new Appearance(reader); attractor = new Attractor(reader); reader.Skip(12); for (int i = 0; i < variableCount; i++) variables.Add(new Variable(reader)); EventAction[] actions = new EventAction[actionCount]; for (int i = 0; i < actionCount; i++) actions[i] = new EventAction(reader); try { for (int i = 0; i < emitterCount; i++) emitters.Add(new Emitter(reader)); } catch (System.IO.EndOfStreamException) { // ignore corrupted particles that contain an invalid emitter count field } for (int i = 0; i < eventActions.Length; i++) { ActionRange range = eventActions[i]; if (!range.IsEmpty) events.Add(new Event((EventType)i, actions, range.First, range.Last - range.First)); } } public void Write(BinaryWriter writer) { List actions = new List(); ActionRange[] ranges = new ActionRange[16]; for (int i = 0; i < ranges.Length; i++) { Event e = events.Find(x => (int)x.Type == i); ActionRange range = new ActionRange(); range.First = actions.Count; range.Last = actions.Count + (e == null ? 0 : e.Actions.Count); ranges[i] = range; if (e != null) actions.AddRange(e.Actions); } writer.Write((int)flags1 | ((int)spriteType << 5)); writer.Write((int)flags2 | ((int)disableDetailLevel << 5)); writer.Skip(4); writer.WriteUInt16(variables.Count); writer.WriteUInt16(actions.Count); writer.WriteUInt16(emitters.Count); writer.WriteUInt16(256); for (int i = 0; i < ranges.Length; i++) { writer.WriteUInt16(ranges[i].First); writer.WriteUInt16(ranges[i].Last); } lifetime.Write(writer); collisionRadius.Write(writer); writer.Write(aiDodgeRadius); writer.Write(aiAlertRadius); writer.Write(flybySoundName, 16); appearance.Write(writer); attractor.Write(writer); writer.Skip(12); foreach (Variable variable in variables) variable.Write(writer); foreach (EventAction action in actions) action.Write(writer); foreach (Emitter emitter in emitters) emitter.Write(writer); } public ParticleFlags1 Flags1 { get { return (flags1 & ~ParticleFlags1.SpriteModeMask); } set { flags1 = value; } } public ParticleFlags2 Flags2 { get { return (flags2 & ~ParticleFlags2.DisableLevelMask); } set { flags2 = value; } } public SpriteType SpriteType { get { return spriteType; } set { spriteType = value; } } public DisableDetailLevel DisableDetailLevel { get { return disableDetailLevel; } set { disableDetailLevel = value; } } public string FlyBySoundName { get { return flybySoundName; } set { flybySoundName = value; } } public Value Lifetime { get { return lifetime; } set { lifetime = value; } } public Value CollisionRadius { get { return collisionRadius; } set { collisionRadius = value; } } public float AIDodgeRadius { get { return aiDodgeRadius; } set { aiDodgeRadius = value; } } public float AIAlertRadius { get { return aiAlertRadius; } set { aiAlertRadius = value; } } public Appearance Appearance => appearance; public Attractor Attractor => attractor; public List Variables => variables; public List Emitters => emitters; public List Events => events; } }