axiosengine/axios/Engine/Glee2D/Level.cs
2012-05-07 18:23:03 -05:00

506 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System.IO;
using FarseerPhysics.Dynamics;
using FarseerPhysics.Common;
using FarseerPhysics.SamplesFramework;
using FarseerPhysics.Factories;
namespace Axios.Engine.Glee2D
{
public partial class Level
{
private World _world;
/// <summary>
/// The name of the level.
/// </summary>
[XmlAttribute()]
public String Name;
[XmlAttribute()]
public bool Visible;
/// <summary>
/// A Level contains several Layers. Each Layer contains several Items.
/// </summary>
public List<Layer> Layers;
/// <summary>
/// A Dictionary containing any user-defined Properties.
/// </summary>
public SerializableDictionary CustomProperties;
public Level()
{
Visible = true;
Layers = new List<Layer>();
CustomProperties = new SerializableDictionary();
}
public Level(World world)
{
Visible = true;
Layers = new List<Layer>();
CustomProperties = new SerializableDictionary();
_world = world;
}
public static Level FromFile(string filename, ContentManager cm, World world)
{
FileStream stream = System.IO.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, world);
}
}
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
{
/// <summary>
/// The name of the layer.
/// </summary>
[XmlAttribute()]
public String Name;
/// <summary>
/// Should this layer be visible?
/// </summary>
[XmlAttribute()]
public bool Visible;
/// <summary>
/// The list of the items in this layer.
/// </summary>
public List<Item> Items;
/// <summary>
/// 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.
/// </summary>
public Vector2 ScrollSpeed;
public Layer()
{
Items = new List<Item>();
ScrollSpeed = Vector2.One;
}
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
{
/// <summary>
/// The name of this item.
/// </summary>
[XmlAttribute()]
public String Name;
/// <summary>
/// Should this item be visible?
/// </summary>
[XmlAttribute()]
public bool Visible;
/// <summary>
/// The item's position in world space.
/// </summary>
public Vector2 Position;
/// <summary>
/// A Dictionary containing any user-defined Properties.
/// </summary>
public SerializableDictionary CustomProperties;
public Item()
{
CustomProperties = new SerializableDictionary();
}
/// <summary>
/// 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).
/// </summary>
public virtual void load(ContentManager cm, World world)
{
}
public virtual void draw(SpriteBatch sb)
{
}
}
public partial class TextureItem : Item
{
/// <summary>
/// The item's rotation in radians.
/// </summary>
public float Rotation;
/// <summary>
/// The item's scale factor.
/// </summary>
public float Scale;
/// <summary>
/// The color to tint the item's texture with (use white for no tint).
/// </summary>
public Color TintColor;
/// <summary>
/// If true, the texture is flipped horizontally when drawn.
/// </summary>
public bool FlipHorizontally;
/// <summary>
/// If true, the texture is flipped vertically when drawn.
/// </summary>
public bool FlipVertically;
/// <summary>
/// The path to the texture's filename (including the extension) relative to ContentRootFolder.
/// </summary>
public String texture_filename;
/// <summary>
/// The texture_filename without extension. For using in Content.Load<Texture2D>().
/// </summary>
public String asset_name;
/// <summary>
/// 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.
/// </summary>
Texture2D texture;
/// <summary>
/// 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.
/// </summary>
public Vector2 Origin;
public TextureItem()
{
}
/// <summary>
/// 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.
/// </summary>
public override void load(ContentManager cm, World world)
{
//throw new NotImplementedException();
//TODO: provide your own implementation of how a TextureItem loads its assets
//for example:
//this.texture = Texture2D.FromFile(<GraphicsDevice>, texture_filename);
//or by using the Content Pipeline:
this.texture = cm.Load<Texture2D>(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;
Body _body;
public RectangleItem()
{
}
public override void load(ContentManager cm, World world)
{
base.load(cm, world);
_body = BodyFactory.CreateRectangle(world, Width, Height, 1f);
_body.Position = Position;
_body.UserData = this;
}
}
public partial class CircleItem : Item
{
public float Radius;
public Color FillColor;
Body _body;
public CircleItem()
{
}
public override void load(ContentManager cm, World world)
{
base.load(cm, world);
_body = BodyFactory.CreateCircle(world, Radius, 1f);
_body.Position = Position;
_body.UserData = this;
}
}
public partial class PathItem : Item
{
public Vector2[] LocalPoints;
public Vector2[] WorldPoints;
public bool IsPolygon;
public int LineWidth;
public Color LineColor;
Body _body;
public PathItem()
{
}
public override void load(ContentManager cm, World world)
{
base.load(cm, world);
Vertices v = new Vertices(WorldPoints.Length);
foreach (Vector2 vec in WorldPoints)
v.Add(new Vector2(ConvertUnits.ToSimUnits(vec.X), ConvertUnits.ToSimUnits(vec.Y)));
_body = BodyFactory.CreateLoopShape(world, v);
_body.Position = this.Position;
_body.UserData = this;
}
}
///////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////
//
// 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<String, CustomProperty>, 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);
#if WINDOWS
cp.value = Convert.ChangeType(obj, cp.type);
#elif WINDOWS_PHONE || XBOX360
cp.value = Convert.ChangeType(obj, cp.type, null);
#endif
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();
}
}
/// <summary>
/// Must be called after all Items have been deserialized.
/// Restores the Item references in CustomProperties of type Item.
/// </summary>
public void RestoreItemAssociations(Level level)
{
foreach (CustomProperty cp in Values)
{
if (cp.type == typeof(Item)) cp.value = level.getItemByName((string)cp.value);
}
}
}
}