using System; using System.Collections.Generic; using System.IO; using System.Xml; using System.Xml.Serialization; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Content; namespace GLEED2D { public partial class Level { /// /// The name of the level. /// [XmlAttribute()] public String Name; [XmlAttribute()] public bool Visible; /// /// A Level contains several Layers. Each Layer contains several Items. /// public List Layers; /// /// A Dictionary containing any user-defined Properties. /// public SerializableDictionary CustomProperties; public Level() { Visible = true; Layers = new List(); CustomProperties = new SerializableDictionary(); } public static Level FromFile(string filename, ContentManager cm) { FileStream stream = File.Open(filename, FileMode.Open); XmlSerializer serializer = new XmlSerializer(typeof(Level)); Level level = (Level)serializer.Deserialize(stream); stream.Close(); foreach (Layer layer in level.Layers) { foreach (Item item in layer.Items) { item.CustomProperties.RestoreItemAssociations(level); item.load(cm); } } return level; } public Item getItemByName(string name) { foreach (Layer layer in Layers) { foreach (Item item in layer.Items) { if (item.Name == name) return item; } } return null; } public Layer getLayerByName(string name) { foreach (Layer layer in Layers) { if (layer.Name == name) return layer; } return null; } public void draw(SpriteBatch sb) { foreach (Layer layer in Layers) layer.draw(sb); } } public partial class Layer { /// /// The name of the layer. /// [XmlAttribute()] public String Name; /// /// Should this layer be visible? /// [XmlAttribute()] public bool Visible; /// /// The list of the items in this layer. /// public List Items; /// /// The Scroll Speed relative to the main camera. The X and Y components are /// interpreted as factors, so (1;1) means the same scrolling speed as the main camera. /// Enables parallax scrolling. /// public Vector2 ScrollSpeed; /// /// A Dictionary containing any user-defined Properties. /// public SerializableDictionary CustomProperties; public Layer() { Items = new List(); ScrollSpeed = Vector2.One; CustomProperties = new SerializableDictionary(); } public void draw(SpriteBatch sb) { if (!Visible) return; foreach (Item item in Items) item.draw(sb); } } [XmlInclude(typeof(TextureItem))] [XmlInclude(typeof(RectangleItem))] [XmlInclude(typeof(CircleItem))] [XmlInclude(typeof(PathItem))] public partial class Item { /// /// The name of this item. /// [XmlAttribute()] public String Name; /// /// Should this item be visible? /// [XmlAttribute()] public bool Visible; /// /// The item's position in world space. /// public Vector2 Position; /// /// A Dictionary containing any user-defined Properties. /// public SerializableDictionary CustomProperties; public Item() { CustomProperties = new SerializableDictionary(); } /// /// Called by Level.FromFile(filename) on each Item after the deserialization process. /// Should be overriden and can be used to load anything needed by the Item (e.g. a texture). /// public virtual void load(ContentManager cm) { } public virtual void draw(SpriteBatch sb) { } } public partial class TextureItem : Item { /// /// The item's rotation in radians. /// public float Rotation; /// /// The item's scale vector. /// public Vector2 Scale; /// /// The color to tint the item's texture with (use white for no tint). /// public Color TintColor; /// /// If true, the texture is flipped horizontally when drawn. /// public bool FlipHorizontally; /// /// If true, the texture is flipped vertically when drawn. /// public bool FlipVertically; /// /// The path to the texture's filename (including the extension) relative to ContentRootFolder. /// public String texture_filename; /// /// The texture_filename without extension. For using in Content.Load(). /// public String asset_name; /// /// The XNA texture to be drawn. Can be loaded either from file (using "texture_filename") /// or via the Content Pipeline (using "asset_name") - then you must ensure that the texture /// exists as an asset in your project. /// Loading is done in the Item's load() method. /// Texture2D texture; /// /// The item's origin relative to the upper left corner of the texture. Usually the middle of the texture. /// Used for placing and rotating the texture when drawn. /// public Vector2 Origin; public TextureItem() { } /// /// Called by Level.FromFile(filename) on each Item after the deserialization process. /// Loads all assets needed by the TextureItem, especially the Texture2D. /// You must provide your own implementation. However, you can rely on all public fields being /// filled by the level deserialization process. /// public override void load(ContentManager cm) { //throw new NotImplementedException(); //TODO: provide your own implementation of how a TextureItem loads its assets //for example: //this.texture = Texture2D.FromFile(, texture_filename); //or by using the Content Pipeline: //this.texture = cm.Load(asset_name); } public override void draw(SpriteBatch sb) { if (!Visible) return; SpriteEffects effects = SpriteEffects.None; if (FlipHorizontally) effects |= SpriteEffects.FlipHorizontally; if (FlipVertically) effects |= SpriteEffects.FlipVertically; sb.Draw(texture, Position, null, TintColor, Rotation, Origin, Scale, effects, 0); } } public partial class RectangleItem : Item { public float Width; public float Height; public Color FillColor; public RectangleItem() { } } public partial class CircleItem : Item { public float Radius; public Color FillColor; public CircleItem() { } } public partial class PathItem : Item { public Vector2[] LocalPoints; public Vector2[] WorldPoints; public bool IsPolygon; public int LineWidth; public Color LineColor; public PathItem() { } } /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// // // NEEDED FOR SERIALIZATION. YOU SHOULDN'T CHANGE ANYTHING BELOW! // /////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////// public class CustomProperty { public string name; public object value; public Type type; public string description; public CustomProperty() { } public CustomProperty(string n, object v, Type t, string d) { name = n; value = v; type = t; description = d; } public CustomProperty clone() { CustomProperty result = new CustomProperty(name, value, type, description); return result; } } public class SerializableDictionary : Dictionary, IXmlSerializable { public SerializableDictionary() : base() { } public SerializableDictionary(SerializableDictionary copyfrom) : base(copyfrom) { string[] keyscopy = new string[Keys.Count]; Keys.CopyTo(keyscopy, 0); foreach (string key in keyscopy) { this[key] = this[key].clone(); } } public System.Xml.Schema.XmlSchema GetSchema() { return null; } public void ReadXml(System.Xml.XmlReader reader) { bool wasEmpty = reader.IsEmptyElement; reader.Read(); if (wasEmpty) return; while (reader.NodeType != System.Xml.XmlNodeType.EndElement) { CustomProperty cp = new CustomProperty(); cp.name = reader.GetAttribute("Name"); cp.description = reader.GetAttribute("Description"); string type = reader.GetAttribute("Type"); if (type == "string") cp.type = typeof(string); if (type == "bool") cp.type = typeof(bool); if (type == "Vector2") cp.type = typeof(Vector2); if (type == "Color") cp.type = typeof(Color); if (type == "Item") cp.type = typeof(Item); if (cp.type == typeof(Item)) { cp.value = reader.ReadInnerXml(); this.Add(cp.name, cp); } else { reader.ReadStartElement("Property"); XmlSerializer valueSerializer = new XmlSerializer(cp.type); object obj = valueSerializer.Deserialize(reader); cp.value = Convert.ChangeType(obj, cp.type); this.Add(cp.name, cp); reader.ReadEndElement(); } reader.MoveToContent(); } reader.ReadEndElement(); } public void WriteXml(System.Xml.XmlWriter writer) { foreach (String key in this.Keys) { writer.WriteStartElement("Property"); writer.WriteAttributeString("Name", this[key].name); if (this[key].type == typeof(string)) writer.WriteAttributeString("Type", "string"); if (this[key].type == typeof(bool)) writer.WriteAttributeString("Type", "bool"); if (this[key].type == typeof(Vector2)) writer.WriteAttributeString("Type", "Vector2"); if (this[key].type == typeof(Color)) writer.WriteAttributeString("Type", "Color"); if (this[key].type == typeof(Item)) writer.WriteAttributeString("Type", "Item"); writer.WriteAttributeString("Description", this[key].description); if (this[key].type == typeof(Item)) { Item item = (Item)this[key].value; if (item != null) writer.WriteString(item.Name); else writer.WriteString("$null$"); } else { XmlSerializer valueSerializer = new XmlSerializer(this[key].type); valueSerializer.Serialize(writer, this[key].value); } writer.WriteEndElement(); } } /// /// Must be called after all Items have been deserialized. /// Restores the Item references in CustomProperties of type Item. /// public void RestoreItemAssociations(Level level) { foreach (CustomProperty cp in Values) { if (cp.type == typeof(Item)) cp.value = level.getItemByName((string)cp.value); } } } }