#nullable enable using System; using System.Collections.Generic; using System.Linq; using Godot; namespace AceFieldNewHorizon.Scripts.System; public enum GridLayer { Ground, // Base layer (e.g., terrain, floors) Building, // Main building layer Decoration // Additional layer for decorations, effects, etc. } public partial class GridManager : Node { private Dictionary> _layers = new(); public GridManager() { // Initialize all layers foreach (GridLayer layer in Enum.GetValues(typeof(GridLayer))) _layers[layer] = new Dictionary(); } public bool IsAreaFree(Vector2I topLeft, Vector2I size, float rotation, GridLayer layer = GridLayer.Building) { var occupiedCells = GridUtils.GetOccupiedCells(topLeft, size, rotation); return occupiedCells.All(cell => !_layers[layer].ContainsKey(cell)); } public void OccupyArea(Vector2I topLeft, Node2D building, Vector2I size, float rotation, GridLayer layer = GridLayer.Building) { var occupiedCells = GridUtils.GetOccupiedCells(topLeft, size, rotation); foreach (var cell in occupiedCells) { _layers[layer][cell] = (building, size, rotation); } } public void FreeArea(Vector2I topLeft, Vector2I size, float rotation, GridLayer layer = GridLayer.Building) { var occupiedCells = GridUtils.GetOccupiedCells(topLeft, size, rotation); foreach (var cell in occupiedCells) { if (_layers[layer].ContainsKey(cell)) _layers[layer].Remove(cell); } } public Node2D? GetBuildingAtCell(Vector2I cell, GridLayer layer = GridLayer.Building) { return _layers[layer].TryGetValue(cell, out var data) ? data.Building : null; } public bool IsAnyCellOccupied(Vector2I cell, params GridLayer[] layers) { if (layers.Length == 0) { // Check all layers if none specified foreach (var layer in _layers.Values) { if (layer.ContainsKey(cell)) return true; } return false; } foreach (var layer in layers) { if (_layers[layer].ContainsKey(cell)) return true; } return false; } public bool IsAreaOccupied(Vector2I topLeft, Vector2I size, float rotation, params GridLayer[] layers) { var occupiedCells = GridUtils.GetOccupiedCells(topLeft, size, rotation); if (layers.Length == 0) { // Check all layers if none specified foreach (var cell in occupiedCells) { foreach (var layer in _layers.Values) if (layer.ContainsKey(cell)) return true; } return false; } foreach (var cell in occupiedCells) { foreach (var layer in layers) if (_layers[layer].ContainsKey(cell)) return true; } return false; } } public static class GridUtils { public const int TileSize = 54; public static Vector2I WorldToGrid(Vector2 pos) { return new Vector2I( Mathf.FloorToInt(pos.X / TileSize), Mathf.FloorToInt(pos.Y / TileSize) ); } public static Vector2 GridToWorld(Vector2I cell) { return new Vector2( cell.X * TileSize, cell.Y * TileSize ); } public static Vector2 GetCenterOffset(Vector2I size, float rotation) { var rotatedSize = (Mathf.RoundToInt(rotation / 90f) % 2) == 0 ? size : new Vector2I(size.Y, size.X); return new Vector2(rotatedSize.X * TileSize / 2f, rotatedSize.Y * TileSize / 2f); } public static List GetOccupiedCells(Vector2I topLeft, Vector2I size, float rotation) { var occupiedCells = new List(); var rotatedSize = (Mathf.RoundToInt(rotation / 90f) % 2) == 0 ? size : new Vector2I(size.Y, size.X); for (int x = 0; x < rotatedSize.X; x++) { for (int y = 0; y < rotatedSize.Y; y++) { occupiedCells.Add(new Vector2I(topLeft.X + x, topLeft.Y + y)); } } return occupiedCells; } }