using System; using System.Collections.Generic; using System.Linq; using System.Xml; namespace Otter { /// /// A Component that can manage a set of Bone Components that can move Entities around. Sort of like a system /// similar to Spine or Spriter, but moving actual Entities around instead of just textures. /// public class Skeleton : Component { #region Public Fields /// /// Determines if the Skeleton should render debug displays for each Bone. /// public bool RenderBones; #endregion Public Fields #region Private Fields List bones = new List(); Dictionary bonesByString = new Dictionary(); Dictionary stringByBones = new Dictionary(); #endregion Private Fields #region Public Properties /// /// The base bone (the first bone added to the Skeleton.) /// public Bone Base { get { return bones.First(); } } #endregion Public Properties #region Public Indexers /// /// Get a Bone by enum name out of the Skeleton. /// /// The name of the bone to retrieve. /// The Bone with that name. public Bone this[Enum name] { get { return GetBone(name); } } /// /// Get a Bone by enum name out of the Skeleton. /// /// The name of the bone to retrieve. /// The Bone with that name. public Bone this[string name] { get { return GetBone(name); } } #endregion Public Indexers #region Public Methods /// /// Adds every Bone in the Skeleton's Entity to the Scene that the Skeleton's Entity is in. /// public void AddAllBonesToScene() { if (!Entity.IsInScene) return; foreach (var b in bones) { if (b.Entity == null) { Entity.Scene.Add(b.Entity); continue; } if (!b.Entity.IsInScene) { Entity.Scene.Add(b.Entity); } } } /// /// Add a Bone to the Skeleton. /// /// The Bone to add. /// The added Bone. public Bone AddBone(Bone bone) { bones.Add(bone); bone.Skeleton = this; return bone; } /// /// Add a Bone to the Skeleton. /// /// The name of the Bone to parent this bone to. /// The name of the Bone being added. /// The Bone being added. /// The added Bone. public Bone AddBone(string parentName, string name, Bone bone = null) { bone = bone == null ? new Bone() : bone; var parent = bonesByString[parentName]; parent.AddBone(bone); AddBone(name, bone); return bone; } /// /// Add a Bone to the Skeleton. /// /// The name of the Bone to parent this bone to. /// The name of the Bone being added. /// The Bone being added. /// The added Bone. public Bone AddBone(Enum parentName, Enum name, Bone bone = null) { return AddBone(Util.EnumValueToString(parentName), Util.EnumValueToString(name), bone); } /// /// Add a Bone to the Skeleton. /// /// The parent Bone. /// The Bone being added. /// The added Bone. public Bone AddBone(Bone parent, Bone bone = null) { bone = bone == null ? new Bone() : bone; parent.AddBone(bone); AddBone(bone); return bone; } /// /// Add a Bone to the Skeleton. /// /// The name of the Bone being added. /// The Bone being added. /// The added Bone. public Bone AddBone(string name, Bone bone = null) { bone = bone == null ? new Bone() : bone; bonesByString.Add(name, bone); stringByBones.Add(bone, name); // Inverted dictionary for cross look up or something I dunno lol AddBone(bone); bone.Name = name; return bone; } /// /// Add a Bone to the Skeleton. /// /// The id of the bone being added. /// The Bone being added. /// The added Bone. public Bone AddBone(Enum id, Bone bone = null) { return AddBone(Util.EnumValueToString(id), bone); } /// /// Retrieve a Bone by its string name. /// /// The name of the Bone. /// The Bone with that name. public Bone GetBone(string name) { return bonesByString[name]; } /// /// Retrieve a Bone by its Enum name. /// /// The name of the Bone. /// The Bone with that name. public Bone GetBone(Enum name) { return GetBone(Util.EnumValueToString(name)); } /// /// Retrieve a Bone by its int id. /// /// The id of the Bone. /// The Bone with that id. public Bone GetBone(int id) { return bones[id]; } /// /// Retrieve a Bone by its Entity. /// /// The Entity that the desired Bone belongs to. /// The Bone with that Entity. public Bone GetBone(Entity e) { foreach (var b in bones) { if (b.Entity == e) return b; } return null; } /// /// A list of all the Bones contained in this Skeleton. /// /// A list of Bones. public List GetBones() { return bones.ToList(); } /// /// Load a Skeleton from XML data. /// /// The XML data to parse. public void LoadXml(string xml) { var xmlDoc = new XmlDocument(); xmlDoc.LoadXml(xml); var xmlSkeleton = xmlDoc.SelectSingleNode("//skeleton"); ParseXml(xmlSkeleton.ChildNodes); } /// /// Remove all the Entities controlled by the Bones in this Skeleton from the Scene. /// public void RemoveAllBonesFromScene() { if (!Entity.IsInScene) return; foreach (var b in bones) { if (b.Entity.IsInScene) { Entity.Scene.Remove(b.Entity); } } } /// /// Remove a Bone from the Skeleton. /// /// The name of the Bone to remove. /// The Bone to return if no Bone was removed. /// The removed Bone, or the Bone passed into the method if no Bone was removed. public Bone RemoveBone(string name, Bone bone = null) { bone = bone == null ? new Bone() : bone; if (bonesByString.ContainsKey(name)) { stringByBones.Remove(bonesByString[name]); bone = bonesByString[name]; bonesByString.Remove(name); bones.RemoveIfContains(bone); bone.Skeleton = null; } return bone; } /// /// Remove a Bone from the Skeleton. /// /// /// public Bone RemoveBone(Enum id) { return RemoveBone(Util.EnumValueToString(id)); } /// /// Remove a Bone from the Skeleton. /// /// /// public Bone RemoveBone(Bone bone) { if (stringByBones.ContainsKey(bone)) { bonesByString.Remove(stringByBones[bone]); stringByBones.Remove(bone); } bones.RemoveIfContains(bone); bone.Skeleton = null; return bone; } public override void Render() { base.Render(); if (RenderBones) { foreach (var b in bones) { b.Render(); } } } public override void Update() { base.Update(); if (Base != null) { Base.LocalX = Entity.X; Base.LocalY = Entity.Y; Base.UpdateTransforms(); } } #endregion Public Methods #region Private Methods void ParseXml(XmlNodeList nodes, string parent = "") { foreach (XmlNode node in nodes) { var bone = new Bone( node.AttributeFloat("x", 0), node.AttributeFloat("y", 0), node.AttributeFloat("rotation", 0), node.AttributeFloat("scaleX", 1), node.AttributeFloat("scaleY", 1) ); bone.FlipGraphicY = node.AttributeBool("flipGraphicY", false); bone.FlipGraphicX = node.AttributeBool("flipGraphicX", false); bone.LocalFlipX = node.AttributeBool("flipX", false); bone.LocalFlipY = node.AttributeBool("flipY", false); var boneName = node.AttributeString("name", "base"); var boneEntityType = Util.GetTypeFromAllAssemblies(node.AttributeString("entity")); var boneEntity = (Entity)Activator.CreateInstance(boneEntityType, 0, 0); bone.SetEntity(boneEntity); if (parent == "") { AddBone(boneName, bone); } else { AddBone(parent, boneName, bone); } if (node.ChildNodes.Count > 0) { ParseXml(node.ChildNodes, boneName); } } } #endregion Private Methods } }