using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Xml;
namespace Otter {
///
/// Class used for importing OgmoProject files quickly, and loading levels created in Ogmo Editor
/// (http://ogmoeditor.com) Currently OgmoProjects must export in XML Co-ords for Tiles and Entities,
/// and Bitstring for Grids.
///
public class OgmoProject {
#region Private Fields
Dictionary ColliderTags = new Dictionary();
Dictionary levelValueTypes = new Dictionary();
Dictionary assetMappings = new Dictionary();
#endregion
#region Public Fields
///
/// Determines if grid layers will render in the game. Only applies at loading time.
///
public bool DisplayGrids = true;
///
/// The default image path to search for tilemaps in.
///
public string ImagePath;
///
/// Determines if loaded levels will use camera bounds in the Scene.
///
public bool UseCameraBounds = true;
///
/// Determines if tilemaps are located in an Atlas.
///
public bool UseAtlas;
///
/// The default background color of the Ogmo Project.
///
public Color BackgroundColor;
///
/// The default background grid color of the Ogmo Project.
///
public Color GridColor;
///
/// The known layers loaded from the Ogmo Editor oep file.
///
public Dictionary Layers = new Dictionary();
///
/// Mapping the tile layers to file paths.
///
public Dictionary TileMaps = new Dictionary();
///
/// The entities stored to create tilemaps and grids. Cleared every time LoadLevel is called.
///
public Dictionary Entities = new Dictionary();
///
/// The name of the method to use for creating Entities when loading an .oel file into a Scene.
///
public string CreationMethodName = "CreateFromXml";
///
/// The drawing layer to place the first loaded tile map on.
///
public int BaseTileDepth;
///
/// Determines the drawing layers for each subsequently loaded tile map. For example, the first
/// tilemap will be at Layer 0, the second at Layer 100, the third at Layer 200, etc.
///
public int TileDepthIncrement = 100;
#endregion
#region Public Properties
///
/// The level data last loaded with LoadLevel()
///
public string CurrentLevel { get; private set; }
#endregion
#region Constructors
///
/// Create an OgmoProject from a source .oep file.
///
/// The path to the .oep file.
/// The default image path to use for loading tilemaps.
public OgmoProject(string source, string imagePath = "") {
if (!File.Exists(source)) throw new ArgumentException("Ogmo project file could not be found.");
if (imagePath == "") {
UseAtlas = true;
}
ImagePath = imagePath;
var xmlDoc = new XmlDocument();
xmlDoc.Load(source);
BackgroundColor = new Color(xmlDoc["project"]["BackgroundColor"]);
GridColor = new Color(xmlDoc["project"]["GridColor"]);
var xmlLayers = xmlDoc.GetElementsByTagName("LayerDefinition");
foreach (XmlElement x in xmlLayers) {
var layer = new OgmoLayer(x);
Layers.Add(layer.Name, layer);
}
//I dont know if I need to do this
var xmlEntities = xmlDoc.GetElementsByTagName("EntityDefinition");
foreach (XmlElement x in xmlEntities) {
}
var xmlTilesets = xmlDoc.GetElementsByTagName("Tileset");
foreach (XmlElement x in xmlTilesets) {
TileMaps.Add(x["Name"].InnerText, x["FilePath"].InnerText);
}
//var xmlLevelValues = xmlDoc.GetElementsByTagName("ValueDefinitions");
//dirty dirty hack because there should only be one element with that name
//and for SOME REASON I can't just grab an XmlElement, I have to grab a NodeList and enumerate it for my element. What gives, microsoft?
var xmlLevelValues = xmlDoc.GetElementsByTagName("LevelValueDefinitions")[0] as XmlElement;
foreach (XmlElement x in xmlLevelValues.GetElementsByTagName("ValueDefinition")) {
levelValueTypes.Add(x.Attributes["Name"].Value, x.Attributes["xsi:type"].Value);
}
}
#endregion
#region Private Methods
void CreateEntity(XmlElement e, Scene scene) {
Type entityType = Util.GetTypeFromAllAssemblies(e.Name);
object[] arguments = new object[2];
arguments[0] = scene;
arguments[1] = e.Attributes;
if (entityType != null) {
MethodInfo method = entityType.GetMethod(CreationMethodName, BindingFlags.Static | BindingFlags.Public);
if (method != null) {
method.Invoke(null, arguments);
}
else {
// Attempt to create with just constructor
var x = e.AttributeInt("x");
var y = e.AttributeInt("y");
Entity entity = null;
if (entityType.GetConstructor(new Type[] { typeof(int), typeof(int) }) != null) {
entity = (Entity)Activator.CreateInstance(entityType, x, y);
}
else if (entityType.GetConstructor(new Type[] { typeof(int), typeof(int), typeof(OgmoData) }) != null) {
entity = (Entity)Activator.CreateInstance(entityType, x, y, new OgmoData(e.Attributes));
}
if (entity != null) {
scene.Add(entity);
}
}
}
}
#endregion
#region Public Methods
///
/// Assign a replacement asset for a Tilemap when LoadLevel is called.
///
/// The asset path to find (searches at the end of the string!)
/// The full path to replace the matching asset with.
public void RemapAsset(string searchPath, string replacement) {
assetMappings.Add(searchPath, replacement);
}
///
/// Get a value from an Ogmo level.
///
/// The type of value.
/// The name of the value.
/// The level data to use. If left blank will use the CurrentLevel.
/// The value cast to type T.
public T GetValue(string name, string data = "") {
if (data == "") {
data = CurrentLevel;
}
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(data);
var xmlLevel = xmlDoc["level"];
var value = xmlLevel.Attributes[name].Value;
if (typeof(T) == typeof(Color)) {
return (T)Activator.CreateInstance(typeof(T), value.Substring(1, 6));
}
else {
return (T)Convert.ChangeType(value, typeof(T));
}
}
///
/// Get a value from an Ogmo level.
///
/// The type of value.
/// The name of the value.
/// The level data to use. If left blank will use the CurrentLevel.
/// The value cast to type T.
public T GetValue(Enum name, string source = "") {
return GetValue(Util.EnumValueToBasicString(name), source);
}
public T GetValueFromFile(string name, string path) {
return (T)GetValue(name, File.ReadAllText(path));
}
public string GetLayerData(string name, string data = "") {
if (data == "") {
data = CurrentLevel;
}
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(data);
var xmlLevel = xmlDoc["level"];
return xmlLevel[name].InnerText;
}
///
/// Load level data from a string into a Scene.
///
/// The level data to load.
/// The Scene to load into.
public void LoadLevel(string data, Scene scene) {
Entities.Clear();
CurrentLevel = data;
var xmlDoc = new XmlDocument();
xmlDoc.LoadXml(data);
var xmlLevel = xmlDoc["level"];
scene.Width = int.Parse(xmlDoc["level"].Attributes["width"].Value);
scene.Height = int.Parse(xmlDoc["level"].Attributes["height"].Value);
int i = 0;
foreach (var layer in Layers.Values) {
if (layer.Type == "GridLayerDefinition") {
var Entity = new Entity();
var grid = new GridCollider(scene.Width, scene.Height, layer.GridWidth, layer.GridHeight);
grid.LoadString(xmlLevel[layer.Name].InnerText);
if (ColliderTags.ContainsKey(layer.Name)) {
grid.AddTag(ColliderTags[layer.Name]);
}
if (DisplayGrids) {
var tilemap = new Tilemap(scene.Width, scene.Height, layer.GridWidth, layer.GridHeight);
tilemap.LoadString(xmlLevel[layer.Name].InnerText, layer.Color);
Entity.AddGraphic(tilemap);
}
Entity.AddCollider(grid);
scene.Add(Entity);
Entities.Add(layer.Name, Entity);
}
if (layer.Type == "TileLayerDefinition") {
var Entity = new Entity();
var xmlTiles = xmlLevel[layer.Name];
var tileset = xmlTiles.Attributes["tileset"].Value;
var tilepath = ImagePath + TileMaps[tileset];
foreach(var kv in assetMappings) {
var find = kv.Key;
var replace = kv.Value;
if (tilepath.EndsWith(find)) {
tilepath = replace;
break;
}
}
var tilemap = new Tilemap(tilepath, scene.Width, scene.Height, layer.GridWidth, layer.GridHeight);
var exportMode = xmlTiles.Attributes["exportMode"].Value;
switch (exportMode) {
case "CSV":
tilemap.LoadCSV(xmlTiles.InnerText);
break;
case "XMLCoords":
foreach (XmlElement t in xmlTiles) {
tilemap.SetTile(t);
}
break;
}
tilemap.Update();
Entity.AddGraphic(tilemap);
Entity.Layer = BaseTileDepth - i * TileDepthIncrement;
i++;
scene.Add(Entity);
Entities.Add(layer.Name, Entity);
}
if (layer.Type == "EntityLayerDefinition") {
var xmlEntities = xmlLevel[layer.Name];
if (xmlEntities != null) {
foreach (XmlElement e in xmlEntities) {
CreateEntity(e, scene);
}
}
}
}
if (UseCameraBounds) {
scene.CameraBounds = new Rectangle(0, 0, scene.Width, scene.Height);
scene.UseCameraBounds = true;
}
}
///
/// Load data into a Scene from a source .oel file.
///
/// The oel to load.
/// The Scene to load into.
public void LoadLevelFromFile(string path, Scene scene) {
LoadLevel(File.ReadAllText(path), scene);
}
///
/// Register a collision tag on a grid layer loaded from the oel file.
///
/// The tag to use.
/// The layer name that should use the tag.
public void RegisterTag(int tag, string layerName) {
ColliderTags.Add(layerName, tag);
}
///
/// Register a collision tag on a grid layer loaded from the oel file.
///
/// The enum tag to use. (Casts to int!)
/// The layer name that should use the tag.
public void RegisterTag(Enum tag, string layerName) {
RegisterTag(Convert.ToInt32(tag), layerName);
}
///
/// Get the Entity that was created for a specific Ogmo layer.
///
/// The name of the layer to find.
/// The Entity created for that layer.
public Entity GetEntityFromLayerName(string layerName) {
return Entities[layerName];
}
///
/// Get a list of all the known layer names from the .oep file.
///
///
public List GetLayerNames() {
var s = new List();
foreach (var l in Layers) {
s.Add(l.Key);
}
return s;
}
#endregion
}
///
/// Class representing a layer loaded from Ogmo.
///
public class OgmoLayer {
#region Public Fields
///
/// The name of the layer.
///
public string Name;
///
/// The export mode of the layer from Ogmo Editor.
///
public string ExportMode;
///
/// The type of the layer from Ogmo Editor.
///
public string Type;
///
/// The width of each grid cell.
///
public int GridWidth;
///
/// The height of each grid cell.
///
public int GridHeight;
///
/// The horizontal parallax of the layer.
///
public float ScrollX = 1;
///
/// The vertical parallax of the layer.
///
public float ScrollY = 1;
///
/// The color of layer from Ogmo Editor.
///
public Color Color;
#endregion
#region Constructors
///
/// Create a new OgmoLayer.
///
/// The name of the layer.
/// The type of the layer.
public OgmoLayer(string name, string type) {
Name = name;
Type = type;
}
///
/// Create a new OgmoLayer by parsing an XmlElement.
///
/// An XmlElement from an Ogmo Editor project file.
public OgmoLayer(XmlElement xml) {
Name = xml["Name"].InnerText;
Type = xml.Attributes["xsi:type"].Value;
GridWidth = int.Parse(xml["Grid"]["Width"].InnerText);
GridHeight = int.Parse(xml["Grid"]["Height"].InnerText);
ScrollX = int.Parse(xml["ScrollFactor"]["X"].InnerText);
ScrollY = int.Parse(xml["ScrollFactor"]["Y"].InnerText);
if (Type == "GridLayerDefinition") {
Color = new Color(xml["Color"]);
}
}
#endregion
}
///
/// A simple data class that just extends Dictionary.
///
public class OgmoData : Dictionary {
public OgmoData(XmlAttributeCollection attributes) {
foreach (XmlAttribute attr in attributes) {
Add(attr.Name, attr.Value);
}
}
public int GetInt(string key, int onNull) {
return this.ValueAsInt(key, onNull);
}
public bool GetBool(string key, bool onNull) {
return this.ValueAsBool(key, onNull);
}
public float GetFloat(string key, float onNull) {
return this.ValueAsFloat(key, onNull);
}
public Color GetColor(string key, Color onNull) {
return this.ValueAsColor(key, onNull);
}
}
}