using System; using System.Collections.Generic; using System.Text; using Cysharp.Threading.Tasks; namespace DanieleMarotta.RiversongCodeShowcase { public class BuildMenuUIController : UIControllerSystem, IUpdatable, IDisposable { [InjectService] private IGameDatabase _gameDatabase; [InjectService] private IEditingService _editingService; [InjectService] private ISignalBus _signalBus; [InjectService] private World _world; [InjectService] private TextFormatHelper _textFormatHelper; [InjectService] private ICancelAction _cancelAction; private BuildMenuModel _model; private List _pendingBuildings = new(); private List _pendingTeasers = new(); private bool _isPlayingButtonAnimation; private Action _onUnlockAnimationStarted; public BuildMenuUIController(IServiceLocator serviceLocator) : base(serviceLocator) { } protected override BuildMenuUIView View => UIRoot.GetView(); public override async UniTask InitializeAsync() { await base.InitializeAsync(); var buildings = _gameDatabase.OfType(); buildings.Sort((x, y) => x.UIOrder.CompareTo(y.UIOrder)); _model = new BuildMenuModel(); var sb = new StringBuilder(); foreach (var building in buildings) { if (_world.UnlocksState.TryGetBuildingUnlock(building, out var unlockId)) { var unlock = _gameDatabase.WithId(unlockId); _textFormatHelper.FormatUnlockConditions(unlock, sb); } var buildingModel = new BuildMenuBuildingModel { Building = building, UnlockConditions = sb.ToString() }; _model.Buildings.Add(buildingModel); sb.Clear(); } View.SetModel(_model); View.ButtonClick += OnButtonClick; _editingService.ActiveToolChanged += OnActiveToolChanged; _signalBus.Subscribe(OnUnlockUnlocked); foreach (var unlockId in _world.UnlocksState.Unlocked) { var unlock = _gameDatabase.WithId(unlockId); OnUnlockUnlocked(unlock); } _cancelAction.AddHandler( (int)CancelActions.CloseBuildMenu, _ => { if (!View.IsOpen()) return false; CloseView(); return true; }); } public void Dispose() { View.ButtonClick -= OnButtonClick; _editingService.ActiveToolChanged -= OnActiveToolChanged; _signalBus.Unsubscribe(OnUnlockUnlocked); } private void OnButtonClick(BuildingDefinition building) { if (!_world.UnlocksState.UnlockedBuildings.Contains(building.RuntimeId)) return; var buildTool = _editingService.EditingState.BuildTool; buildTool.Building = building; _editingService.ActivateTool(buildTool); } private void OnActiveToolChanged(EditTool tool) { BuildingDefinition selectedBuilding = null; if (tool is BuildTool buildTool) selectedBuilding = buildTool.Building; _model.SelectedBuilding = selectedBuilding; } private void OnUnlockUnlocked(UnlockDefinition unlock) { if (unlock.Building) { _pendingBuildings.Add(unlock.Building); _pendingBuildings.Sort(); } if (unlock.TeasedBuildings.Count > 0) { foreach (var teasedBuilding in unlock.TeasedBuildings) _pendingTeasers.Add(teasedBuilding); _pendingTeasers.Sort(); } } private void OnUnlockUnlocked(UnlockUnlockedSignal signal) { OnUnlockUnlocked(signal.Unlock); } public void Update() { UpdateModels(); UpdateButtons(); } private void UpdateModels() { foreach (var buildingModel in _model.Buildings) buildingModel.IsUnlocked = _world.UnlocksState.UnlockedBuildings.Contains(buildingModel.Building.RuntimeId); } private void UpdateButtons() { if (!View.IsOpen() || _isPlayingButtonAnimation || (_pendingBuildings.Count <= 0 && _pendingTeasers.Count <= 0)) return; if (_pendingBuildings.Count > 0) { var building = _pendingBuildings[0]; _pendingBuildings.RemoveAt(0); _ = UnlockButtonAsync(building); return; } while (_pendingTeasers.Count > 0) { var teaserBuilding = _pendingTeasers[0]; _pendingTeasers.RemoveAt(0); if (_world.UnlocksState.UnlockedBuildings.Contains(teaserBuilding.RuntimeId)) continue; _ = CreateTeaserButtonAsync(teaserBuilding); break; } } private async UniTask UnlockButtonAsync(BuildingDefinition building) { _isPlayingButtonAnimation = true; try { _onUnlockAnimationStarted ??= () => _signalBus.Raise(new BuildMenuButtonUnlockAnimationStartedSignal()); await View.UnlockOrCreateUnlockedButtonAsync(building, _onUnlockAnimationStarted); await UniTask.WaitForSeconds(0.5f, true); } finally { _isPlayingButtonAnimation = false; } } private async UniTask CreateTeaserButtonAsync(BuildingDefinition building) { _isPlayingButtonAnimation = true; try { await View.CreateTeaserButtonAsync(building); await UniTask.WaitForSeconds(0.5f, true); } finally { _isPlayingButtonAnimation = false; } } protected override void CloseView(bool animate = true) { if (_isPlayingButtonAnimation) return; base.CloseView(animate); } } }