//------------------------------------------------------------------------------ // Function: WriteTileLayers // Author: nholmes // Summary: writes all the tile layer data //------------------------------------------------------------------------------ public bool WriteTileLayers(TileLayer[] tileLayers, int masterTileLayer) { XmlNode listNode; XmlNode childNode; XmlAttribute newAttribute; // if we haven't got a valid root node, return failure if (writeRoot == null) return false; // create a node to hold the list of tile layers listNode = writeLevelDOM.CreateElement("TileLayers"); // write the index of the master tile layer as an attribute of the node newAttribute = writeLevelDOM.CreateAttribute("MasterTileLayer"); newAttribute.Value = masterTileLayer.ToString(); ; listNode.Attributes.Append(newAttribute); // add an attribute to the list node to store the number of tile layers we are going to store newAttribute = writeLevelDOM.CreateAttribute("Count"); newAttribute.Value = tileLayers.Length.ToString(); listNode.Attributes.Append(newAttribute); // create a node for each tile layer to hold it's data for (int tileLayer = 0; tileLayer < tileLayers.Length; tileLayer++) { // create a node for this tile layer childNode = writeLevelDOM.CreateElement("TileLayer"); // write the name of the tile layer as an attribute of the node newAttribute = writeLevelDOM.CreateAttribute("Name"); newAttribute.Value = tileLayers[tileLayer].Name; childNode.Attributes.Append(newAttribute); // write the index of this tile layer as an attribute of the node newAttribute = writeLevelDOM.CreateAttribute("Number"); newAttribute.Value = tileLayer.ToString(); childNode.Attributes.Append(newAttribute); // write the mode of the tile layer as an attribute of the node newAttribute = writeLevelDOM.CreateAttribute("Mode"); newAttribute.Value = tileLayers[tileLayer].LayerMode.ToString(); childNode.Attributes.Append(newAttribute); // write the target layer of this tile layer as an attribte of the node newAttribute = writeLevelDOM.CreateAttribute("Target"); if (tileLayers[tileLayer].Target == null) newAttribute.Value = "None"; else newAttribute.Value = tileLayers[tileLayer].Target.Name; childNode.Attributes.Append(newAttribute); // write the tile set this tile layer uses as an attribute of the node newAttribute = writeLevelDOM.CreateAttribute("TileSet"); newAttribute.Value = tileLayers[tileLayer].TileSet.Name; childNode.Attributes.Append(newAttribute); // write the tile map this tile layer uses as an attribte of the node newAttribute = writeLevelDOM.CreateAttribute("TileMap"); newAttribute.Value = tileLayers[tileLayer].TileMap.Name; childNode.Attributes.Append(newAttribute); // write the tint color this tile layer uses as an attribte of the node newAttribute = writeLevelDOM.CreateAttribute("TintColor"); newAttribute.Value = tileLayers[tileLayer].TintColor.A + "," + tileLayers[tileLayer].TintColor.R + "," + tileLayers[tileLayer].TintColor.G + "," + tileLayers[tileLayer].TintColor.B; childNode.Attributes.Append(newAttribute); // write the position scale this tile layer uses as an attribte of the node newAttribute = writeLevelDOM.CreateAttribute("PositionScale"); newAttribute.Value = tileLayers[tileLayer].PositionScaleX + "," + tileLayers[tileLayer].PositionScaleY; childNode.Attributes.Append(newAttribute); // write the position offset this tile layer uses as an attribte of the node newAttribute = writeLevelDOM.CreateAttribute("PositionOffset"); newAttribute.Value = tileLayers[tileLayer].PositionOffsetX + "," + tileLayers[tileLayer].PositionOffsetY; childNode.Attributes.Append(newAttribute); // append the node to the list of tile layer nodes listNode.AppendChild(childNode); } // add the list of tile layers to the docuemnt writeRoot.AppendChild(listNode); // report success! return true; }
//------------------------------------------------------------------------------ // Function: ReadEditorInfo // Author: nholmes // Summary: reads the editor related info (background colour, view scales etc) //------------------------------------------------------------------------------ public bool ReadEditorInfo(out Color backgroundColor, out float tileSetViewScale, out float tileLayerViewScale, TileLayer[] tileSetLayers) { // set some defaults just in case we have a problem and need to bail out... backgroundColor = Color.Blue; tileSetViewScale = 1.0f; tileLayerViewScale = 1.0f; // if we haven't got a valid root node, return failure if (readRoot == null) return false; // find the editor info node XmlNode tileLayerNode = readRoot.SelectSingleNode("//EditorInfo"); // find the background colour attribute XmlNode attributeNode = tileLayerNode.Attributes.GetNamedItem("BackgroundColor"); // was the background colour attribute found ok? return failure if not! if (attributeNode == null) return false; // read and store the background color string dataRef = attributeNode.InnerText; backgroundColor = new Color(); // get the alpha value int nextComma = dataRef.IndexOf(','); backgroundColor.A = (byte)Convert.ToInt32(dataRef.Remove(nextComma)); dataRef = dataRef.Remove(0, nextComma + 1); // get the red value nextComma = dataRef.IndexOf(','); backgroundColor.R = (byte)Convert.ToInt32(dataRef.Remove(nextComma)); dataRef = dataRef.Remove(0, nextComma + 1); // get the green value nextComma = dataRef.IndexOf(','); backgroundColor.G = (byte)Convert.ToInt32(dataRef.Remove(nextComma)); dataRef = dataRef.Remove(0, nextComma + 1); // get the blue value backgroundColor.B = (byte)Convert.ToInt32(dataRef); // find the tile list view scale attribute attributeNode = tileLayerNode.Attributes.GetNamedItem("TileSetViewScale"); // was the tile list view scale attribute found ok? return failure if not! if (attributeNode == null) return false; // retrieve and store the tile list view scale tileSetViewScale = Convert.ToSingle(attributeNode.Value); // find the tile layer view scale attribute attributeNode = tileLayerNode.Attributes.GetNamedItem("TileLayerViewScale"); // was the tile layer view scale attribute found ok? return failure if not! if (attributeNode == null) return false; // retrieve and store the tile layer view scale tileLayerViewScale = Convert.ToSingle(attributeNode.Value); // return success! return true; }
//------------------------------------------------------------------------------ // Function: ReadTileLayers // Author: nholmes // Summary: finds all the tile layers in the loaded level and re-creates them //------------------------------------------------------------------------------ public bool ReadTileLayers(Game game, out TileLayer[] tileLayers, out int masterTileLayer, TileSet[] tileSets, TileMap[] tileMaps) { // initialy set tileLayers to null, in case there are any errors and we need to bail out early! tileLayers = null; // set master tile layer to -1, in case there are any errors and we need to bail out early! masterTileLayer = -1; // if we haven't got a valid root node, return failure if (readRoot == null) return false; // find the tile layer node XmlNode tileLayerNode = readRoot.SelectSingleNode("//TileLayers"); // find the master tile layer attribute XmlNode masterTileLayerNode = tileLayerNode.Attributes.GetNamedItem("MasterTileLayer"); // was the master tile layer attribute found ok? return failure if not! if (masterTileLayerNode == null) return false; // store the master tile layer index masterTileLayer = Convert.ToInt32(masterTileLayerNode.InnerText); // find the number of tile layers attribute XmlNode numberOfTileLayersNode = tileLayerNode.Attributes.GetNamedItem("Count"); // was the number of tile layers attribute found ok? return failure if not! if (numberOfTileLayersNode == null) return false; // store the number of tile layers int numTileLayers = Convert.ToInt32(numberOfTileLayersNode.InnerText); // create the tile layer array tileLayers = new TileLayer[numTileLayers]; // create an array to hold the tile layer target names so we can process them once all layers are loaded string[] layerTargetNames = new string[numTileLayers]; // loop through all of the tile maps and re-create them for (int tileLayer = 0; tileLayer < numTileLayers; tileLayer++) { // get the tile layer that corresponds to the current index XmlNode tileLayerData = readRoot.SelectSingleNode("//TileLayers/TileLayer[@Number=" + tileLayer + "]"); // check that the tile layer was found and return failure if not if (tileLayerData == null) return false; // read the tile layer name XmlNode tileLayerNameNode = tileLayerData.Attributes.GetNamedItem("Name"); // was the name data found ok? return failure if not! if (tileLayerNameNode == null) return false; // create the tile layer tileLayers[tileLayer] = new TileLayer(game, tileLayerNameNode.InnerText); // null the tile layer's tile set and tile map pointers in case there is an error and we have to bail out tileLayers[tileLayer].TileSet = null; tileLayers[tileLayer].TileMap = null; // find the tile set data XmlNode tileSetData = tileLayerData.Attributes.GetNamedItem("TileSet"); // was the tile set data found ok? return failure if not! if (tileSetData == null) return false; // Find the correct tile set in the supplied list of tile sets for(int tileSet = 0; tileSet < tileSets.Length; tileSet++) { if(tileSets[tileSet].Name == tileSetData.InnerText) { // found the correct tile set - store a reference to the tile set in the layer tileLayers[tileLayer].TileSet = tileSets[tileSet]; break; } } // check that we found a valid tile set - return failure if we didn't! if (tileLayers[tileLayer].TileSet == null) return false; // find the tile map data XmlNode tileMapData = tileLayerData.Attributes.GetNamedItem("TileMap"); // was the tile map data found ok? return failure if not! if (tileMapData == null) return false; // Find the correct tile map in the supplied list of tile maps for (int tileMap = 0; tileMap < tileMaps.Length; tileMap++) { if (tileMaps[tileMap].Name == tileMapData.InnerText) { // found the correct tile map - store a reference to the tile map in the layer tileLayers[tileLayer].TileMap = tileMaps[tileMap]; break; } } // check that we found a valid tile map - return failure if we didn't! if (tileLayers[tileLayer].TileMap == null) return false; // find the layer mode data XmlNode layerModeData = tileLayerData.Attributes.GetNamedItem("Mode"); // was the layer mode data found ok? return failure if not! if (layerModeData == null) return false; // set the layer mode according to the data we extracted switch (layerModeData.InnerText) { case "Static": tileLayers[tileLayer].LayerMode = TileLayerMode.Static; break; case "Forced": tileLayers[tileLayer].LayerMode = TileLayerMode.Forced; break; case "Follow": tileLayers[tileLayer].LayerMode = TileLayerMode.Follow; break; case "Relative": tileLayers[tileLayer].LayerMode = TileLayerMode.Relative; break; case "Scaled": tileLayers[tileLayer].LayerMode = TileLayerMode.Scaled; break; default: // something has gone wrong - return failure! return false; } // find the layer target data XmlNode layerTargetData = tileLayerData.Attributes.GetNamedItem("Target"); // was the layer target data found ok? return failure if not! if (layerTargetData == null) return false; // read and store the layer target name so we can fix these up once all layers are loaded layerTargetNames[tileLayer] = layerTargetData.InnerText; // find the tint color data XmlNode tintColorData = tileLayerData.Attributes.GetNamedItem("TintColor"); // was the tint color data found ok? return failure if not! if (tintColorData == null) return false; // read and store the tint color string dataRef = tintColorData.InnerText; Color tintColor = new Color(); // get the alpha value int nextComma = dataRef.IndexOf(','); tintColor.A = (byte)Convert.ToInt32(dataRef.Remove(nextComma)); dataRef = dataRef.Remove(0, nextComma + 1); // get the red value nextComma = dataRef.IndexOf(','); tintColor.R = (byte)Convert.ToInt32(dataRef.Remove(nextComma)); dataRef = dataRef.Remove(0, nextComma + 1); // get the green value nextComma = dataRef.IndexOf(','); tintColor.G = (byte)Convert.ToInt32(dataRef.Remove(nextComma)); dataRef = dataRef.Remove(0, nextComma + 1); // get the blue value tintColor.B = (byte)Convert.ToInt32(dataRef); // store the tint color tileLayers[tileLayer].TintColor = tintColor; // find the position scale data XmlNode positionScaleData = tileLayerData.Attributes.GetNamedItem("PositionScale"); // was the position scale data found ok? return failure if not! if (positionScaleData == null) return false; // read and store the position scale dataRef = positionScaleData.InnerText; // get and store the X value nextComma = dataRef.IndexOf(','); tileLayers[tileLayer].PositionScaleX = Convert.ToSingle(dataRef.Remove(nextComma)); dataRef = dataRef.Remove(0, nextComma + 1); // get and store the Y value tileLayers[tileLayer].PositionScaleY = Convert.ToSingle(dataRef); // find the position scale data XmlNode positionOffsetData = tileLayerData.Attributes.GetNamedItem("PositionOffset"); // was the position offset data found ok? return failure if not! if (positionOffsetData == null) return false; // read and store the position offset dataRef = positionOffsetData.InnerText; // get and store the X value nextComma = dataRef.IndexOf(','); tileLayers[tileLayer].PositionOffsetX = Convert.ToSingle(dataRef.Remove(nextComma)); dataRef = dataRef.Remove(0, nextComma + 1); // get and store the Y value tileLayers[tileLayer].PositionOffsetY = Convert.ToSingle(dataRef); } // now all the layers are loaded and created we need to fix up the layer targets for (int tileLayer = 0; tileLayer < numTileLayers; tileLayer++) { if (layerTargetNames[tileLayer] == "None") tileLayers[tileLayer].Target = null; else { int searchLayer; // search all of the tile layers for the layer this layer wishes to target for (searchLayer = 0; searchLayer < numTileLayers; searchLayer++) { // check if this layer matches the name we are looking for if (tileLayers[searchLayer].Name == layerTargetNames[tileLayer]) { // found the match - store it and bail out tileLayers[tileLayer].Target = tileLayers[searchLayer]; break; } } // check to see if we failed to find a batch and bail out with failure if so if (searchLayer == numTileLayers) return false; } } // return success return true; }
//------------------------------------------------------------------------------ // Function: Update // Author: nholmes // Summary: helper function to update the positions of all other tile layers // relative to the master tile layer. NOTE: assumes that you only call // this function on the master tile layer! //------------------------------------------------------------------------------ public void UpdateAllLayers(TileLayer[] tileLayers, int masterLayerIndex) { int tileLayer; // clear the update status of all tile layers for (tileLayer = 0; tileLayer < tileLayers.Length; tileLayer++) tileLayers[tileLayer].updated = false; // set the updated status of the master tileset to true tileLayers[masterLayerIndex].updated = true; // continue to process throught all the layers until they have all been updated (allows for crazy out-of-order map layers!) while (true) { // spin through all of the supplied layers and update any that refer to the master layer for (tileLayer = 0; tileLayer < tileLayers.Length; tileLayer++) { // if this layer has been updated, skip to the next one if (tileLayers[tileLayer].updated == true) continue; // not updated yet, see if this layer has a target if (tileLayers[tileLayer].target != null) { // has the target been updated already? bail out if not.. if (tileLayers[tileLayer].target.updated == false) continue; // ok, time to update this layer! switch (tileLayers[tileLayer].mode) { case TileLayerMode.Static: // no nothing :) break; case TileLayerMode.Forced: // use the position scale values to move the layer by a fixed amount each frame tileLayers[tileLayer].position = tileLayers[tileLayer].PositionScale; break; case TileLayerMode.Follow: // exactly match the target position tileLayers[tileLayer].position = tileLayers[tileLayer].target.Position; break; case TileLayerMode.Relative: // move relative to the target's position taking into account the size of both layers tileLayers[tileLayer].position.X = tileLayers[tileLayer].target.Position.X * ((float)tileLayers[tileLayer].tileMap.Width / (float)tileLayers[tileLayer].target.tileMap.Width); tileLayers[tileLayer].position.Y = tileLayers[tileLayer].target.Position.Y * ((float)tileLayers[tileLayer].tileMap.Height / (float)tileLayers[tileLayer].target.tileMap.Height); break; case TileLayerMode.Scaled: // move as a scaled amount of the target's position tileLayers[tileLayer].position = tileLayers[tileLayer].target.Position * tileLayers[tileLayer].positionScale; break; } // clamp the layer to it's allowed bounds tileLayers[tileLayer].ClampLayerPosition(); // flag this layer as updated tileLayers[tileLayer].updated = true; } else { // check to see if this layer is a forced scrolling layer if (mode == TileLayerMode.Forced) { // move the tile layer by the scale X and scale Y amounts tileLayers[tileLayer].Position += tileLayers[tileLayer].PositionScale; // clamp the layer's new position tileLayers[tileLayer].ClampLayerPosition(); } // if we get here then this layer has been update succesfully - flag it tileLayers[tileLayer].updated = true; } } // check the status of all the layers to see if we have finished for (tileLayer = 0; tileLayer < tileLayers.Length; tileLayer++) { // bail out if we find a layer that has not yet been updated if (tileLayers[tileLayer].updated == false) break; } // are they all completed? if so, break out of the while loop! if (tileLayer == tileLayers.Length) break; } }
//------------------------------------------------------------------------------ // Function: TileLayer // Author: nholmes // Summary: constructor to use when data is available - sets the map data to use, // graphics to use, the size of the tiles and how the layer moves in // relation to it's parent position //------------------------------------------------------------------------------ public TileLayer(string layerName, TileSet tileSet, TileMap tileMap, TileLayerMode layerMode, TileLayer targetLayer, float displayScale, Vector2 positionScale, Vector2 positionOffset, Vector2 displaySize, Color tintColor) { // store the layer's name this.name = layerName; // store the name of the tile set and a reference to them tileSetName = tileSet.Name; this.tileSet = tileSet; // store the name of the tile map and a reference to them tileMapName = tileMap.Name; this.tileMap = tileMap; // store the layer mode this.mode = layerMode; // store the target layer target = targetLayer; // clear the updated status updated = false; // store the scale this.displayScale = displayScale; // store the position scale (this will be ignored if position mode is anything other than 'Scaled' this.positionScale = positionScale; // store the position offset (used by all targeted layer modes) this.positionOffset = positionOffset; // precalculate values used for displaying the tile layer SetDisplaySize(displaySize); // set the tint color this.tintColor = tintColor; }
//------------------------------------------------------------------------------ // Function: TileLayer // Author: nholmes // Summary: constructor to use to create a blank tile layer //------------------------------------------------------------------------------ public TileLayer(Game game, string layerName) { // get a handle to the display manager service displayManager = (DisplayManager)game.Services.GetService(typeof(DisplayManager)); // store the layer's name name = layerName; // store the name of the tile set and a reference to them tileSetName = ""; tileSet = null; // store the name of the tile map and a reference to them tileMapName = ""; tileMap = null; // store the layer mode mode = TileLayerMode.Follow; // set the target layer to be undefined target = null; // default tint color is white tintColor = new Color(255, 255, 255, 255); // clear the updated status updated = false; // store the scale displayScale = 1.0f; // store the position scale (this will be ignored if position mode is anything other than 'Scaled' positionScale = new Vector2(1.0f, 1.0f); // store the position offset (used by all targeted layer modes) positionOffset = new Vector2(0.0f, 0.0f); }