using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; namespace DanieleMarotta.RiversongCodeShowcase { [Service(typeof(IUnlocksService))] [InitializeAfter(typeof(HouseTierCountTrackingSystem))] public class UnlocksManagerSystem : GameSystem, IInitializable, IDisposable, IUnlocksService { [InjectService] private IGameDatabase _gameDatabase; [InjectService] private ISignalBus _signalBus; [InjectService] private IEntityCollection _entityCollection; [InjectService] private World _world; private List _unlocks; public UnlocksManagerSystem(IServiceLocator serviceLocator) : base(serviceLocator) { } public UniTask InitializeAsync() { _unlocks = _gameDatabase.OfType(); foreach (var unlock in _unlocks) if (unlock.Building) _world.UnlocksState.AddBuildingUnlock(unlock.Building, unlock.RuntimeId); _signalBus.Subscribe(OnWorldReady); _signalBus.Subscribe(OnBuildingCreated); _signalBus.Subscribe(OnBuildingUpgraded); return UniTask.CompletedTask; } public void Dispose() { _signalBus.Unsubscribe(OnWorldReady); _signalBus.Unsubscribe(OnBuildingCreated); _signalBus.Unsubscribe(OnBuildingUpgraded); } private void OnWorldReady(WorldReadySignal signal) { _ = GrantFreeUnlocksWithDelayAsync(); } private async UniTask GrantFreeUnlocksWithDelayAsync() { await UniTask.WaitForSeconds(2, true); foreach (var unlock in _unlocks) if (unlock.Conditions.Count <= 0) Unlock(unlock); } private void OnBuildingCreated(BuildingCreatedSignal signal) { TryUnlockAvailableUnlocks(); } private void OnBuildingUpgraded(BuildingUpgradedSignal signal) { TryUnlockAvailableUnlocks(); } private void TryUnlockAvailableUnlocks() { foreach (var unlock in _unlocks) if (CanUnlock(unlock)) Unlock(unlock); } private bool CanUnlock(UnlockDefinition unlock) { if (_world.UnlocksState.Unlocked.Contains(unlock.RuntimeId)) return false; foreach (var condition in unlock.Conditions) if (!IsConditionSatisfied(condition)) return false; return true; } public void Unlock(UnlockDefinition unlock) { if (!_world.UnlocksState.Unlocked.Add(unlock.RuntimeId)) return; switch (unlock.Type) { case UnlockType.UnlockBuilding: UnlockBuilding(unlock); break; case UnlockType.None: default: throw new ArgumentOutOfRangeException(); } _signalBus.Raise(new UnlockUnlockedSignal(unlock)); } public void UnlockAll() { foreach (var unlock in _unlocks) Unlock(unlock); } private void UnlockBuilding(UnlockDefinition unlock) { var unlocksState = _world.UnlocksState; var unlockedId = unlock.Building.RuntimeId; unlocksState.UnlockedBuildings.Add(unlockedId); unlocksState.TeasedBuildings.Remove(unlockedId); foreach (var teased in unlock.TeasedBuildings) { if (unlocksState.UnlockedBuildings.Contains(teased.RuntimeId)) continue; unlocksState.TeasedBuildings.Add(teased.RuntimeId); } } private bool IsConditionSatisfied(UnlockCondition condition) { switch (condition.Type) { case UnlockConditionType.BuildingPlaced: foreach (Building building in _entityCollection.GetInternalEntityList(typeof(Building))) if (building.Definition.RuntimeId == condition.Building.RuntimeId) return true; return false; case UnlockConditionType.HouseCountAtTierOrAbove: var houseCount = 0; var houseCountsByTier = _world.PopulationState.HouseCountsByTier; for (var i = condition.MinHouseTierIndex; i < WorldPopulationState.MaxBuildingTier; i++) houseCount += houseCountsByTier[i]; return houseCount >= condition.HouseCount; case UnlockConditionType.None: default: throw new ArgumentOutOfRangeException(); } } } }