/// <summary> /// Constructs a changeset for overwriting an integer field. /// </summary> /// <param name="targetCell">The cell of the target to perform the changeset.</param> /// <param name="modifier">Reference to the modifier.</param> /// <param name="tileset">The tileset of this changeset.</param> public CellChangeSet(RCIntVector targetCell, ICellDataModifier modifier, TileSet tileset) : base(modifier, tileset) { this.CheckAndAssignCtorParams(targetCell); }
/// <summary> /// Constructs a changeset for overwriting an integer field. /// </summary> /// <param name="targetRow">The row of the target to perform the changeset.</param> /// <param name="modifier">Reference to the modifier.</param> /// <param name="tileset">The tileset of this changeset.</param> public RowChangeSet(int targetRow, ICellDataModifier modifier, TileSet tileset) : base(modifier, tileset) { this.CheckAndAssignCtorParams(targetRow); }
/// <summary> /// Constructs a NeighbourCondition instance. /// </summary> /// <param name="combination">The terrain combination of the neighbour to check.</param> /// <param name="direction">The direction of the neighbour to check.</param> /// <param name="tileset">The tileset of this condition.</param> public NeighbourCondition(TerrainCombination combination, MapDirection direction, TileSet tileset) { if (tileset == null) { throw new ArgumentNullException("tileset"); } this.combination = combination; this.direction = direction; this.tileset = tileset; }
/// <summary> /// Constructs a TerrainObjectType instance. /// </summary> /// <param name="name">The name of the TerrainObjectType.</param> /// <param name="imageData">The byte sequence that contains the image data of the TerrainObjectType.</param> /// <param name="quadraticSize">The size of the TerrainObjectType in quadratic tiles.</param> /// <param name="transparentColor">The transparent color of this TerrainObjectType.</param> /// <param name="tileset">Reference to the tileset that this TerrainObjectType belongs to.</param> public TerrainObjectType(string name, byte[] imageData, RCIntVector quadraticSize, RCColor transparentColor, TileSet tileset) { if (name == null) { throw new ArgumentNullException("name"); } if (imageData == null || imageData.Length == 0) { throw new ArgumentNullException("imageData"); } if (quadraticSize == RCIntVector.Undefined) { throw new ArgumentNullException("quadraticSize"); } if (transparentColor == RCColor.Undefined) { throw new ArgumentNullException("transparentColor"); } if (tileset == null) { throw new ArgumentNullException("tileset"); } if (quadraticSize.X <= 0 || quadraticSize.Y <= 0) { throw new ArgumentOutOfRangeException("quadraticSize", "Quadratic size cannot be 0 in any direction!"); } this.name = name; this.imageData = imageData; this.quadraticSize = quadraticSize; this.transparentColor = transparentColor; this.tileset = tileset; this.areaCanBeExcluded = true; this.constraints = new List <ITerrainObjectConstraint>(); this.cellDataChangesets = new List <ICellDataChangeSet>(); this.includedQuadCoords = new RCSet <RCIntVector>(); for (int x = 0; x < quadraticSize.X; x++) { for (int y = 0; y < quadraticSize.Y; y++) { this.includedQuadCoords.Add(new RCIntVector(x, y)); } } }
/// <summary> /// Constructs a mixed tile type for the given terrain types and combination. /// </summary> /// <param name="terrainTypeA">The name of the first terrain type.</param> /// <param name="terrainTypeB">The name of the second terrain type.</param> /// <param name="tileset">The tileset of the tile type.</param> /// <remarks>Terrain type A must be the parent of terrain type B.</remarks> public IsoTileType(string terrainTypeA, string terrainTypeB, TerrainCombination combination, TileSet tileset) { if (terrainTypeA == null) { throw new ArgumentNullException("terrainTypeA"); } if (terrainTypeB == null) { throw new ArgumentNullException("terrainTypeB"); } if (tileset == null) { throw new ArgumentNullException("tileset"); } if (combination == TerrainCombination.Simple) { throw new ArgumentException("combination", "Invalid combination for a mixed tile type!"); } this.tileset = tileset; TerrainType tA = this.tileset.GetTerrainTypeImpl(terrainTypeA); TerrainType tB = this.tileset.GetTerrainTypeImpl(terrainTypeB); if (tB.Parent != tA) { throw new ArgumentException(string.Format("TerrainType '{0}' must be the parent of TerrainType '{1}'!", terrainTypeA, terrainTypeB)); } this.terrainA = tA; this.terrainB = tB; this.combination = combination; this.tmpCurrentBranch = null; this.defaultBranch = null; this.variants = new List <Tuple <List <IsoTileVariant>, IIsoTileCondition> >(); }
/// <summary> /// Constructs a changeset for overwriting an integer field. /// </summary> /// <param name="targetCol">The column of the target to perform the changeset.</param> /// <param name="modifier">Reference to the modifier.</param> /// <param name="tileset">The tileset of this changeset.</param> public ColumnChangeSet(int targetCol, ICellDataModifier modifier, TileSet tileset) : base(modifier, tileset) { this.CheckAndAssignCtorParams(targetCol); }
/// <summary> /// Loads the variants of a tile type from the XML element into the given tileset. /// </summary> /// <param name="fromElem">The XML element to load from.</param> /// <param name="tile">The tile type to load to.</param> /// <param name="tileset">The tileset of the tile type.</param> private static void LoadVariants(XElement fromElem, IsoTileType tile, TileSet tileset) { TileReadStatus status = TileReadStatus.None; foreach (XElement childElem in fromElem.Elements()) { if (status == TileReadStatus.None) { /// We are in the initial read status. if (childElem.Name == XmlTileSetConstants.VARIANT_ELEM) { /// No conditional expression at the current tile type. status = TileReadStatus.NoCondition; LoadVariant(childElem, tile, tileset); } else if (childElem.Name == XmlTileSetConstants.IF_ELEM) { /// Beginning of the conditional branch. status = TileReadStatus.ConditionalBranch; LoadBranch(childElem, tile, tileset); } else { /// Other XML elements not allowed at this read status. throw new TileSetException(string.Format("Unexpected node '{0}'!", childElem.Name)); } } else if (status == TileReadStatus.NoCondition) { /// We are in the read status where only variant elements are allowed. if (childElem.Name == XmlTileSetConstants.VARIANT_ELEM) { LoadVariant(childElem, tile, tileset); } else { /// Other XML elements not allowed at this read status. throw new TileSetException(string.Format("Unexpected node '{0}'!", childElem.Name)); } } else if (status == TileReadStatus.ConditionalBranch) { /// We are in the read status where only conditional branch and default branch elements are allowed. if (childElem.Name == XmlTileSetConstants.ELSEIF_ELEM) { /// Load the conditional branch. LoadBranch(childElem, tile, tileset); } else if (childElem.Name == XmlTileSetConstants.ELSE_ELEM) { /// Load the default branch. status = TileReadStatus.DefaultBranch; foreach (XElement varElem in childElem.Elements(XmlTileSetConstants.VARIANT_ELEM)) { LoadVariant(varElem, tile, tileset); } } else { /// Other XML elements not allowed at this read status. throw new TileSetException(string.Format("Unexpected node '{0}'!", childElem.Name)); } } else { /// Other XML elements not allowed. throw new TileSetException(string.Format("Unexpected node '{0}' after default branch!", childElem.Name)); } } }
/// <summary> /// Load a cell data changeset from the given XML element. /// </summary> /// <param name="fromElem">The XML element to load from.</param> /// <param name="tileset">The tileset being loaded.</param> /// <returns>The loaded cell data changeset.</returns> private static ICellDataChangeSet LoadCellDataChangeSet(XElement fromElem, TileSet tileset) { ICellDataChangeSet retObj = null; ICellDataModifier modifier = null; /// Load the name of the target field. XAttribute fieldAttr = fromElem.Attribute(XmlTileSetConstants.CELLDATACHANGESET_FIELD_ATTR); if (fieldAttr == null) { throw new TileSetException("Field name not defined for a data changeset element!"); } if (fieldAttr.Value == XmlTileSetConstants.CELLDATA_ISWALKABLE_NAME) { modifier = new WalkabilityFlagModifier(XmlHelper.LoadBool(fromElem.Value)); } else if (fieldAttr.Value == XmlTileSetConstants.CELLDATA_ISBUILDABLE_NAME) { modifier = new BuildabilityFlagModifier(XmlHelper.LoadBool(fromElem.Value)); } else if (fieldAttr.Value == XmlTileSetConstants.CELLDATA_GROUNDLEVEL_NAME) { modifier = new GroundLevelModifier(XmlHelper.LoadInt(fromElem.Value)); } if (modifier == null) { throw new TileSetException("Unexpected field name defined for a data changeset element!"); } switch (fromElem.Name.LocalName) { case XmlTileSetConstants.CELLDATACHANGESET_ALL_ELEM: retObj = new CellDataChangeSetBase(modifier, tileset); break; case XmlTileSetConstants.CELLDATACHANGESET_CELL_ELEM: XAttribute cellAttr = fromElem.Attribute(XmlTileSetConstants.CELLDATACHANGESET_CELL_CELL_ATTR); if (cellAttr == null) { throw new TileSetException("Cell not defined for a cell data changeset element!"); } retObj = new CellChangeSet(XmlHelper.LoadIntVector(cellAttr.Value), modifier, tileset); break; case XmlTileSetConstants.CELLDATACHANGESET_COL_ELEM: XAttribute colIndexAttr = fromElem.Attribute(XmlTileSetConstants.CELLDATACHANGESET_COL_INDEX_ATTR); if (colIndexAttr == null) { throw new TileSetException("Column not defined for a column data changeset element!"); } retObj = new ColumnChangeSet(XmlHelper.LoadInt(colIndexAttr.Value), modifier, tileset); break; case XmlTileSetConstants.CELLDATACHANGESET_QUARTER_ELEM: XAttribute quarterAttr = fromElem.Attribute(XmlTileSetConstants.CELLDATACHANGESET_QUARTER_WHICH_ATTR); if (quarterAttr == null) { throw new TileSetException("Quarter not defined for a quarter data changeset element!"); } MapDirection quarter; if (!EnumMap <MapDirection, string> .TryDemap(quarterAttr.Value, out quarter)) { throw new TileSetException(string.Format("Unexpected quarter '{0}' defined for quarter data changeset!", quarterAttr.Value)); } retObj = new IsoQuarterChangeSet(quarter, modifier, tileset); break; case XmlTileSetConstants.CELLDATACHANGESET_RECT_ELEM: XAttribute rectAttr = fromElem.Attribute(XmlTileSetConstants.CELLDATACHANGESET_RECT_RECT_ATTR); if (rectAttr == null) { throw new TileSetException("Rectangle not defined for a rectangle data changeset element!"); } retObj = new RectangleChangeSet(XmlHelper.LoadIntRectangle(rectAttr.Value), modifier, tileset); break; case XmlTileSetConstants.CELLDATACHANGESET_ROW_ELEM: XAttribute rowIndexAttr = fromElem.Attribute(XmlTileSetConstants.CELLDATACHANGESET_ROW_INDEX_ATTR); if (rowIndexAttr == null) { throw new TileSetException("Row not defined for a row data changeset element!"); } retObj = new RowChangeSet(XmlHelper.LoadInt(rowIndexAttr.Value), modifier, tileset); break; default: throw new TileSetException(string.Format("Unexpected data changeset element '{0}'!", fromElem.Name)); } if (retObj == null) { throw new TileSetException("Unable to load cell data changeset!"); } return(retObj); }
/// <summary> /// Load a tile-constraint for a terrain object from the given XML element. /// </summary> /// <param name="fromElem">The XML element to load from.</param> /// <param name="terrainObj">The terrain object.</param> /// <param name="tileset">The tileset being loaded.</param> /// <returns>The loaded tile-constraint.</returns> private static ITerrainObjectConstraint LoadTileConstraint(XElement fromElem, TerrainObjectType terrainObj, TileSet tileset) { /// Load the attributes of the constraint. XAttribute quadCoordsAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_TILECONSTRAINT_QUADCOORDS_ATTR); XAttribute terrainAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_TILECONSTRAINT_TERRAIN_ATTR); XAttribute terrainAAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_TILECONSTRAINT_TERRAINA_ATTR); XAttribute terrainBAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_TILECONSTRAINT_TERRAINB_ATTR); XAttribute combinationsAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_TILECONSTRAINT_COMBINATIONS_ATTR); if (quadCoordsAttr == null) { throw new TileSetException("Quadratic coordinates not defined for tile constraint element!"); } if (terrainAttr != null && (terrainAAttr != null || terrainBAttr != null || combinationsAttr != null)) { throw new TileSetException("Invalid attributes defined for tile constraint on a simple tile!"); } if (terrainAttr == null && (terrainAAttr == null || terrainBAttr == null || combinationsAttr == null)) { throw new TileSetException("Invalid attributes defined for tile constraint on a mixed tile!"); } RCIntVector quadCoords = XmlHelper.LoadIntVector(quadCoordsAttr.Value); if (terrainObj.IsExcluded(quadCoords)) { throw new TileSetException(string.Format("TileConstraint at excluded coordinates {0} cannot be defined!", quadCoords)); } if (terrainAttr != null) { TerrainType terrain = tileset.GetTerrainTypeImpl(terrainAttr.Value); return(new IsoTileConstraint(quadCoords, terrain, tileset)); } else { TerrainType terrainA = tileset.GetTerrainTypeImpl(terrainAAttr.Value); TerrainType terrainB = tileset.GetTerrainTypeImpl(terrainBAttr.Value); List <TerrainCombination> combinations = new List <TerrainCombination>(); string[] combinationStrings = combinationsAttr.Value.Split(';'); if (combinationStrings.Length == 0) { throw new TileSetException("Terrain combination not defined for tile constraint on a mixed tile!"); } foreach (string combStr in combinationStrings) { TerrainCombination combination; if (!EnumMap <TerrainCombination, string> .TryDemap(combStr, out combination) || combination == TerrainCombination.Simple) { throw new TileSetException(string.Format("Unexpected terrain combination '{0}' defined for tile constraint!", combStr)); } combinations.Add(combination); } return(new IsoTileConstraint(quadCoords, terrainA, terrainB, combinations, tileset)); } }
/// <summary> /// Reads the given XML document, and constructs a TileSet object from it. /// </summary> /// <param name="xmlStr">The string that contains the XML document to read.</param> /// <param name="imageDir">The directory where the referenced images can be found. (TODO: this is a hack!)</param> /// <returns>The constructed TileSet object.</returns> public static TileSet Read(string xmlStr, string imageDir) { if (xmlStr == null) { throw new ArgumentNullException("xmlStr"); } if (imageDir == null) { throw new ArgumentNullException("imageDir"); } tmpImageDir = imageDir; /// Load the XML document. XDocument xmlDoc = XDocument.Parse(xmlStr); XAttribute tilesetNameAttr = xmlDoc.Root.Attribute(XmlTileSetConstants.TILESET_NAME_ATTR); XElement terrainTreeElem = xmlDoc.Root.Element(XmlTileSetConstants.TERRAINTYPE_ELEM); XElement declareTilesElem = xmlDoc.Root.Element(XmlTileSetConstants.DECLARETILES_ELEM); XElement declareTerrainObjectsElem = xmlDoc.Root.Element(XmlTileSetConstants.DECLARETERRAINOBJECTS_ELEM); if (tilesetNameAttr == null) { throw new TileSetException("Tileset name not defined!"); } if (terrainTreeElem == null) { throw new TileSetException("Terrain-tree not defined!"); } if (declareTilesElem == null) { throw new TileSetException("Tile declarations not found"); } /// Create the TileSet object. TileSet tileset = new TileSet(tilesetNameAttr.Value); /// Load the terrain tree. LoadTerrainTree(terrainTreeElem, null, tileset); /// Load the simple tiles. foreach (XElement simpleTileElem in declareTilesElem.Elements(XmlTileSetConstants.SIMPLETILE_ELEM)) { LoadSimpleTile(simpleTileElem, tileset); } /// Load the mixed tiles. foreach (XElement mixedTileElem in declareTilesElem.Elements(XmlTileSetConstants.MIXEDTILE_ELEM)) { LoadMixedTile(mixedTileElem, tileset); } /// Load the terrain objects. if (declareTerrainObjectsElem != null) { foreach (XElement terrainObjElem in declareTerrainObjectsElem.Elements(XmlTileSetConstants.TERRAINOBJECT_ELEM)) { LoadTerrainObject(terrainObjElem, tileset); } } tileset.CheckAndFinalize(); return(tileset); }
/// <summary> /// Loads a terrain object definition from the XML element into the given tileset. /// </summary> /// <param name="fromElem">The XML element to load from.</param> /// <param name="tileset">The TileSet to load to.</param> private static void LoadTerrainObject(XElement fromElem, TileSet tileset) { XAttribute nameAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_NAME_ATTR); XAttribute imageAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_IMAGE_ATTR); XAttribute quadSizeAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_QUADSIZE_ATTR); XAttribute transpColorAttr = fromElem.Attribute(XmlTileSetConstants.TERRAINOBJ_TRANSPCOLOR_ATTR); if (nameAttr == null) { throw new TileSetException("Name not defined for terrain object!"); } if (imageAttr == null) { throw new TileSetException("Image not defined for terrain object!"); } if (quadSizeAttr == null) { throw new TileSetException("Quadratic size not defined for terrain object!"); } if (transpColorAttr == null) { throw new TileSetException("Transparent color not defined for terrain object!"); } /// Read the image data. string imagePath = Path.Combine(tmpImageDir, imageAttr.Value); byte[] imageData = File.ReadAllBytes(imagePath); tileset.CreateTerrainObjectType(nameAttr.Value, imageData, XmlHelper.LoadIntVector(quadSizeAttr.Value), XmlHelper.LoadColor(transpColorAttr.Value)); TerrainObjectType terrainObj = tileset.GetTerrainObjectTypeImpl(nameAttr.Value); /// Apply the defined area exclusions. foreach (XElement excludeAreaElem in fromElem.Elements(XmlTileSetConstants.TERRAINOBJ_EXCLUDEAREA_ELEM)) { XAttribute rectAttr = excludeAreaElem.Attribute(XmlTileSetConstants.TERRAINOBJ_EXCLUDEAREA_RECT_ATTR); if (rectAttr == null) { throw new TileSetException("The rectangle of the excluded area not defined!"); } terrainObj.ExcludeArea(XmlHelper.LoadIntRectangle(rectAttr.Value)); } /// Load the constraints and the cell data changesets. foreach (XElement childElem in fromElem.Elements()) { if (childElem.Name.LocalName == XmlTileSetConstants.TERRAINOBJ_TILECONSTRAINT_ELEM) { ITerrainObjectConstraint constraint = LoadTileConstraint(childElem, terrainObj, tileset); terrainObj.AddConstraint(constraint); } else if (childElem.Name.LocalName != XmlTileSetConstants.TERRAINOBJ_EXCLUDEAREA_ELEM) { ICellDataChangeSet changeset = LoadCellDataChangeSet(childElem, tileset); terrainObj.AddCellDataChangeset(changeset); } /// TODO: loading other constraint types can take place here! } }
/// <summary> /// Constructs a ComplexCondition instance. /// </summary> /// <param name="subconditions">The subconditions connected by a logical operator.</param> /// <param name="logicalOp">The operator.</param> /// <param name="tileset">The tileset of this condition.</param> public ComplexCondition(List <IIsoTileCondition> subconditions, LogicalOp logicalOp, TileSet tileset) { if (subconditions == null) { throw new ArgumentNullException("subconditions"); } if (tileset == null) { throw new ArgumentNullException("tileset"); } if (logicalOp == LogicalOp.AND || logicalOp == LogicalOp.OR) { if (subconditions.Count < 2) { throw new TileSetException("At least 2 subconditions must be defined in case of LogicalOp.AND or LogicalOp.OR operators!"); } } else { if (subconditions.Count != 1) { throw new TileSetException("Only one subcondition must be defined in case of LogicalOp.NOT operator!"); } } this.subconditions = new List <IIsoTileCondition>(subconditions); this.logicalOp = logicalOp; this.tileset = tileset; }
/// <summary> /// Constructs a changeset for overwriting an integer field. /// </summary> /// <param name="targetRect">The rectangle of the target to perform the changeset.</param> /// <param name="modifier">Reference to the modifier.</param> /// <param name="tileset">The tileset of this changeset.</param> public RectangleChangeSet(RCIntRectangle targetRect, ICellDataModifier modifier, TileSet tileset) : base(modifier, tileset) { this.CheckAndAssignCtorParams(targetRect); }