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 _visualizations = new(); private SpatialLookup<(ConstructionSite, GameObject)> _spatialLookup = new(SpatialLookupCellSize); private MaterialPropertyBlock _materialPropertyBlock; public ConstructionSiteManager(IServiceLocator serviceLocator) : base(serviceLocator) { } public UniTask InitializeAsync() { _signalBus.Subscribe(OnBuildingPlaced); _signalBus.Subscribe(OnCollectDeletedGameObjects); _signalBus.Subscribe(OnDoDeleteTool); return UniTask.CompletedTask; } public void Dispose() { _signalBus.Unsubscribe(OnBuildingPlaced); _signalBus.Unsubscribe(OnCollectDeletedGameObjects); _signalBus.Unsubscribe(OnDoDeleteTool); } private void OnBuildingPlaced(BuildingPlacementAnimationCompletedSignal signal) { var constructionSite = _entityCollection.Create(); 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.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.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)); } } } }