using System; using System.Threading.Tasks; using AceFieldNewHorizon.Scripts.System; using Godot; using Vector2 = Godot.Vector2; namespace AceFieldNewHorizon.Scripts.Tiles; public partial class BaseTile : Node2D { [Export] public string TileId { get; set; } private CollisionShape2D _collisionShape; private Sprite2D _sprite; private ColorRect _progressOverlay; private Action _onConstructionComplete; private bool _isConstructing = false; public override void _Ready() { _collisionShape = GetNodeOrNull("CollisionShape2D"); _sprite = GetNodeOrNull("Sprite2D"); _progressOverlay = GetNodeOrNull("ProgressOverlay"); if (_progressOverlay != null) _progressOverlay.Visible = false; } public void SetGhostMode(bool canPlace) { // Don't modify collision for constructing buildings if (_isConstructing) return; if (_collisionShape != null) _collisionShape.Disabled = true; if (_sprite != null) _sprite.Modulate = canPlace ? new Color(0, 1, 0, 0.5f) : new Color(1, 0, 0, 0.5f); } public void FinalizePlacement() { if (_collisionShape != null) _collisionShape.Disabled = false; if (_sprite != null) _sprite.Modulate = Colors.White; } // Building progress visualization public void StartConstruction(float buildTime, Action onComplete = null) { _isConstructing = true; if (_collisionShape != null) _collisionShape.Disabled = true; if (_progressOverlay == null || _sprite?.Texture == null) { _isConstructing = false; onComplete?.Invoke(); return; } _onConstructionComplete = onComplete; var texSize = new Vector2(GridUtils.TileSize, GridUtils.TileSize); // Set initial transparency for construction if (_sprite != null) _sprite.Modulate = new Color(1, 1, 1, 0.8f); _progressOverlay.Visible = true; _progressOverlay.Modulate = Colors.White; _progressOverlay.Color = new Color(0, 0, 1, 0.4f); // semi-transparent blue async void RunProgress() { var elapsed = 0f; while (elapsed < buildTime) { await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); elapsed += (float)GetProcessDeltaTime(); var ratio = Mathf.Clamp(elapsed / buildTime, 0, 1); // Fill from bottom to top, aligned with sprite center pivot _progressOverlay.Size = new Vector2(texSize.X, texSize.Y * ratio); _progressOverlay.Position = new Vector2( -texSize.X / 2, // align left edge texSize.Y / 2 - _progressOverlay.Size.Y // move upward ); } // Fade out the overlay await FadeOutOverlay(0.5f); // Construction complete - restore full opacity and enable collision if (_sprite != null) _sprite.Modulate = Colors.White; _isConstructing = false; if (_collisionShape != null) _collisionShape.Disabled = false; _onConstructionComplete?.Invoke(); } RunProgress(); } private async Task FadeOutOverlay(float duration) { var elapsed = 0f; var startAlpha = _progressOverlay.Modulate.A; while (elapsed < duration) { await ToSignal(GetTree(), SceneTree.SignalName.ProcessFrame); elapsed += (float)GetProcessDeltaTime(); var alpha = Mathf.Lerp(startAlpha, 0f, elapsed / duration); _progressOverlay.Modulate = new Color(1, 1, 1, alpha); } _progressOverlay.Visible = false; _progressOverlay.Modulate = Colors.White; // Reset alpha for next use } }