✨ Complete resource manager
This commit is contained in:
@@ -9,11 +9,12 @@ namespace AceFieldNewHorizon.Scripts.System;
|
||||
public partial class PlacementManager : Node2D
|
||||
{
|
||||
[Export] public GridManager Grid { get; set; }
|
||||
[Export] public ResourceManager Inventory { get; set; }
|
||||
[Export] public BuildingRegistry Registry { get; set; }
|
||||
[Export] public int MaxConcurrentBuilds { get; set; } = 6; // Make it adjustable in editor
|
||||
|
||||
private static readonly List<string> BuildableTiles = ["wall", "miner"];
|
||||
private readonly List<BuildTask> _activeBuilds = new();
|
||||
private readonly Dictionary<Node2D, BuildTask> _buildTasks = new();
|
||||
private AudioStreamPlayer _completionSound;
|
||||
|
||||
public override void _Ready()
|
||||
@@ -33,10 +34,16 @@ public partial class PlacementManager : Node2D
|
||||
private void OnBuildCompleted()
|
||||
{
|
||||
// Remove all completed builds
|
||||
_activeBuilds.RemoveAll(task => task.IsCompleted);
|
||||
var completed = _buildTasks.Where(kvp => kvp.Value.IsCompleted)
|
||||
.Select(kvp => kvp.Key)
|
||||
.ToList();
|
||||
foreach (var key in completed)
|
||||
{
|
||||
_buildTasks.Remove(key);
|
||||
}
|
||||
|
||||
// If no builds left, play the completion sound
|
||||
if (_activeBuilds.Count == 0)
|
||||
if (_buildTasks.Count == 0)
|
||||
{
|
||||
_completionSound.Play();
|
||||
}
|
||||
@@ -46,30 +53,34 @@ public partial class PlacementManager : Node2D
|
||||
private bool CanStartNewBuild()
|
||||
{
|
||||
// Remove completed builds
|
||||
_activeBuilds.RemoveAll(task => task.IsCompleted);
|
||||
return _activeBuilds.Count < MaxConcurrentBuilds;
|
||||
var completed = _buildTasks.Where(kvp => kvp.Value.IsCompleted)
|
||||
.Select(kvp => kvp.Key)
|
||||
.ToList();
|
||||
foreach (var key in completed)
|
||||
{
|
||||
_buildTasks.Remove(key);
|
||||
}
|
||||
|
||||
return _buildTasks.Count < MaxConcurrentBuilds;
|
||||
}
|
||||
|
||||
private class BuildTask : IDisposable
|
||||
private class BuildTask(Action onCompleted) : IDisposable
|
||||
{
|
||||
private readonly Action _onCompleted;
|
||||
public bool IsCompleted { get; private set; }
|
||||
public bool WasCancelled { get; private set; }
|
||||
|
||||
public BuildTask(Action onCompleted)
|
||||
{
|
||||
_onCompleted = onCompleted;
|
||||
}
|
||||
|
||||
public void Complete()
|
||||
public void Complete(bool wasCancelled = false)
|
||||
{
|
||||
if (IsCompleted) return;
|
||||
|
||||
IsCompleted = true;
|
||||
_onCompleted?.Invoke();
|
||||
WasCancelled = wasCancelled;
|
||||
onCompleted?.Invoke();
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
Complete();
|
||||
Complete(true); // Mark as cancelled if disposed before completion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,6 +199,13 @@ public partial class PlacementManager : Node2D
|
||||
return;
|
||||
}
|
||||
|
||||
// Consume resources first
|
||||
if (!ConsumeBuildingResources(_currentBuildingId))
|
||||
{
|
||||
// Optionally show feedback to player that they can't afford this building
|
||||
return;
|
||||
}
|
||||
|
||||
var scene = building.Scene;
|
||||
var buildingInstance = (BaseTile)scene.Instantiate();
|
||||
buildingInstance.RotationDegrees = _currentRotation;
|
||||
@@ -195,13 +213,36 @@ public partial class PlacementManager : Node2D
|
||||
buildingInstance.Position = _ghostBuilding.Position;
|
||||
AddChild(buildingInstance);
|
||||
|
||||
// Check if area is free before placing
|
||||
if (!IsAreaFree(_hoveredCell, building.Size, _currentRotation, building.Layer))
|
||||
{
|
||||
RefundBuildingResources(_currentBuildingId);
|
||||
buildingInstance.QueueFree();
|
||||
return;
|
||||
}
|
||||
|
||||
// Occupy the area
|
||||
Grid.OccupyArea(_hoveredCell, buildingInstance, building.Size, _currentRotation, building.Layer);
|
||||
|
||||
if (building.BuildTime > 0f)
|
||||
{
|
||||
var buildTask = new BuildTask(OnBuildCompleted);
|
||||
_activeBuilds.Add(buildTask);
|
||||
buildingInstance.StartConstruction(building.BuildTime, buildTask.Complete);
|
||||
_buildTasks[buildingInstance] = buildTask;
|
||||
|
||||
buildingInstance.StartConstruction(building.BuildTime, () => {
|
||||
// On construction complete
|
||||
if (_buildTasks.TryGetValue(buildingInstance, out var task))
|
||||
{
|
||||
if (task.WasCancelled)
|
||||
{
|
||||
RefundBuildingResources(_currentBuildingId);
|
||||
Grid.FreeArea(_hoveredCell, building.Size, _currentRotation, building.Layer);
|
||||
buildingInstance.QueueFree();
|
||||
}
|
||||
task.Complete();
|
||||
_buildTasks.Remove(buildingInstance);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,14 +267,64 @@ public partial class PlacementManager : Node2D
|
||||
}
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
base._ExitTree();
|
||||
_buildTasks.Clear();
|
||||
}
|
||||
|
||||
private bool CanAffordBuilding(string buildingId)
|
||||
{
|
||||
if (Inventory == null) return false;
|
||||
|
||||
var buildingData = Registry.GetBuilding(buildingId);
|
||||
if (buildingData == null) return false;
|
||||
|
||||
// Check if we have enough of each required resource
|
||||
foreach (var (resourceId, amount) in buildingData.Cost)
|
||||
if (!Inventory.HasResource(resourceId, amount))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ConsumeBuildingResources(string buildingId)
|
||||
{
|
||||
if (Inventory == null) return false;
|
||||
|
||||
var buildingData = Registry.GetBuilding(buildingId);
|
||||
if (buildingData == null) return false;
|
||||
|
||||
// First verify we can afford it
|
||||
if (!CanAffordBuilding(buildingId)) return false;
|
||||
|
||||
// Then consume each resource
|
||||
foreach (var (resourceId, amount) in buildingData.Cost)
|
||||
Inventory.RemoveResource(resourceId, amount);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RefundBuildingResources(string buildingId)
|
||||
{
|
||||
if (Inventory == null) return;
|
||||
|
||||
var buildingData = Registry.GetBuilding(buildingId);
|
||||
if (buildingData == null) return;
|
||||
|
||||
// Refund each resource
|
||||
foreach (var (resourceId, amount) in buildingData.Cost)
|
||||
{
|
||||
Inventory.AddResource(resourceId, amount);
|
||||
}
|
||||
}
|
||||
|
||||
private bool CanPlaceBuilding()
|
||||
{
|
||||
var buildingData = Registry.GetBuilding(_currentBuildingId);
|
||||
if (buildingData == null) return false;
|
||||
|
||||
// Check if rotation is allowed
|
||||
if (!buildingData.IsRotationAllowed(_currentRotation))
|
||||
return false;
|
||||
// Check if we can afford the building
|
||||
if (!CanAffordBuilding(_currentBuildingId)) return false;
|
||||
|
||||
// Check if area is free
|
||||
var rotatedSize = buildingData.GetRotatedSize(_currentRotation);
|
||||
@@ -250,6 +341,11 @@ public partial class PlacementManager : Node2D
|
||||
_ => []
|
||||
};
|
||||
}
|
||||
|
||||
private bool IsAreaFree(Vector2I topLeft, Vector2I size, float rotation, GridLayer layer)
|
||||
{
|
||||
return !Grid.IsAreaOccupied(topLeft, size, rotation, GetBlockingLayers(layer));
|
||||
}
|
||||
}
|
||||
|
||||
public static class GridManagerExtensions
|
||||
|
Reference in New Issue
Block a user