using System; using Cysharp.Threading.Tasks; using Unity.Mathematics; using UnityEngine; namespace DanieleMarotta.RiversongCodeShowcase { public class PopulationNeedsSystem : GameSystem, IInitializable, IDisposable, IUpdatable { private const float MaxHappinessScoreChangeRate = 0.1f; [InjectService] private ISignalBus _signalBus; [InjectService] private IEntityCache _entityCache; [InjectService] private World _world; [InjectService] private GameConfig _config; public PopulationNeedsSystem(IServiceLocator serviceLocator) : base(serviceLocator) { } public UniTask InitializeAsync() { _signalBus.Subscribe(OnEndOfWeek); return UniTask.CompletedTask; } public void Dispose() { _signalBus.Unsubscribe(OnEndOfWeek); } public void Update() { if (_world.TimeState.DayNightCycleStep != DayNightCycleStep.Day) return; foreach (var house in _entityCache.GetHouses()) { ref var needsState = ref house.GetNeedsStateRW(); ref var storage = ref house.GetStorageRW(); var targetHappiness = 1f; var happinessScore = 0; var maxHappinessScore = 0; needsState.AllNeedsMet = true; for (var i = 0; i < needsState.Needs.Length; i++) { var need = needsState.Needs[i]; if (need.TierIndex > house.TierIndex) break; if (need.Type != PopulationNeedType.Product) throw new NotImplementedException(); if (need.Current < need.YieldOnFetch) { var productCount = storage.AvailableNow(need.ProductHandle); if (productCount > 0) { storage.Take(need.ProductHandle, productCount); need.Current += (ushort)math.min(productCount * need.YieldOnFetch, ushort.MaxValue); } } var needMet = need.Current > 0; if (needMet) happinessScore += need.HappinessScore; maxHappinessScore += need.HappinessScore; needsState.AllNeedsMet &= needMet; needsState.Needs[i] = need; } if (maxHappinessScore > 0) { needsState.MaxHappinessScore += MaxHappinessScoreChangeRate * Time.deltaTime; needsState.MaxHappinessScore = math.min(needsState.MaxHappinessScore, maxHappinessScore); targetHappiness = needsState.MaxHappinessScore > 0 ? math.saturate(happinessScore / needsState.MaxHappinessScore) : 1; } else { needsState.MaxHappinessScore = 0; } needsState.Happiness = Mathf.MoveTowards(needsState.Happiness, targetHappiness, _config.Population.OverallHappinessChangeRate * Time.deltaTime); needsState.Happiness = math.saturate(needsState.Happiness); needsState.OverallHappinessWeight = math.saturate((float)(_world.TimeState.TotalWeeks - house.WeekCreated) / _config.Population.HouseWeightRampUpWeekCount); } } private void OnEndOfWeek(EndOfWeekSignal signal) { foreach (var house in _entityCache.GetHouses()) { ref var needsState = ref house.GetNeedsStateRW(); if (needsState.AllNeedsMet) needsState.NeedsMetForWeeks++; else needsState.NeedsMetForWeeks = 0; for (var i = 0; i < needsState.Needs.Length; i++) { var need = needsState.Needs[i]; if (need.TierIndex > house.TierIndex) break; if (need.Type != PopulationNeedType.Product) continue; if (need.Current >= need.ConsumptionRate) need.Current -= need.ConsumptionRate; else need.Current = 0; needsState.Needs[i] = need; } } } } }