using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using UnityEngine; namespace DanieleMarotta.RiversongCodeShowcase { [Service(typeof(IBuildingVisualizationCollection))] public class BuildingVisualizationManager : GameSystem, IInitializable, IDisposable, IBuildingVisualizationCollection { private const int SpatialLookupCellSize = 5; [InjectService] private ISignalBus _signalBus; [InjectService] private IScene _scene; [InjectService] private ITileSpace _tileSpace; [InjectService] private IEntityCollection _entityCollection; [InjectService] private World _world; private HashSet _pendingVisualizations = new(); private Dictionary _visualizations = new(); private SpatialLookup _allVisualizationsSpatialLookup = new(SpatialLookupCellSize); private SpatialLookup _canBeDeletedSpatialLookup = new(SpatialLookupCellSize); public BuildingVisualizationManager(IServiceLocator serviceLocator) : base(serviceLocator) { } public UniTask InitializeAsync() { _signalBus.Subscribe(OnBuildingCreated); _signalBus.Subscribe(OnCollectDeletedGameObjects); _signalBus.Subscribe(OnBuildingDeleted); _signalBus.Subscribe(OnBuildingUpgraded); return UniTask.CompletedTask; } public void Dispose() { _signalBus.Unsubscribe(OnBuildingCreated); _signalBus.Unsubscribe(OnCollectDeletedGameObjects); _signalBus.Unsubscribe(OnBuildingDeleted); _signalBus.Unsubscribe(OnBuildingUpgraded); } private void OnBuildingCreated(BuildingCreatedSignal signal) { var building = signal.Building; _pendingVisualizations.Add(building.Id); _ = CreateVisualizationAsync(building); } private async UniTask CreateVisualizationAsync(Building building) { var folder = _scene.SceneFolders.Buildings; var visualizationGameObject = await building.Definition.Visualization.InstantiateAsync(folder); if (!_pendingVisualizations.Remove(building.Id)) { building.Definition.Visualization.ReleaseInstance(visualizationGameObject); return; } var visualization = visualizationGameObject.GetComponent(); if (!visualization) { Debug.LogError($"Building visualization '{visualizationGameObject.name}' has no {nameof(BuildingVisualization)} component"); return; } visualization.SetTier(0); var position = _tileSpace.GetRectWorldCenter(building.Rect); var rotation = building.Orientation.ToQuaternion(); visualization.transform.SetPositionAndRotation(position, rotation); _visualizations.Add(building.Id, visualization); _signalBus.Raise(new BuildingVisualizationCreatedSignal(building, visualization)); _allVisualizationsSpatialLookup.Add(visualization.gameObject, building.Rect, building.Id); if (building.Definition.CanBeDeleted) _canBeDeletedSpatialLookup.Add(visualization.gameObject, building.Rect, building.Id); } private void OnCollectDeletedGameObjects(CollectDeletedGameObjectsSignal signal) { if ((signal.Filter & DeletedGameObjectsFilter.Buildings) == 0) return; _canBeDeletedSpatialLookup.Find(signal.Rect, signal.GameObjects); } private void OnBuildingDeleted(BuildingDeletedSignal signal) { var building = signal.Building; if (_pendingVisualizations.Remove(building.Id)) { _signalBus.Raise(new BuildingDeleteAnimationCompletedSignal(building)); return; } _visualizations.Remove(building.Id, out var visualization); _allVisualizationsSpatialLookup.Remove(building.Rect, building.Id); _canBeDeletedSpatialLookup.Remove(building.Rect, building.Id); if (signal.Options == DeleteBuildingOptions.Silent) { FinalizeBuildingDeletion(building, visualization); return; } _ = PlayDeleteAnimationAsync(building, visualization); } private async UniTask PlayDeleteAnimationAsync(Building building, BuildingVisualization visualization) { var deleteAnimation = visualization.GetComponent(); await deleteAnimation.PlayAsync(building); FinalizeBuildingDeletion(building, visualization); } private void FinalizeBuildingDeletion(Building building, BuildingVisualization visualization) { _signalBus.Raise(new BuildingDeleteAnimationCompletedSignal(building)); building.Definition.Visualization.ReleaseInstance(visualization.gameObject); } private void OnBuildingUpgraded(BuildingUpgradedSignal signal) { var building = signal.Building; var visualization = _visualizations[building.Id]; visualization.SetTier(building.TierIndex); if (visualization.TryGetComponent(out var animation)) _ = animation.PlayAsync(building); } public bool IsVisualizationPending(int id) { return _pendingVisualizations.Contains(id); } public bool TryGetVisualization(int id, out BuildingVisualization visualization) { return _visualizations.TryGetValue(id, out visualization); } } }