using System; using AceFieldNewHorizon.Scripts.Tiles; using Godot; namespace AceFieldNewHorizon.Scripts.System; public partial class PlacementManager : Node2D { [Export] public GridManager Grid { get; set; } [Export] public BuildingRegistry Registry { get; set; } private string _currentBuildingId = "wall"; private GridLayer _currentLayer = GridLayer.Building; // Default layer private Vector2I _hoveredCell; private BaseTile _ghostBuilding; private float _currentRotation = 0f; public void SetCurrentBuilding(string buildingId, GridLayer layer = GridLayer.Building) { _currentBuildingId = buildingId; _currentLayer = layer; // Replace ghost immediately if (_ghostBuilding == null) return; _ghostBuilding.QueueFree(); _ghostBuilding = null; } public void SetCurrentLayer(GridLayer layer) { _currentLayer = layer; // Update ghost building if needed if (_ghostBuilding != null) { _ghostBuilding.QueueFree(); _ghostBuilding = null; } } private void RotateGhost(bool reverse = false) { if (_ghostBuilding == null) return; if (reverse) _currentRotation = (_currentRotation - 90f) % 360f; else _currentRotation = (_currentRotation + 90f) % 360f; _ghostBuilding.RotationDegrees = _currentRotation; } public override void _Process(double delta) { // Snap mouse to grid var mousePos = GetGlobalMousePosition(); _hoveredCell = GridUtils.WorldToGrid(mousePos); if (Input.IsActionJustPressed("rotate_tile_reverse")) RotateGhost(reverse: true); else if (Input.IsActionJustPressed("rotate_tile")) RotateGhost(); if (_ghostBuilding == null) { var scene = Registry.GetBuilding(_currentBuildingId)?.Scene; if (scene == null) return; _ghostBuilding = (BaseTile)scene.Instantiate(); _ghostBuilding.SetGhostMode(true); _ghostBuilding.RotationDegrees = _currentRotation; _ghostBuilding.ZAsRelative = false; _ghostBuilding.ZIndex = (int)_currentLayer; // Use layer as Z-index AddChild(_ghostBuilding); } var placementPos = GridUtils.GridToWorld(_hoveredCell); var spaceState = GetWorld2D().DirectSpaceState; var centerPos = placementPos + new Vector2(GridUtils.TileSize / 2f, GridUtils.TileSize / 2f); var query = new PhysicsPointQueryParameters2D { Position = centerPos, CollideWithBodies = true, CollideWithAreas = true, CollisionMask = uint.MaxValue }; var collision = spaceState.IntersectPoint(query, 8); // 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.SetGhostMode(canPlace); // Left click to place if (Input.IsActionPressed("build_tile") && canPlace) { var scene = Registry.GetBuilding(_currentBuildingId)?.Scene; if (scene == null) return; _ghostBuilding.FinalizePlacement(); _ghostBuilding.RotationDegrees = _currentRotation; _ghostBuilding.ZIndex = (int)_currentLayer; var buildingData = Registry.GetBuilding(_currentBuildingId); Grid.OccupyCell(_hoveredCell, _ghostBuilding, _currentLayer); if (buildingData is { BuildTime: > 0f }) _ghostBuilding.StartConstruction(buildingData.BuildTime); // Create new ghost for next placement _ghostBuilding = (BaseTile)scene.Instantiate(); _ghostBuilding.SetGhostMode(true); _ghostBuilding.RotationDegrees = _currentRotation; _ghostBuilding.ZAsRelative = false; _ghostBuilding.ZIndex = (int)_currentLayer; AddChild(_ghostBuilding); } if (Input.IsActionPressed("destroy_tile") && !Grid.IsCellFree(_hoveredCell, _currentLayer)) { // Right click to destroy from current layer var building = Grid.GetBuildingAtCell(_hoveredCell, _currentLayer); if (building == null) return; building.QueueFree(); 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 }, _ => [] }; } }