✨ Grid layer system
This commit is contained in:
		| @@ -1,31 +1,68 @@ | |||||||
| #nullable enable | #nullable enable | ||||||
|  | using System; | ||||||
| using System.Collections.Generic; | using System.Collections.Generic; | ||||||
| using Godot; | using Godot; | ||||||
|  |  | ||||||
| namespace AceFieldNewHorizon.Scripts.System; | 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 | public partial class GridManager : Node | ||||||
| { | { | ||||||
|     private Dictionary<Vector2I, Node2D> _grid = new(); |     private Dictionary<GridLayer, Dictionary<Vector2I, Node2D>> _layers = new(); | ||||||
|  |  | ||||||
|     public bool IsCellFree(Vector2I cell) |     public GridManager() | ||||||
|     { |     { | ||||||
|         return !_grid.ContainsKey(cell); |         // Initialize all layers | ||||||
|  |         foreach (GridLayer layer in Enum.GetValues(typeof(GridLayer))) | ||||||
|  |         { | ||||||
|  |             _layers[layer] = new Dictionary<Vector2I, Node2D>(); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void OccupyCell(Vector2I cell, Node2D building) |     public bool IsCellFree(Vector2I cell, GridLayer layer = GridLayer.Building) | ||||||
|     { |     { | ||||||
|         _grid[cell] = building; |         return !_layers[layer].ContainsKey(cell); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public void FreeCell(Vector2I cell) |     public void OccupyCell(Vector2I cell, Node2D building, GridLayer layer = GridLayer.Building) | ||||||
|     { |     { | ||||||
|         _grid.Remove(cell); |         _layers[layer][cell] = building; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     public Node2D? GetBuildingAtCell(Vector2I cell) |     public void FreeCell(Vector2I cell, GridLayer layer = GridLayer.Building) | ||||||
|     { |     { | ||||||
|         return _grid.GetValueOrDefault(cell); |         if (_layers[layer].ContainsKey(cell)) | ||||||
|  |             _layers[layer].Remove(cell); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     public Node2D? GetBuildingAtCell(Vector2I cell, GridLayer layer = GridLayer.Building) | ||||||
|  |     { | ||||||
|  |         return _layers[layer].GetValueOrDefault(cell); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     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; | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -1,3 +1,4 @@ | |||||||
|  | using System; | ||||||
| using AceFieldNewHorizon.Scripts.Tiles; | using AceFieldNewHorizon.Scripts.Tiles; | ||||||
| using Godot; | using Godot; | ||||||
|  |  | ||||||
| @@ -8,14 +9,16 @@ public partial class PlacementManager : Node2D | |||||||
|     [Export] public GridManager Grid { get; set; } |     [Export] public GridManager Grid { get; set; } | ||||||
|     [Export] public BuildingRegistry Registry { get; set; } |     [Export] public BuildingRegistry Registry { get; set; } | ||||||
|  |  | ||||||
|     private string _currentBuildingId = "miner"; |     private string _currentBuildingId = "wall"; | ||||||
|  |     private GridLayer _currentLayer = GridLayer.Building; // Default layer | ||||||
|     private Vector2I _hoveredCell; |     private Vector2I _hoveredCell; | ||||||
|     private BaseTile _ghostBuilding; |     private BaseTile _ghostBuilding; | ||||||
|  |     private float _currentRotation = 0f; | ||||||
|  |  | ||||||
|     public void SetCurrentBuilding(string buildingId) |     public void SetCurrentBuilding(string buildingId, GridLayer layer = GridLayer.Building) | ||||||
|     { |     { | ||||||
|         _currentBuildingId = buildingId; |         _currentBuildingId = buildingId; | ||||||
|  |         _currentLayer = layer; | ||||||
|  |  | ||||||
|         // Replace ghost immediately |         // Replace ghost immediately | ||||||
|         if (_ghostBuilding == null) return; |         if (_ghostBuilding == null) return; | ||||||
| @@ -23,10 +26,17 @@ public partial class PlacementManager : Node2D | |||||||
|         _ghostBuilding = null; |         _ghostBuilding = null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     // Add this field to PlacementManager |     public void SetCurrentLayer(GridLayer layer) | ||||||
|     private float _currentRotation = 0f; |     { | ||||||
|  |         _currentLayer = layer; | ||||||
|  |         // Update ghost building if needed | ||||||
|  |         if (_ghostBuilding != null) | ||||||
|  |         { | ||||||
|  |             _ghostBuilding.QueueFree(); | ||||||
|  |             _ghostBuilding = null; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|     // Add this method to handle rotation |  | ||||||
|     private void RotateGhost(bool reverse = false) |     private void RotateGhost(bool reverse = false) | ||||||
|     { |     { | ||||||
|         if (_ghostBuilding == null) return; |         if (_ghostBuilding == null) return; | ||||||
| @@ -56,6 +66,8 @@ public partial class PlacementManager : Node2D | |||||||
|             _ghostBuilding = (BaseTile)scene.Instantiate(); |             _ghostBuilding = (BaseTile)scene.Instantiate(); | ||||||
|             _ghostBuilding.SetGhostMode(true); |             _ghostBuilding.SetGhostMode(true); | ||||||
|             _ghostBuilding.RotationDegrees = _currentRotation; |             _ghostBuilding.RotationDegrees = _currentRotation; | ||||||
|  |             _ghostBuilding.ZAsRelative = false; | ||||||
|  |             _ghostBuilding.ZIndex = (int)_currentLayer; // Use layer as Z-index | ||||||
|             AddChild(_ghostBuilding); |             AddChild(_ghostBuilding); | ||||||
|         } |         } | ||||||
|  |  | ||||||
| @@ -68,17 +80,18 @@ public partial class PlacementManager : Node2D | |||||||
|             Position = centerPos, |             Position = centerPos, | ||||||
|             CollideWithBodies = true, |             CollideWithBodies = true, | ||||||
|             CollideWithAreas = true, |             CollideWithAreas = true, | ||||||
|             CollisionMask = uint.MaxValue // check against all layers/masks |             CollisionMask = uint.MaxValue | ||||||
|             // Exclude = new Godot.Collections.Array<Rid>() // optional, see below |  | ||||||
|         }; |         }; | ||||||
|  |  | ||||||
|         var collision = spaceState.IntersectPoint(query, 8); |         var collision = spaceState.IntersectPoint(query, 8); | ||||||
|         var canPlace = Grid.IsCellFree(_hoveredCell) && collision.Count == 0; |          | ||||||
|  |         // Check if the current cell is free in the current layer | ||||||
|  |         var canPlace = Grid.IsCellFree(_hoveredCell, _currentLayer) &&  | ||||||
|  |                       !Grid.IsAnyCellOccupied(_hoveredCell, GetBlockingLayers()); | ||||||
|  |  | ||||||
|         _ghostBuilding.Position = placementPos; |         _ghostBuilding.Position = placementPos; | ||||||
|         _ghostBuilding.SetGhostMode(canPlace); |         _ghostBuilding.SetGhostMode(canPlace); | ||||||
|  |  | ||||||
|  |  | ||||||
|         // Left click to place |         // Left click to place | ||||||
|         if (Input.IsActionPressed("build_tile") && canPlace) |         if (Input.IsActionPressed("build_tile") && canPlace) | ||||||
|         { |         { | ||||||
| @@ -87,26 +100,42 @@ public partial class PlacementManager : Node2D | |||||||
|  |  | ||||||
|             _ghostBuilding.FinalizePlacement(); |             _ghostBuilding.FinalizePlacement(); | ||||||
|             _ghostBuilding.RotationDegrees = _currentRotation; |             _ghostBuilding.RotationDegrees = _currentRotation; | ||||||
|  |             _ghostBuilding.ZIndex = (int)_currentLayer; | ||||||
|  |  | ||||||
|             var buildingData = Registry.GetBuilding(_currentBuildingId); |             var buildingData = Registry.GetBuilding(_currentBuildingId); | ||||||
|             Grid.OccupyCell(_hoveredCell, _ghostBuilding); |             Grid.OccupyCell(_hoveredCell, _ghostBuilding, _currentLayer); | ||||||
|  |  | ||||||
|             if (buildingData is { BuildTime: > 0f }) |             if (buildingData is { BuildTime: > 0f }) | ||||||
|                 _ghostBuilding.StartConstruction(buildingData.BuildTime); |                 _ghostBuilding.StartConstruction(buildingData.BuildTime); | ||||||
|  |  | ||||||
|  |             // Create new ghost for next placement | ||||||
|             _ghostBuilding = (BaseTile)scene.Instantiate(); |             _ghostBuilding = (BaseTile)scene.Instantiate(); | ||||||
|             _ghostBuilding.SetGhostMode(true); |             _ghostBuilding.SetGhostMode(true); | ||||||
|             _ghostBuilding.RotationDegrees = _currentRotation; |             _ghostBuilding.RotationDegrees = _currentRotation; | ||||||
|  |             _ghostBuilding.ZAsRelative = false; | ||||||
|  |             _ghostBuilding.ZIndex = (int)_currentLayer; | ||||||
|             AddChild(_ghostBuilding); |             AddChild(_ghostBuilding); | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         if (Input.IsActionPressed("destroy_tile") && !Grid.IsCellFree(_hoveredCell)) |         if (Input.IsActionPressed("destroy_tile") && !Grid.IsCellFree(_hoveredCell, _currentLayer)) | ||||||
|         { |         { | ||||||
|             // Right click to destroy |             // Right click to destroy from current layer | ||||||
|             var building = Grid.GetBuildingAtCell(_hoveredCell); |             var building = Grid.GetBuildingAtCell(_hoveredCell, _currentLayer); | ||||||
|             if (building == null) return; |             if (building == null) return; | ||||||
|             building.QueueFree(); |             building.QueueFree(); | ||||||
|             Grid.FreeCell(_hoveredCell); |             Grid.FreeCell(_hoveredCell, _currentLayer); | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     // Define which layers should block placement | ||||||
|  |     private GridLayer[] GetBlockingLayers() | ||||||
|  |     { | ||||||
|  |         return _currentLayer switch | ||||||
|  |         { | ||||||
|  |             GridLayer.Ground => new[] { GridLayer.Ground }, | ||||||
|  |             GridLayer.Building => new[] { GridLayer.Ground, GridLayer.Building }, | ||||||
|  |             GridLayer.Decoration => new[] { GridLayer.Ground, GridLayer.Building, GridLayer.Decoration }, | ||||||
|  |             _ => [] | ||||||
|  |         }; | ||||||
|  |     } | ||||||
| } | } | ||||||
		Reference in New Issue
	
	Block a user