using UnityEngine; using System.Collections.Generic; /// /// Manages fog of war for the maze - only shows explored areas /// AI entities can only see areas they've visited or are within their vision range /// public class MazeFogOfWar : MonoBehaviour { [Header("Fog Settings")] [SerializeField] private MazeController mazeController; [Header("Rendering")] [SerializeField] private ChunkedMazeRenderer chunkedRenderer; [SerializeField] private MeshMazeRenderer meshRenderer; [SerializeField] private Color fogColor = new Color(0.1f, 0.1f, 0.1f, 0.8f); [SerializeField] private Material fogMaterial; private HashSet exploredTiles = new(); private Dictionary> entityVisionRanges = new(); void Start() { if (fogMaterial == null) { CreateFogMaterial(); } if (meshRenderer == null) { meshRenderer = GetComponent() ?? FindFirstObjectByType(); } } /// /// Marks a tile as explored /// public void ExploreTile(Vector2Int tilePos) { exploredTiles.Add(tilePos); UpdateRendering(); } /// /// Marks multiple tiles as explored /// public void ExploreTiles(IEnumerable tiles) { foreach (var tile in tiles) { exploredTiles.Add(tile); } UpdateRendering(); } /// /// Updates vision range for an entity /// public void UpdateEntityVision(GameObject entity, Vector2Int position, float range) { HashSet visibleTiles = GetVisibleTiles(position, range); if (!entityVisionRanges.ContainsKey(entity)) { entityVisionRanges[entity] = new HashSet(); } // Remove old vision foreach (var tile in entityVisionRanges[entity]) { if (!exploredTiles.Contains(tile) && !IsTileVisibleByOtherEntities(tile)) { // Tile should be hidden } } // Add new vision entityVisionRanges[entity] = visibleTiles; foreach (var tile in visibleTiles) { exploredTiles.Add(tile); } UpdateRendering(); } /// /// Gets all tiles visible from a position within range /// private HashSet GetVisibleTiles(Vector2Int center, float range) { HashSet visible = new(); var maze = mazeController.GetCurrentMaze(); if (maze == null) return visible; int rangeInt = Mathf.CeilToInt(range); for (int x = center.x - rangeInt; x <= center.x + rangeInt; x++) { for (int y = center.y - rangeInt; y <= center.y + rangeInt; y++) { Vector2Int tilePos = new Vector2Int(x, y); if (Vector2Int.Distance(center, tilePos) <= range) { if (maze.IsInBounds(x, y) && HasLineOfSight(center, tilePos)) { visible.Add(tilePos); } } } } return visible; } /// /// Checks if there's line of sight between two points /// private bool HasLineOfSight(Vector2Int from, Vector2Int to) { var maze = mazeController.GetCurrentMaze(); if (maze == null) return false; // Simple line of sight check - can be enhanced with proper raycasting Vector2Int delta = to - from; int steps = Mathf.Max(Mathf.Abs(delta.x), Mathf.Abs(delta.y)); for (int i = 1; i < steps; i++) { float t = (float)i / steps; Vector2Int checkPos = new Vector2Int( Mathf.RoundToInt(from.x + delta.x * t), Mathf.RoundToInt(from.y + delta.y * t) ); if (!maze.IsWalkable(checkPos.x, checkPos.y)) { return false; } } return true; } /// /// Checks if a tile is visible by any other entity /// private bool IsTileVisibleByOtherEntities(Vector2Int tile) { foreach (var vision in entityVisionRanges.Values) { if (vision.Contains(tile)) { return true; } } return false; } /// /// Updates the rendering to show only explored areas /// private void UpdateRendering() { if (chunkedRenderer != null) { // The chunked renderer will handle showing/hiding tiles based on exploration chunkedRenderer.RefreshVisibleChunks(); } if (meshRenderer != null) { meshRenderer.RefreshFogOfWar(); } } /// /// Creates a default fog material /// private void CreateFogMaterial() { fogMaterial = new Material(Shader.Find("Sprites/Default")); fogMaterial.color = fogColor; } /// /// Checks if a tile is explored /// public bool IsTileExplored(Vector2Int tile) { return exploredTiles.Contains(tile); } /// /// Gets all explored tiles /// public HashSet GetExploredTiles() { return new HashSet(exploredTiles); } /// /// Removes an entity from vision tracking /// public void RemoveEntity(GameObject entity) { entityVisionRanges.Remove(entity); UpdateRendering(); } }