Files
2026-05-21 16:04:49 +02:00

171 lines
6.4 KiB
C#

using System;
using System.Collections.Generic;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.Pool;
namespace DanieleMarotta.RiversongCodeShowcase
{
public class ConstructionSiteManager : GameSystem, IInitializable, IDisposable, IUpdatable
{
private const int SpatialLookupCellSize = 5;
private const int StorageCapacity = 100;
[InjectService]
private GameConfig _config;
[InjectService]
private IScene _scene;
[InjectService]
private ISignalBus _signalBus;
[InjectService]
private IEntityCollection _entityCollection;
[InjectService]
private ITileSpace _tileSpace;
[InjectService]
private IProductStorageManager _productStorageManager;
[InjectService]
private IProductCatalog _productCatalog;
[InjectService]
private IProductStackFactory _productStackFactory;
[InjectService]
private IAgentFactory _agentFactory;
private Dictionary<int, GameObject> _visualizations = new();
private SpatialLookup<(ConstructionSite, GameObject)> _spatialLookup = new(SpatialLookupCellSize);
private MaterialPropertyBlock _materialPropertyBlock;
public ConstructionSiteManager(IServiceLocator serviceLocator) : base(serviceLocator)
{
}
public UniTask InitializeAsync()
{
_signalBus.Subscribe<BuildingPlacementAnimationCompletedSignal>(OnBuildingPlaced);
_signalBus.Subscribe<CollectDeletedGameObjectsSignal>(OnCollectDeletedGameObjects);
_signalBus.Subscribe<DoDeleteToolSignal>(OnDoDeleteTool);
return UniTask.CompletedTask;
}
public void Dispose()
{
_signalBus.Unsubscribe<BuildingPlacementAnimationCompletedSignal>(OnBuildingPlaced);
_signalBus.Unsubscribe<CollectDeletedGameObjectsSignal>(OnCollectDeletedGameObjects);
_signalBus.Unsubscribe<DoDeleteToolSignal>(OnDoDeleteTool);
}
private void OnBuildingPlaced(BuildingPlacementAnimationCompletedSignal signal)
{
var constructionSite = _entityCollection.Create<ConstructionSite>();
constructionSite.Building = signal.Building;
constructionSite.Rect = signal.Rect;
constructionSite.Orientation = signal.Orientation;
_productStorageManager.InitializeProductStorage(constructionSite, StorageCapacity);
_agentFactory.InitializeAgentSource(constructionSite);
_entityCollection.Add(constructionSite);
_ = CreateVisualizationAsync(constructionSite);
}
private async UniTask CreateVisualizationAsync(ConstructionSite constructionSite)
{
var position = _tileSpace.GetRectWorldCenter(constructionSite.Rect);
var rotation = constructionSite.Orientation.ToQuaternion();
var folder = _scene.SceneFolders.Buildings;
var visualization = await constructionSite.Building.Visualization.InstantiateAsync(position, rotation, folder);
ReplaceMaterials(visualization);
_visualizations.Add(constructionSite.Id, visualization);
_spatialLookup.Add((constructionSite, visualization), constructionSite.Rect, constructionSite.Id);
}
private void ReplaceMaterials(GameObject gameObject)
{
var material = _config.Buildings.ConstructionSiteMaterial;
using var renderersScope = ListPool<Renderer>.Get(out var renderers);
gameObject.GetComponentsInChildren(renderers);
_materialPropertyBlock ??= new MaterialPropertyBlock();
foreach (var renderer in renderers) MaterialReplacementCache.ReplaceRendererMaterials(renderer, material, _materialPropertyBlock);
}
public void Update()
{
using var toCompleteScope = ListPool<ConstructionSite>.Get(out var toComplete);
foreach (ConstructionSite constructionSite in _entityCollection.GetInternalEntityList(typeof(ConstructionSite)))
if (_visualizations.ContainsKey(constructionSite.Id) && AllBuildingMaterialsFetched(constructionSite))
toComplete.Add(constructionSite);
foreach (var constructionSite in toComplete)
{
_entityCollection.Remove(constructionSite.Id);
_visualizations.Remove(constructionSite.Id, out var visualization);
_spatialLookup.Remove(constructionSite.Rect, constructionSite.Id);
constructionSite.Building.Visualization.ReleaseInstance(visualization);
_signalBus.Raise(new ConstructionCompletedSignal(constructionSite));
}
}
private bool AllBuildingMaterialsFetched(ConstructionSite constructionSite)
{
ref var storage = ref constructionSite.GetStorageRW();
foreach (var (product, amount) in constructionSite.Building.BuildingMaterials)
{
var handle = _productCatalog.GetHandle(product);
if (storage.AvailableNow(handle) < amount) return false;
}
return true;
}
private void OnCollectDeletedGameObjects(CollectDeletedGameObjectsSignal signal)
{
if ((signal.Filter & DeletedGameObjectsFilter.Buildings) == 0) return;
using var lookupResultScope = ListPool<(ConstructionSite, GameObject)>.Get(out var lookupResult);
_spatialLookup.Find(signal.Rect, lookupResult);
foreach (var (_, visualization) in lookupResult) signal.GameObjects.Add(visualization);
}
private void OnDoDeleteTool(DoDeleteToolSignal signal)
{
using var lookupResultScope = ListPool<(ConstructionSite, GameObject)>.Get(out var lookupResult);
_spatialLookup.Find(signal.Rect, lookupResult);
foreach (var (constructionSite, visualization) in lookupResult)
{
_productStackFactory.CreateProductStacksFrom(constructionSite);
_entityCollection.Remove(constructionSite.Id);
_visualizations.Remove(constructionSite.Id);
_spatialLookup.Remove(constructionSite.Rect, constructionSite.Id);
constructionSite.Building.Visualization.ReleaseInstance(visualization);
_signalBus.Raise(new ConstructionSiteDeletedSignal(constructionSite));
}
}
}
}