324 lines
12 KiB
C#
324 lines
12 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using Cysharp.Threading.Tasks;
|
|
using UnityEngine.Pool;
|
|
|
|
namespace DanieleMarotta.RiversongCodeShowcase
|
|
{
|
|
[Service(typeof(IBuildingFactory))]
|
|
[Service(typeof(IDeleteBuildingService))]
|
|
[Service(typeof(IBuildingSpatialQuery))]
|
|
public class BuildingManagerSystem : GameSystem, IInitializable, IDisposable, IBuildingFactory, IDeleteBuildingService, IBuildingSpatialQuery
|
|
{
|
|
[InjectService]
|
|
private ISignalBus _signalBus;
|
|
|
|
[InjectService]
|
|
private IEntityCollection _entityCollection;
|
|
|
|
[InjectService]
|
|
private IProductStorageManager _productStorageManager;
|
|
|
|
[InjectService]
|
|
private IProductCatalog _productCatalog;
|
|
|
|
[InjectService]
|
|
private IProductStackFactory _productStackFactory;
|
|
|
|
[InjectService]
|
|
private IEntityCache _entityCache;
|
|
|
|
[InjectService]
|
|
private IAgentFactory _agentFactory;
|
|
|
|
[InjectService]
|
|
private IFailedPathCache _failedPathCache;
|
|
|
|
[InjectService]
|
|
private World _world;
|
|
|
|
[InjectService]
|
|
private GameConfig _config;
|
|
|
|
private Func<Building, int, bool> _fetchValidator;
|
|
|
|
private Func<Building, int, bool> _deliveryValidator;
|
|
|
|
private Func<Building, int, bool> _storageRequestValidator;
|
|
|
|
private Func<Building, int, int> _sourceRangeFunc;
|
|
|
|
private Func<Building, int, int> _candidateRangeFunc;
|
|
|
|
private Func<Building, int, int> _infiniteRangeFunc;
|
|
|
|
public BuildingManagerSystem(IServiceLocator serviceLocator) : base(serviceLocator)
|
|
{
|
|
_fetchValidator = FetchValidator;
|
|
_deliveryValidator = DeliveryValidator;
|
|
_storageRequestValidator = StorageRequestValidator;
|
|
_sourceRangeFunc = SourceRange;
|
|
_candidateRangeFunc = CandidateRange;
|
|
_infiniteRangeFunc = InfiniteRange;
|
|
}
|
|
|
|
public UniTask InitializeAsync()
|
|
{
|
|
_signalBus.Subscribe<ConstructionCompletedSignal>(OnConstructionCompleted);
|
|
_signalBus.Subscribe<DoDeleteToolSignal>(OnDoDeleteTool);
|
|
|
|
return UniTask.CompletedTask;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_signalBus.Unsubscribe<ConstructionCompletedSignal>(OnConstructionCompleted);
|
|
_signalBus.Unsubscribe<DoDeleteToolSignal>(OnDoDeleteTool);
|
|
}
|
|
|
|
private void OnConstructionCompleted(ConstructionCompletedSignal signal)
|
|
{
|
|
var constructionSite = signal.ConstructionSite;
|
|
Create(constructionSite.Building, constructionSite.Rect, constructionSite.Orientation);
|
|
}
|
|
|
|
public Building Create(BuildingDefinition definition, TileRect rect, Directions orientation)
|
|
{
|
|
var building = _entityCollection.Create<Building>();
|
|
|
|
building.Definition = definition;
|
|
building.Rect = rect;
|
|
building.Orientation = orientation;
|
|
building.WeekCreated = _world.TimeState.TotalWeeks;
|
|
InitializeStorage(building);
|
|
InitializeProductionState(building);
|
|
InitializeAgentSource(building);
|
|
InitializeHouse(building);
|
|
InitializeSleepState(building);
|
|
|
|
_entityCollection.Add(building);
|
|
|
|
_signalBus.Raise(new BuildingCreatedSignal(building));
|
|
|
|
return building;
|
|
}
|
|
|
|
private void InitializeStorage(Building building)
|
|
{
|
|
var capacity = building.Definition.StorageCapacity;
|
|
if (capacity <= 0) return;
|
|
|
|
_productStorageManager.InitializeProductStorage(building, capacity);
|
|
}
|
|
|
|
private void InitializeProductionState(Building building)
|
|
{
|
|
var recipeDefinition = building.Definition.Recipe;
|
|
if (!recipeDefinition) return;
|
|
|
|
ref var productionState = ref building.GetProductionStateRW();
|
|
|
|
productionState.State = ProducerState.NotWorking;
|
|
|
|
productionState.Recipe.Inputs.Length = recipeDefinition.Inputs.Count;
|
|
for (var i = 0; i < recipeDefinition.Inputs.Count; i++)
|
|
{
|
|
var input = recipeDefinition.Inputs[i];
|
|
var productHandle = _productCatalog.GetHandle(input.Product);
|
|
productionState.Recipe.Inputs[i] = (productHandle, input.Amount);
|
|
}
|
|
|
|
productionState.Recipe.OutputProductHandle = _productCatalog.GetHandle(recipeDefinition.Output.Product);
|
|
productionState.Recipe.OutputAmount = recipeDefinition.Output.Amount;
|
|
|
|
productionState.Recipe.ProductionTime = recipeDefinition.ProductionTime;
|
|
}
|
|
|
|
private void InitializeAgentSource(Building building)
|
|
{
|
|
_agentFactory.InitializeAgentSource(building);
|
|
}
|
|
|
|
private void InitializeHouse(Building building)
|
|
{
|
|
if (!building.Definition.IsHouse) return;
|
|
|
|
var tiers = building.Definition.HouseTiers;
|
|
building.PopulationCapacity = tiers.Count > 0 ? tiers[0].Capacity : 0;
|
|
|
|
InitializeHouseNeeds(building);
|
|
}
|
|
|
|
private void InitializeHouseNeeds(Building building)
|
|
{
|
|
ref var needsState = ref building.GetNeedsStateRW();
|
|
|
|
var tiers = building.Definition.HouseTiers;
|
|
for (var i = 0; i < tiers.Count; i++)
|
|
foreach (var need in tiers[i].Needs)
|
|
needsState.Needs.Add(
|
|
new PopulationNeed
|
|
{
|
|
TierIndex = (byte)i,
|
|
Type = need.Type,
|
|
ProductHandle = _productCatalog.GetHandle(need.Product),
|
|
HappinessScore = (ushort)need.HappinessScore,
|
|
ConsumptionRate = (byte)need.ConsumptionRate,
|
|
FetchThreshold = (byte)need.FetchThreshold,
|
|
YieldOnFetch = (ushort)need.YieldOnFetch
|
|
});
|
|
|
|
needsState.UpgradeState = tiers.Count > 1 ? TierUpgradeState.NotReady : TierUpgradeState.MaxedOut;
|
|
|
|
needsState.Happiness = _config.Population.InitialHouseHappiness;
|
|
}
|
|
|
|
private void InitializeSleepState(Building building)
|
|
{
|
|
ref var sleepState = ref building.GetSleepStateRW();
|
|
sleepState.HasHomelessWorkers = false;
|
|
sleepState.EfficiencyModifier = _config.Economy.RestedWorkersEfficiencyModifier;
|
|
}
|
|
|
|
private void OnDoDeleteTool(DoDeleteToolSignal signal)
|
|
{
|
|
using var toDeleteScope = HashSetPool<int>.Get(out var toDelete);
|
|
|
|
foreach (var tile in TileRange.From(signal.Rect))
|
|
foreach (Building building in _entityCollection.GetInternalEntityList(typeof(Building)))
|
|
{
|
|
if (!building.Definition.CanBeDeleted || !building.Rect.Contains(tile)) continue;
|
|
|
|
toDelete.Add(building.Id);
|
|
|
|
break;
|
|
}
|
|
|
|
foreach (var id in toDelete)
|
|
{
|
|
var building = _entityCollection.Get<Building>(id);
|
|
Delete(building, DeleteBuildingOptions.WithFeedback);
|
|
}
|
|
}
|
|
|
|
public void Delete(Building building, DeleteBuildingOptions options)
|
|
{
|
|
_entityCollection.Remove(building.Id);
|
|
|
|
_productStackFactory.CreateProductStacksFrom(building);
|
|
|
|
_signalBus.Raise(new BuildingDeletedSignal(building, options));
|
|
}
|
|
|
|
public bool FindStorageForFetch(int sourceId, TileRect sourceRect, int productHandle, out Building building)
|
|
{
|
|
return FindReachableBuilding(sourceId, sourceRect, 0, productHandle, _entityCache.GetStorageBuildings(), _fetchValidator, _infiniteRangeFunc, out building);
|
|
}
|
|
|
|
public bool FindStorageForFetch(int sourceId, TileRect sourceRect, int sourceRange, int productHandle, out Building building)
|
|
{
|
|
return FindReachableBuilding(sourceId, sourceRect, sourceRange, productHandle, _entityCache.GetStorageBuildings(), _fetchValidator, _sourceRangeFunc, out building);
|
|
}
|
|
|
|
public bool FindStorageForDelivery(int sourceId, TileRect sourceRect, int productHandle, out Building building)
|
|
{
|
|
return FindReachableBuilding(sourceId, sourceRect, 0, productHandle, _entityCache.GetStorageBuildings(), _deliveryValidator, _infiniteRangeFunc, out building);
|
|
}
|
|
|
|
public bool FindStorageForRequest(int sourceId, TileRect sourceRect, int sourceRange, int productHandle, out Building building)
|
|
{
|
|
return FindReachableBuilding(
|
|
sourceId,
|
|
sourceRect,
|
|
sourceRange,
|
|
productHandle,
|
|
_entityCache.GetStorageRequestBuildings(),
|
|
_storageRequestValidator,
|
|
_infiniteRangeFunc,
|
|
out building);
|
|
}
|
|
|
|
public bool FindProviderForFetch(int sourceId, TileRect sourceRect, int productHandle, out Building building)
|
|
{
|
|
return FindReachableBuilding(sourceId, sourceRect, 0, productHandle, _entityCache.GetProviders(), _fetchValidator, _candidateRangeFunc, out building);
|
|
}
|
|
|
|
public void FindProvidersForHouse(TileRect sourceRect, List<Building> providers)
|
|
{
|
|
foreach (var provider in _entityCache.GetProviders())
|
|
if (TileMath.StepCount(provider.Rect, sourceRect) <= provider.Definition.Range)
|
|
providers.Add(provider);
|
|
}
|
|
|
|
public void FindProvidersForStorage(TileRect sourceRect, List<Building> providers)
|
|
{
|
|
foreach (var provider in _entityCache.GetProviders())
|
|
if (provider.Definition.FetchesProducts && TileMath.StepCount(provider.Rect, sourceRect) <= provider.Definition.Range)
|
|
providers.Add(provider);
|
|
}
|
|
|
|
private bool FindReachableBuilding(int sourceId,
|
|
TileRect sourceRect,
|
|
int sourceRange,
|
|
int productHandle,
|
|
List<Building> candidates,
|
|
Func<Building, int, bool> validator,
|
|
Func<Building, int, int> rangeFunc,
|
|
out Building building)
|
|
{
|
|
building = null;
|
|
var best = int.MaxValue;
|
|
|
|
foreach (var candidate in candidates)
|
|
{
|
|
if (_failedPathCache.IsKnownFailedPath(sourceId, candidate.Id)) continue;
|
|
|
|
var stepCount = TileMath.StepCount(sourceRect, candidate.Rect);
|
|
if (stepCount >= best || stepCount > rangeFunc.Invoke(candidate, sourceRange)) continue;
|
|
|
|
if (!validator.Invoke(candidate, productHandle)) continue;
|
|
|
|
best = stepCount;
|
|
building = candidate;
|
|
}
|
|
|
|
return best < int.MaxValue;
|
|
}
|
|
|
|
private bool FetchValidator(Building candidate, int productHandle)
|
|
{
|
|
ref var storage = ref candidate.GetStorageRW();
|
|
return storage.AvailableNow(productHandle) > 0;
|
|
}
|
|
|
|
private bool DeliveryValidator(Building candidate, int productHandle)
|
|
{
|
|
ref var storage = ref candidate.GetStorageRW();
|
|
ref var storagePolicy = ref candidate.GetProductStoragePolicyRW();
|
|
return storage.FreeSpace() > 0 && storagePolicy.IsTakingAllowed(productHandle);
|
|
}
|
|
|
|
private bool StorageRequestValidator(Building candidate, int productHandle)
|
|
{
|
|
ref var storage = ref candidate.GetStorageRW();
|
|
ref var storagePolicy = ref candidate.GetProductStoragePolicyRW();
|
|
return storage.CountIncludingReservations(productHandle) > storagePolicy.GetRequestedAmount(productHandle) && storagePolicy.CanFulfillRequests(productHandle);
|
|
}
|
|
|
|
private int SourceRange(Building candidate, int sourceRange)
|
|
{
|
|
return sourceRange;
|
|
}
|
|
|
|
private int CandidateRange(Building candidate, int sourceRange)
|
|
{
|
|
return candidate.Definition.Range;
|
|
}
|
|
|
|
private int InfiniteRange(Building candidate, int sourceRange)
|
|
{
|
|
return int.MaxValue;
|
|
}
|
|
}
|
|
}
|