/// <summary> /// Gets the current FOW-state at the given quadratic tile. /// </summary> /// <param name="quadCoords">The quadratic coordinates of the tile.</param> /// <returns>The current FOW-state at the given quadratic tile.</returns> public FOWTypeEnum GetFowStateAtQuadTile(RCIntVector quadCoords) { /// Consider FOWTypeEnum.None at coordinates outside of the map. if (quadCoords.X < 0 || quadCoords.X >= this.scenario.Map.Size.X || quadCoords.Y < 0 || quadCoords.Y >= this.scenario.Map.Size.Y) { return(FOWTypeEnum.None); } /// If the FOW-state of the given tile has not yet been cached or is invalid, then calculate and save it to the cache. if (this.scenario.CurrentFrameIndex != this.fowCacheMatrix[quadCoords.X, quadCoords.Y].LastFowStateUpdate) { bool isFowRunning = false; FOWTypeEnum minimumFow = FOWTypeEnum.Full; for (int idx = 0; idx < Player.MAX_PLAYERS; idx++) { if (this.runningFows[idx] != null) { FOWTypeEnum fowAtQuadTile = this.runningFows[idx].GetFogOfWar(quadCoords); if (fowAtQuadTile < minimumFow) { minimumFow = fowAtQuadTile; } isFowRunning = true; } } this.fowCacheMatrix[quadCoords.X, quadCoords.Y].LastFowStateUpdate = this.scenario.CurrentFrameIndex; this.fowCacheMatrix[quadCoords.X, quadCoords.Y].FowState = isFowRunning ? minimumFow : FOWTypeEnum.None; } /// Return with the cached value. return(this.fowCacheMatrix[quadCoords.X, quadCoords.Y].FowState); }
/// <see cref="IFogOfWarBC.GetEntitySnapshotsInWindow"/> public IEnumerable <EntitySnapshot> GetEntitySnapshotsInWindow(RCIntRectangle quadWindow) { if (this.ActiveScenario == null) { throw new InvalidOperationException("No active scenario!"); } if (this.runningFowsCount == 0) { yield break; } for (int column = quadWindow.Left; column < quadWindow.Right; column++) { for (int row = quadWindow.Top; row < quadWindow.Bottom; row++) { RCIntVector quadCoords = new RCIntVector(column, row); /// If the FOW is full at the current quadratic tile -> continue with the next. FOWTypeEnum fowAtQuadTile = this.fowCacheMatrix.GetFowStateAtQuadTile(quadCoords); if (fowAtQuadTile == FOWTypeEnum.Full) { continue; } /// Add the entity snapshot into the returned list. EntitySnapshot snapshot = this.fowCacheMatrix.GetEntitySnapshotAtQuadTile(quadCoords); if (snapshot != null) { yield return(snapshot); } } } }
/// <summary> /// Tests whether the FOWSpriteGroup for drawing the given type of Fog Of War is generated properly. /// </summary> private void FOWSpriteGroupTest(FOWTypeEnum fowType) { /// Load the Fog Of War sprite palette and create a FOWSpriteGroup out of it. string fowSpritePalettePath = System.IO.Path.Combine(FOW_SPRITEPALETTE_DIR, FOW_SPRITEPALETTE_FILE); XDocument fowSpritePaletteXml = XDocument.Load(fowSpritePalettePath); ISpritePalette <FOWTypeEnum> spritePalette = XmlHelper.LoadSpritePalette(fowSpritePaletteXml.Root, fowType, FOW_SPRITEPALETTE_DIR); FOWSpriteGroup spriteGroup = new FOWSpriteGroup(spritePalette, fowType); spriteGroup.Load(); /// Check that 2 UISprite instances are the same if and only if they images have the same bytes. PrivateObject spriteGroupObj = new PrivateObject(spriteGroup, new PrivateType(typeof(SpriteGroup))); List <UISprite> fowSprites = (List <UISprite>)spriteGroupObj.GetField("spriteList"); RCSet <UISprite> savedFowSprites = new RCSet <UISprite>(); for (int indexA = 0; indexA < fowSprites.Count - 1; indexA++) { if (fowSprites[indexA] == null) { continue; } byte[] bytesOfSpriteA = fowSprites[indexA].Save(); for (int indexB = indexA + 1; indexB < fowSprites.Count; indexB++) { if (fowSprites[indexB] == null) { continue; } byte[] bytesOfSpriteB = fowSprites[indexB].Save(); if (StructuralComparisons.StructuralEqualityComparer.Equals(bytesOfSpriteA, bytesOfSpriteB)) { Assert.AreEqual(fowSprites[indexA], fowSprites[indexB]); } else { Assert.AreNotEqual(fowSprites[indexA], fowSprites[indexB]); } } if (savedFowSprites.Add(fowSprites[indexA])) { string outputPath = System.IO.Path.Combine(OUTPUT_DIR, string.Format("{0}_{1}.png", fowType, Convert.ToString(indexA, 2).PadLeft(9, '0'))); File.WriteAllBytes(outputPath, bytesOfSpriteA); } } /// Destroy the FOWSpriteGroup object. spriteGroup.Unload(); }
/// <summary> /// Gets the FOW-status at the given minimap pixel. /// </summary> /// <param name="pixelCoords">The coordinates of the pixel on the minimap image.</param> /// <returns>The FOW-status at the given minimap pixel.</returns> private FOWTypeEnum GetFowStateAtPixel(RCIntVector pixelCoords) { RCIntRectangle quadRect = this.MapWindowBC.Minimap.GetMinimapPixel(pixelCoords).CoveredQuadTiles; FOWTypeEnum lowestFowState = FOWTypeEnum.Full; for (int column = quadRect.Left; column < quadRect.Right; column++) { for (int row = quadRect.Top; row < quadRect.Bottom; row++) { FOWTypeEnum currentFowState = this.fogOfWarBC.GetFowState(new RCIntVector(column, row)); if (currentFowState == FOWTypeEnum.None) { lowestFowState = currentFowState; break; } if ((int)currentFowState < (int)lowestFowState) { lowestFowState = currentFowState; } } } return(lowestFowState); }
/// <summary> /// Constructs a FOWSpriteGroup instance from the given sprite palette. /// </summary> /// <param name="spritePalette">The sprite palette that contains the sprites for drawing the Fog Of War.</param> /// <param name="fowType">The type of the Fog Of War stored in this sprite-group.</param> public FOWSpriteGroup(ISpritePalette <FOWTypeEnum> spritePalette, FOWTypeEnum fowType) { if (spritePalette == null) { throw new ArgumentNullException("spritePalette"); } if (fowType == FOWTypeEnum.None) { throw new ArgumentException("FOWSpriteGroup cannot store sprites for FOWTypeEnum.None!", "fowType"); } if (spritePalette.TransparentColor == RCColor.Undefined) { throw new ArgumentException("Transparent color is not defined for FOW sprite palette!", "spritePalette"); } this.spritePalette = spritePalette; this.fowType = fowType; this.spriteIndices = new Dictionary <FOWTileFlagsEnum, int>(); foreach (KeyValuePair <FOWTileFlagsEnum, string> item in SPRITE_NAMES) { this.spriteIndices[item.Key] = this.spritePalette.GetSpriteIndex(item.Value, this.fowType); } }
/// <summary> /// Calculates the FOW-flags for the given quadratic tile. /// </summary> /// <param name="quadCoords">The quadratic coordinates of the tile.</param> private void CalculateFowFlags(RCIntVector quadCoords) { FOWTypeEnum fowAtQuadCoords = this.GetFowStateAtQuadTile(quadCoords); if (fowAtQuadCoords == FOWTypeEnum.Full) { /// Full FOW-state -> draw only full FOW. this.fowCacheMatrix[quadCoords.X, quadCoords.Y].FullFowFlags = FOWTileFlagsEnum.Current; this.fowCacheMatrix[quadCoords.X, quadCoords.Y].PartialFowFlags = FOWTileFlagsEnum.None; } else if (fowAtQuadCoords == FOWTypeEnum.Partial) { /// Partial FOW-state -> draw partial FOW and calculate full FOW-flags based on the neighbours. FOWTileFlagsEnum fullFowFlags = FOWTileFlagsEnum.None; if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(0, -1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.North; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, -1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.NorthEast; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, 0)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.East; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, 1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.SouthEast; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(0, 1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.South; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, 1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.SouthWest; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, 0)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.West; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, -1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.NorthWest; } this.fowCacheMatrix[quadCoords.X, quadCoords.Y].FullFowFlags = fullFowFlags; this.fowCacheMatrix[quadCoords.X, quadCoords.Y].PartialFowFlags = FOWTileFlagsEnum.Current; } else if (fowAtQuadCoords == FOWTypeEnum.None) { /// No FOW -> calculate full & partial FOW-flags based on the neighbours. FOWTileFlagsEnum fullFowFlags = FOWTileFlagsEnum.None; if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(0, -1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.North; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, -1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.NorthEast; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, 0)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.East; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, 1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.SouthEast; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(0, 1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.South; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, 1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.SouthWest; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, 0)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.West; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, -1)) == FOWTypeEnum.Full) { fullFowFlags |= FOWTileFlagsEnum.NorthWest; } this.fowCacheMatrix[quadCoords.X, quadCoords.Y].FullFowFlags = fullFowFlags; FOWTileFlagsEnum partialFowFlags = FOWTileFlagsEnum.None; if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(0, -1)) != FOWTypeEnum.None) { partialFowFlags |= FOWTileFlagsEnum.North; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, -1)) != FOWTypeEnum.None) { partialFowFlags |= FOWTileFlagsEnum.NorthEast; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, 0)) != FOWTypeEnum.None) { partialFowFlags |= FOWTileFlagsEnum.East; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(1, 1)) != FOWTypeEnum.None) { partialFowFlags |= FOWTileFlagsEnum.SouthEast; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(0, 1)) != FOWTypeEnum.None) { partialFowFlags |= FOWTileFlagsEnum.South; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, 1)) != FOWTypeEnum.None) { partialFowFlags |= FOWTileFlagsEnum.SouthWest; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, 0)) != FOWTypeEnum.None) { partialFowFlags |= FOWTileFlagsEnum.West; } if (this.GetFowStateAtQuadTile(quadCoords + new RCIntVector(-1, -1)) != FOWTypeEnum.None) { partialFowFlags |= FOWTileFlagsEnum.NorthWest; } this.fowCacheMatrix[quadCoords.X, quadCoords.Y].PartialFowFlags = partialFowFlags; } }
/// <summary> /// Implements visibility calculations when at least 1 FogOfWar is running. /// </summary> /// <returns>The results of the visibility calculations.</returns> private FowVisibilityInfo CalculateVisibilityWithFow() { if (this.quadTileWindow == RCIntRectangle.Undefined) { throw new InvalidOperationException("Visibility window not defined!"); } /// Collect the isometric & quadratic tiles that need to be updated. IMapAccess map = this.ActiveScenario.Map; RCSet <IIsoTile> isoTilesToUpdate = new RCSet <IIsoTile>(); RCSet <IQuadTile> quadTilesToUpdate = new RCSet <IQuadTile>(); RCSet <ITerrainObject> terrainObjectsToUpdate = new RCSet <ITerrainObject>(); RCSet <EntitySnapshot> entitySnapshotsToUpdate = new RCSet <EntitySnapshot>(); for (int column = this.quadTileWindow.Left; column < this.quadTileWindow.Right; column++) { for (int row = this.quadTileWindow.Top; row < this.quadTileWindow.Bottom; row++) { RCIntVector quadCoords = new RCIntVector(column, row); /// If the FOW is full at the current quadratic tile -> continue with the next. FOWTypeEnum fowAtQuadTile = this.fowCacheMatrix.GetFowStateAtQuadTile(quadCoords); if (fowAtQuadTile == FOWTypeEnum.Full) { continue; } /// Add the primary & secondary isometric tiles and all of their cutting quadratic tiles into the update lists. IQuadTile quadTileToUpdate = map.GetQuadTile(quadCoords); this.AddIsoTileToUpdate(quadTileToUpdate.PrimaryIsoTile, isoTilesToUpdate, quadTilesToUpdate); this.AddIsoTileToUpdate(quadTileToUpdate.SecondaryIsoTile, isoTilesToUpdate, quadTilesToUpdate); /// Add the terrain object and all of its cutting quadratic tiles into the update lists. this.AddTerrainObjectToUpdate(quadTileToUpdate.TerrainObject, terrainObjectsToUpdate, quadTilesToUpdate); /// Add the entity snapshot and all of its cutting quadratic tiles into the update lists. EntitySnapshot entitySnapshotAtQuadTile = this.fowCacheMatrix.GetEntitySnapshotAtQuadTile(quadCoords); this.AddEntitySnapshotToUpdate(entitySnapshotAtQuadTile, entitySnapshotsToUpdate, quadTilesToUpdate); } } /// Collect the currently visible map objects on the ground. RCSet <MapObject> groundObjectsOnMap = this.ActiveScenario.GetMapObjects( this.mapWindowBC.AttachedWindow.WindowMapCoords, MapObjectLayerEnum.GroundObjects, MapObjectLayerEnum.GroundMissiles); RCSet <MapObject> groundMapObjectsToUpdate = new RCSet <MapObject>(); foreach (MapObject groundMapObj in groundObjectsOnMap) { bool breakLoop = false; for (int col = groundMapObj.QuadraticPosition.Left; !breakLoop && col < groundMapObj.QuadraticPosition.Right; col++) { for (int row = groundMapObj.QuadraticPosition.Top; !breakLoop && row < groundMapObj.QuadraticPosition.Bottom; row++) { if (this.fowCacheMatrix.GetFowStateAtQuadTile(new RCIntVector(col, row)) == FOWTypeEnum.None) { /// Found at least 1 quadratic tile where the map objects is visible. this.AddMapObjectToUpdate(groundMapObj, groundMapObjectsToUpdate, quadTilesToUpdate); breakLoop = true; } } } } /// Collect the currently visible map objects in the air. RCSet <MapObject> airObjectsOnMap = this.ActiveScenario.GetMapObjects( this.mapWindowBC.AttachedWindow.WindowMapCoords, MapObjectLayerEnum.AirObjects, MapObjectLayerEnum.AirMissiles); RCSet <MapObject> airMapObjectsToUpdate = new RCSet <MapObject>(); foreach (MapObject airMapObj in airObjectsOnMap) { bool breakLoop = false; for (int col = airMapObj.QuadraticPosition.Left; !breakLoop && col < airMapObj.QuadraticPosition.Right; col++) { for (int row = airMapObj.QuadraticPosition.Top; !breakLoop && row < airMapObj.QuadraticPosition.Bottom; row++) { if (this.fowCacheMatrix.GetFowStateAtQuadTile(new RCIntVector(col, row)) == FOWTypeEnum.None) { /// Found at least 1 quadratic tile where the map objects is visible. this.AddMapObjectToUpdate(airMapObj, airMapObjectsToUpdate, quadTilesToUpdate); breakLoop = true; } } } } /// Create the calculated visibility info. return(new FowVisibilityInfo { IsoTilesToUpdate = isoTilesToUpdate, TerrainObjectsToUpdate = terrainObjectsToUpdate, QuadTilesToUpdate = quadTilesToUpdate, EntitySnapshotsToUpdate = entitySnapshotsToUpdate, GroundMapObjectsToUpdate = groundMapObjectsToUpdate, AirMapObjectsToUpdate = airMapObjectsToUpdate }); }