/// <summary> /// Checks whether the given area with the given center tile is a subset of the given layer. /// </summary> /// <param name="map">Reference to the map.</param> /// <param name="center">The center of the area to check.</param> /// <param name="layer">The terrain type of the layer to check.</param> /// <param name="area">The area to check.</param> /// <returns>True if the area is a subset of the given layer, false otherwise.</returns> private bool CheckLayer(IMapAccess map, IIsoTile center, ITerrainType layer, FloodArea area) { foreach (FloodItem floodItem in area) { IIsoTile checkedTile = map.GetIsoTile(center.MapCoords + floodItem.Coordinates); if (checkedTile != null) { if (checkedTile.Type.TerrainA.IsDescendantOf(layer) || checkedTile.Type.TerrainA == layer) { continue; } if (checkedTile.Type.Combination != TerrainCombination.Simple) { if (checkedTile.Type.TerrainB == layer) { /// We have to check the combinations if (((int)floodItem.Combination & (int)checkedTile.Type.Combination) != (int)floodItem.Combination) { return(false); } } else { return(false); } } else { return(false); } } } return(true); }
/// <see cref="IMapTerrainView.GetIsoTileDisplayCoords"/> public RCIntVector GetIsoTileDisplayCoords(RCIntVector position) { RCIntVector navCellCoords = this.MapWindowBC.AttachedWindow.WindowToMapCoords(position).Round(); IIsoTile isotile = this.Map.GetCell(navCellCoords).ParentIsoTile; return(this.MapWindowBC.AttachedWindow.CellToWindowRect(new RCIntRectangle(isotile.GetCellMapCoords(new RCIntVector(0, 0)), isotile.CellSize)).Location); }
private void Form1_MouseClick(object sender, MouseEventArgs e) { if (e.Button == System.Windows.Forms.MouseButtons.Left) { RCIntVector screenCoords = new RCIntVector(e.X, e.Y); RCIntVector mapCoords = (screenCoords / 16) - new RCIntVector(1, 16); IIsoTile target = map.GetIsoTile(mapCoords); if (target != null) { this.replacedTiles.Clear(); IEnumerable <IIsoTile> replacedTiles = this.mapEditor.DrawTerrain(this.map, target, map.Tileset.GetTerrainType(this.terrainTypes[this.selectedTerrain])); foreach (IIsoTile item in replacedTiles) { this.replacedTiles.Add(item); } } this.Invalidate(); } else if (e.Button == System.Windows.Forms.MouseButtons.Right) { this.selectedTerrain++; if (this.selectedTerrain == this.terrainTypes.Count) { this.selectedTerrain = 0; } this.Text = this.terrainTypes[this.selectedTerrain]; } }
/// <see cref="IIsoTileType.GetVariant"/> public IIsoTileVariant GetVariant(IIsoTile isoTile, int variantIdx) { if (isoTile == null) { throw new ArgumentNullException("isoTile"); } if (variantIdx < 0) { throw new ArgumentOutOfRangeException("variantIdx", "Variant index must be non-negative!"); } foreach (Tuple <List <IsoTileVariant>, IIsoTileCondition> branch in this.variants) { if (branch.Item2 == null || branch.Item2.Check(isoTile)) { if (variantIdx >= branch.Item1.Count) { throw new ArgumentOutOfRangeException("variantIdx", string.Format("Variant with index {0} doesn't exists for isometric tile at {1}!", variantIdx, isoTile.MapCoords)); } return(branch.Item1[variantIdx]); } } throw new MapException(string.Format("No matching conditional branch found for isometric tile at {0}!", isoTile.MapCoords)); }
/// <see cref="ITerrainObjectConstraint.Check"/> public RCSet <RCIntVector> Check(IMapAccess map, RCIntVector position) { if (map == null) { throw new ArgumentNullException("map"); } if (position == RCIntVector.Undefined) { throw new ArgumentNullException("position"); } RCSet <RCIntVector> retList = new RCSet <RCIntVector>(); RCIntVector absQuadCoords = position + this.quadCoords; if (absQuadCoords.X >= 0 && absQuadCoords.X < map.Size.X && absQuadCoords.Y >= 0 && absQuadCoords.Y < map.Size.Y) { IQuadTile checkedQuadTile = map.GetQuadTile(absQuadCoords); IIsoTile checkedIsoTile = checkedQuadTile.PrimaryIsoTile; if (checkedIsoTile.Type.TerrainA != this.terrainA || checkedIsoTile.Type.TerrainB != this.terrainB || !this.allowedCombinations.Contains(checkedIsoTile.Type.Combination)) { retList.Add(this.quadCoords); foreach (IQuadTile neighbour in checkedQuadTile.Neighbours) { if (neighbour.PrimaryIsoTile == checkedIsoTile) { retList.Add(neighbour.MapCoords - position); } } } } return(retList); }
/// <see cref="IIsoTileCondition.Check"/> public bool Check(IIsoTile isoTile) { bool retVal; if (this.logicalOp == LogicalOp.AND) { retVal = true; foreach (IIsoTileCondition subcond in this.subconditions) { if (!subcond.Check(isoTile)) { retVal = false; break; } } } else if (this.logicalOp == LogicalOp.OR) { retVal = false; foreach (IIsoTileCondition subcond in this.subconditions) { if (subcond.Check(isoTile)) { retVal = true; break; } } } else { retVal = !this.subconditions[0].Check(isoTile); } return(retVal); }
/// <summary> /// Creates the package that contains the description of the isometric tiles of the given map. /// </summary> /// <param name="map">Reference to the map.</param> /// <returns>The data package that contains the description of the isometric tiles of the given map.</returns> private RCPackage CreateIsoTileListPackage(IMapAccess map) { RCPackage isotileList = RCPackage.CreateCustomDataPackage(MapFileFormat.ISOTILE_LIST); /// Create the terrain type index table. List <string> terrainTypeList = new List <string>(); Dictionary <ITerrainType, int> terrainTypeIndexTable = new Dictionary <ITerrainType, int>(); int terrainTypeIndex = 0; foreach (ITerrainType terrainType in map.Tileset.TerrainTypes) { terrainTypeList.Add(terrainType.Name); terrainTypeIndexTable.Add(terrainType, terrainTypeIndex); terrainTypeIndex++; } isotileList.WriteStringArray(0, terrainTypeList.ToArray()); /// Create the packages of the isometric tiles. RCSet <IIsoTile> processedIsoTiles = new RCSet <IIsoTile>(); List <RCPackage> isotilePackages = new List <RCPackage>(); int isotileInfoLength = 0; for (int row = 0; row < map.Size.Y; row++) { for (int column = 0; column < map.Size.X; column++) { IIsoTile currIsoTile = map.GetQuadTile(new RCIntVector(column, row)).PrimaryIsoTile; if (!processedIsoTiles.Contains(currIsoTile)) { RCPackage isotilePackage = RCPackage.CreateCustomDataPackage(MapFileFormat.ISOTILE); isotilePackage.WriteShort(0, (short)column); isotilePackage.WriteShort(1, (short)row); isotilePackage.WriteByte(2, (byte)terrainTypeIndexTable[currIsoTile.Type.TerrainA]); isotilePackage.WriteByte(3, currIsoTile.Type.TerrainB != null ? (byte)terrainTypeIndexTable[currIsoTile.Type.TerrainB] : (byte)0); isotilePackage.WriteByte(4, (byte)currIsoTile.Type.Combination); isotilePackage.WriteByte(5, (byte)currIsoTile.VariantIdx); isotilePackages.Add(isotilePackage); processedIsoTiles.Add(currIsoTile); isotileInfoLength += isotilePackage.PackageLength; } } } /// Write the isometric tile packages into the final package byte[] isotileInfoBytes = new byte[isotileInfoLength]; int offset = 0; foreach (RCPackage isotilePackage in isotilePackages) { offset += isotilePackage.WritePackageToBuffer(isotileInfoBytes, offset); } isotileList.WriteByteArray(1, isotileInfoBytes); return(isotileList); }
/// <summary> /// Converts the given isometric tile into a SpriteInst structure. /// </summary> /// <param name="isotile">The isometric tile to convert.</param> /// <returns>The converted SpriteInst structure.</returns> private SpriteRenderInfo ConvertIsoTileToSpriteInst(IIsoTile isotile) { return(new SpriteRenderInfo() { SpriteGroup = SpriteGroupEnum.IsoTileSpriteGroup, Index = isotile.Variant.Index, DisplayCoords = this.MapWindowBC.FullWindow.CellToWindowRect(new RCIntRectangle(isotile.GetCellMapCoords(new RCIntVector(0, 0)), isotile.CellSize)).Location, Section = RCIntRectangle.Undefined }); }
/// <summary> /// Draws the given isometric tile to the given device context. /// </summary> public void DrawTile(IIsoTile tile, bool isReplaced, Graphics g) { RCIntVector screenCoords = TILE_SIZE * (tile.MapCoords + new RCIntVector(1, 16)); if (!this.tileImages.ContainsKey(tile.Type)) { this.CreateTileImage(tile.Type); } g.DrawImageUnscaled(this.tileImages[tile.Type], screenCoords.X, screenCoords.Y); g.DrawImageUnscaled(isReplaced ? this.gridChangedImage : this.gridImage, screenCoords.X, screenCoords.Y); }
/// <summary> /// Adds the given isometric tile and all of its cutting quadratic tiles into the update lists. /// </summary> /// <param name="isoTile">The isometric tile to add.</param> /// <param name="isoTileUpdateList">The isometric update list.</param> /// <param name="quadTileUpdateList">The quadratic update list.</param> private void AddIsoTileToUpdate(IIsoTile isoTile, RCSet <IIsoTile> isoTileUpdateList, RCSet <IQuadTile> quadTileUpdateList) { if (isoTile != null && isoTileUpdateList.Add(isoTile)) { foreach (IQuadTile cuttingQuadTile in isoTile.CuttingQuadTiles) { if (cuttingQuadTile != null && (this.fowCacheMatrix.GetFullFowFlagsAtQuadTile(cuttingQuadTile.MapCoords) != FOWTileFlagsEnum.None || this.fowCacheMatrix.GetPartialFowFlagsAtQuadTile(cuttingQuadTile.MapCoords) != FOWTileFlagsEnum.None)) { quadTileUpdateList.Add(cuttingQuadTile); } } } }
/// <see cref="IIsoTileType.GetNumOfVariants"/> public int GetNumOfVariants(IIsoTile isoTile) { if (isoTile == null) { throw new ArgumentNullException("isoTile"); } foreach (Tuple <List <IsoTileVariant>, IIsoTileCondition> branch in this.variants) { if (branch.Item2 == null || branch.Item2.Check(isoTile)) { return(branch.Item1.Count); } } throw new MapException(string.Format("No matching conditional branch found for isometric tile at {0}!", isoTile.MapCoords)); }
/// <see cref="IIsoTileCondition.Check"/> public bool Check(IIsoTile isoTile) { if (isoTile == null) { throw new ArgumentNullException("isoTile"); } IIsoTile neighbour = isoTile.GetNeighbour(this.direction); if (neighbour == null) { return(false); } if (isoTile.Type.TerrainA != neighbour.Type.TerrainA || isoTile.Type.TerrainB != neighbour.Type.TerrainB) { return(false); } return(this.combination == neighbour.Type.Combination); }
/// <see cref="IMapEditorService.DrawTerrain"/> public void DrawTerrain(RCIntVector position, string terrainType) { if (this.scenarioManager.ActiveScenario == null) { throw new InvalidOperationException("No active scenario!"); } if (position == RCIntVector.Undefined) { throw new ArgumentNullException("position"); } if (terrainType == null) { throw new ArgumentNullException("terrainType"); } RCIntVector navCellCoords = this.mapWindowBC.AttachedWindow.WindowToMapCoords(position).Round(); IIsoTile isotile = this.scenarioManager.ActiveScenario.Map.GetCell(navCellCoords).ParentIsoTile; IEnumerable <IIsoTile> affectedIsoTiles = this.mapEditor.DrawTerrain(this.scenarioManager.ActiveScenario.Map, isotile, this.scenarioManager.ActiveScenario.Map.Tileset.GetTerrainType(terrainType)); foreach (IIsoTile affectedIsoTile in affectedIsoTiles) { RCNumRectangle isoTileRect = new RCNumRectangle(affectedIsoTile.GetCellMapCoords(new RCIntVector(0, 0)), affectedIsoTile.CellSize) - new RCNumVector(1, 1) / 2; foreach (Entity affectedEntity in this.scenarioManager.ActiveScenario.GetElementsOnMap <Entity>(isoTileRect, MapObjectLayerEnum.AirObjects, MapObjectLayerEnum.GroundObjects)) { if (affectedEntity.CheckPlacementConstraints(affectedEntity.MapObject.QuadraticPosition.Location, new RCSet <Entity>()).Count != 0) { affectedEntity.DetachFromMap(); this.scenarioManager.ActiveScenario.RemoveElementFromScenario(affectedEntity); affectedEntity.Dispose(); } } } }
/// <summary> /// Fills up the layers from the base layer up to the target layer. /// </summary> /// <param name="center">The center of the draw operation.</param> /// <param name="targetTerrain">The target terrain of the draw operation.</param> /// <param name="baseTerrain">The base layer of the draw operation.</param> public void FillLayers(IMapAccess map, IIsoTile center, ITerrainType targetTerrain, ITerrainType baseTerrain) { /// Find the biggest flood area to be filled. FloodArea areaToFill = new FloodArea(); ITerrainType[] layersToFill = targetTerrain.FindRoute(baseTerrain); for (int routeIdx = 0; routeIdx < layersToFill.Length; routeIdx++) { ITerrainType prevTerrain = routeIdx - 1 >= 0 ? layersToFill[routeIdx - 1] : null; if (prevTerrain != null) { areaToFill.Enlarge(prevTerrain.TransitionLength + 1); } } /// Fill the appropriate layers for (int routeIdx = layersToFill.Length - 1; routeIdx >= 0; routeIdx--) { /// Fill the current layer at the appropriate area. ITerrainType currLayer = layersToFill[routeIdx]; foreach (FloodItem floodItem in areaToFill) { IIsoTile filledTile = map.GetIsoTile(center.MapCoords + floodItem.Coordinates); if (filledTile != null) { if (filledTile.Type.Combination != TerrainCombination.Simple) { /// Mixed tile. if (filledTile.Type.TerrainB == currLayer) { int newCombInt = (int)filledTile.Type.Combination | (floodItem.Combination != TerrainCombination.Simple ? (int)floodItem.Combination : 0xF); TerrainCombination newComb = newCombInt != 0xF ? (TerrainCombination)newCombInt : TerrainCombination.Simple; if (newComb != filledTile.Type.Combination) { filledTile.ExchangeType( newComb == TerrainCombination.Simple ? map.Tileset.GetIsoTileType(filledTile.Type.TerrainB.Name) : map.Tileset.GetIsoTileType(filledTile.Type.TerrainA.Name, filledTile.Type.TerrainB.Name, newComb)); } } else if (currLayer.IsDescendantOf(filledTile.Type.TerrainB)) { throw new MapException("Filling over the topmost layer is not possible!"); } } else { /// Simple tile. if (filledTile.Type.TerrainA == currLayer.Parent) { filledTile.ExchangeType( floodItem.Combination == TerrainCombination.Simple ? map.Tileset.GetIsoTileType(currLayer.Name) : map.Tileset.GetIsoTileType(filledTile.Type.TerrainA.Name, currLayer.Name, floodItem.Combination)); } else if (currLayer.IsDescendantOf(filledTile.Type.TerrainA)) { throw new MapException("Filling over the topmost layer is not possible!"); } } } } if (routeIdx > 0) { areaToFill.Reduce(); } } }
/// <summary> /// Clears the given layers for a draw operation. /// </summary> /// <param name="map">Reference to the map.</param> /// <param name="center">The center of the draw operation.</param> /// <param name="targetTerrain">The target terrain of the draw operation.</param> /// <param name="baseTerrain">The base layer of the draw operation.</param> /// <param name="layersToClear">The route from the target terrain up to a topmost layer in the terrain tree.</param> private void ClearLayers(IMapAccess map, IIsoTile center, ITerrainType targetTerrain, ITerrainType baseTerrain, ITerrainType[] layersToClear) { /// Find the biggest flood area to be cleared. FloodArea areaToClear = new FloodArea(); ITerrainType lastUninjuredLayer = null; for (int routeIdx = 0; routeIdx < layersToClear.Length; routeIdx++) { ITerrainType currTerrain = layersToClear[routeIdx]; if (lastUninjuredLayer == null) { /// We are going downstairs. ITerrainType nextTerrain = layersToClear[routeIdx + 1]; if (nextTerrain.Parent == currTerrain) { /// Last uninjured layer found, from now we go upstairs. lastUninjuredLayer = currTerrain; /// Enlarge the clear area by 1 if there was a previous layer along the way downstairs. ITerrainType prevTerrain = routeIdx - 1 >= 0 ? layersToClear[routeIdx - 1] : null; if (prevTerrain != null) { areaToClear.Enlarge(1); } } else { /// Enlarge the clear area by the transition length of the previous layer if there /// was a previous layer along the way downstairs. ITerrainType prevTerrain = routeIdx - 1 >= 0 ? layersToClear[routeIdx - 1] : null; if (prevTerrain != null) { areaToClear.Enlarge(prevTerrain.TransitionLength + 1); } } } else { /// We are going upstairs. ITerrainType prevTerrain = layersToClear[routeIdx - 1]; if (prevTerrain != lastUninjuredLayer) { areaToClear.Enlarge(currTerrain.TransitionLength + 1); } } } /// Clear the appropriate layers. if (lastUninjuredLayer == null) { throw new MapException("Last uninjured layer not found for draw terrain operation!"); } for (int routeIdx = layersToClear.Length - 1; routeIdx >= 0; routeIdx--) { ITerrainType currLayer = layersToClear[routeIdx]; if (currLayer == lastUninjuredLayer) { break; } /// Clear the current layer at the appropriate area. foreach (FloodItem floodItem in areaToClear) { IIsoTile clearedTile = map.GetIsoTile(center.MapCoords + floodItem.Coordinates); if (clearedTile != null) { if (clearedTile.Type.Combination != TerrainCombination.Simple) { /// Mixed tile. if (clearedTile.Type.TerrainB.IsDescendantOf(currLayer)) { /// Check whether TerrainB will be cleared by another branch or this is an error. if (!layersToClear.Contains(clearedTile.Type.TerrainB)) { continue; } else { throw new MapException("Clearing non-topmost layer is not possible!"); } } if (clearedTile.Type.TerrainB == currLayer) { TerrainCombination newComb = (TerrainCombination)((int)clearedTile.Type.Combination & ~(floodItem.Combination != TerrainCombination.Simple ? (int)floodItem.Combination : 0xF)); if (newComb != clearedTile.Type.Combination) { clearedTile.ExchangeType( newComb == TerrainCombination.Simple ? map.Tileset.GetIsoTileType(clearedTile.Type.TerrainA.Name) : map.Tileset.GetIsoTileType(clearedTile.Type.TerrainA.Name, clearedTile.Type.TerrainB.Name, newComb)); } } } else { /// Simple tile. if (clearedTile.Type.TerrainA.IsDescendantOf(currLayer)) { /// Check whether TerrainA will be cleared by another branch or this is an error. if (!layersToClear.Contains(clearedTile.Type.TerrainA)) { continue; } else { throw new MapException("Clearing non-topmost layer is not possible!"); } } if (clearedTile.Type.TerrainA == currLayer) { TerrainCombination newComb = (TerrainCombination)(0xF & ~(floodItem.Combination != TerrainCombination.Simple ? (int)floodItem.Combination : 0xF)); clearedTile.ExchangeType( newComb == TerrainCombination.Simple ? map.Tileset.GetIsoTileType(clearedTile.Type.TerrainA.Parent.Name) : map.Tileset.GetIsoTileType(clearedTile.Type.TerrainA.Parent.Name, clearedTile.Type.TerrainA.Name, newComb)); } } } } if (routeIdx > 1) { areaToClear.Reduce(); } } }
/// <see cref="IMapEditor.DrawTerrain"/> public IEnumerable <IIsoTile> DrawTerrain(IMapAccess targetMap, IIsoTile targetTile, ITerrainType terrainType) { if (targetMap == null) { throw new ArgumentNullException("targetMap"); } if (targetTile == null) { throw new ArgumentNullException("targetTile"); } if (terrainType == null) { throw new ArgumentNullException("terrainType"); } if (targetMap.Tileset != terrainType.Tileset) { throw new InvalidOperationException("The tileset of the new terrain type must be the same as the tileset of the map!"); } /// Notify the map that the tile exchanging procedure is started. targetMap.BeginExchangingTiles(); /// First we have to search the basis layer of the draw operation. ITerrainType baseLayer = terrainType; FloodArea floodArea = new FloodArea(); while (!this.CheckLayer(targetMap, targetTile, baseLayer, floodArea)) { floodArea.Enlarge(baseLayer.TransitionLength + 1); baseLayer = baseLayer.Parent; if (baseLayer == null) { throw new MapException("Basis-layer not found for draw terrain operation!"); } } /// Clear the appropriate areas of the map around the target tile of the draw operation. foreach (ITerrainType topmostLayer in targetMap.Tileset.TerrainTypes) { if (topmostLayer.IsDescendantOf(baseLayer) && topmostLayer != terrainType && !topmostLayer.HasChildren) { ITerrainType[] layersToClear = terrainType.FindRoute(topmostLayer); this.ClearLayers(targetMap, targetTile, terrainType, baseLayer, layersToClear); } } /// Fill the appropriate areas of the map around the target tile of the draw operation. this.FillLayers(targetMap, targetTile, terrainType, baseLayer); /// Force regenerating the variant of the draw operation center and its neighbours. targetTile.ExchangeType(targetTile.Type); /// Remove the terrain objects that are violating the new map terrain. IEnumerable <IIsoTile> affectedIsoTiles = targetMap.EndExchangingTiles(); foreach (IIsoTile affectedIsoTile in affectedIsoTiles) { foreach (IQuadTile cuttingQuadTile in affectedIsoTile.CuttingQuadTiles) { ITerrainObject affectedTerrainObj = cuttingQuadTile.TerrainObject; if (affectedTerrainObj != null && affectedTerrainObj.Type.CheckConstraints(targetMap, affectedTerrainObj.MapCoords).Count != 0) { this.RemoveTerrainObject(targetMap, affectedTerrainObj); } } } return(affectedIsoTiles); }