/// <summary>
        /// Makes terrain visible and sets its appearance to its regular value, and ensures that it will
        /// remain as such as long as it is visible.
        /// </summary>
        /// <param name="terrain">Terrain to modify.</param>
        protected override void UpdateTerrainSeen(RogueLikeCell terrain)
        {
            // Invalid use of MemoryFieldOfViewHandlerBase
            if (!(terrain is MemoryAwareRogueLikeCell awareTerrain))
            {
                throw new InvalidOperationException(
                          $"{nameof(MemoryFieldOfViewHandlerBase)} must only be used on a  map that contains {nameof(MemoryAwareRogueLikeCell)} instances as its terrain.");
            }

            // If the appearances don't match currently, synchronize them
            if (!awareTerrain.LastSeenAppearance.Matches(awareTerrain.TrueAppearance))
            {
                awareTerrain.LastSeenAppearance.CopyAppearanceFrom(awareTerrain.TrueAppearance);
                awareTerrain.LastSeenAppearance.IsVisible = awareTerrain.TrueAppearance.IsVisible;
            }


            // Set up an event handler that will keep the actual appearance in sync with the
            // true one, so long as the tile remains visible to the player.  Because this could be
            // called twice in certain state changes, we make sure we do not add the handler twice.
            //
            // Removing the event even if it does not exist does not throw exception and appears to be the
            // most performant way to account for duplicates.
            awareTerrain.TrueAppearance.IsDirtySet -= On_VisibleTileTrueAppearanceIsDirtySet;
            awareTerrain.TrueAppearance.IsDirtySet += On_VisibleTileTrueAppearanceIsDirtySet;
        }
 /// <summary>
 /// Makes terrain visible and sets its foreground color to its regular value.
 /// </summary>
 /// <param name="terrain">Terrain to modify.</param>
 protected override void UpdateTerrainSeen(RogueLikeCell terrain)
 {
     terrain.Appearance.IsVisible = true;
     if (terrain.Appearance.Decorators.Contains(ExploredDecorator))
     {
         // If there is only 1 decorator, it must be ours so we can replace
         // the array with a static blank one
         terrain.Appearance.Decorators = terrain.Appearance.Decorators.Length == 1 ? Array.Empty <CellDecorator>() : terrain.Appearance.Decorators.Where(i => i != ExploredDecorator).ToArray();
     }
 }
        /// <summary>
        /// Makes terrain invisible if it is not explored.  Makes terrain visible but tints it using
        /// <see cref="ExploredDecorator"/> if it is explored.
        /// </summary>
        /// <param name="terrain">Terrain to modify.</param>
        protected override void UpdateTerrainUnseen(RogueLikeCell terrain)
        {
            // Parent can't be null because of invariants enforced by structure for when
            // this function is called
            var parent = Parent !;

            // If the unseen terrain is outside of FOV, apply the decorator to tint the square appropriately.
            if (parent.PlayerExplored[terrain.Position])
            {
                terrain.Appearance.Decorators = terrain.Appearance.Decorators.Append(ExploredDecorator).ToArray();
            }
            else // If the unseen tile isn't explored, it's invisible
            {
                terrain.Appearance.IsVisible = false;
            }
        }
        /// <summary>
        /// Makes terrain invisible if it is not explored.  Changes the appearance by calling
        /// <see cref="ApplyMemoryAppearance"/> if it is explored.  The terrain will continue to
        /// appear as set by that function (even if its true appearance changes) until the player
        /// sees it again.
        /// </summary>
        /// <param name="terrain">Terrain to modify.</param>
        protected override void UpdateTerrainUnseen(RogueLikeCell terrain)
        {
            // Invalid use of MemoryFieldOfViewHandlerBase
            if (!(terrain is MemoryAwareRogueLikeCell awareTerrain))
            {
                throw new InvalidOperationException(
                          $"{nameof(MemoryFieldOfViewHandlerBase)} must only be used on a  map that contains {nameof(MemoryAwareRogueLikeCell)} instances as its terrain.");
            }

            // If the event handler for synchronizing the appearance with true appearance is added, remove it
            // which will cause the appearance to remain as last-seen if it changes
            awareTerrain.TrueAppearance.IsDirtySet -= On_VisibleTileTrueAppearanceIsDirtySet;

            // If the unseen terrain is explored (eg. it was in FOV previously), apply the visual change
            // as appropriate
            if (Parent !.PlayerExplored[terrain.Position])
            {
                ApplyMemoryAppearance(awareTerrain);
            }