riversong code showcase
This commit is contained in:
323
Source/Riversong/Game/World/Buildings/BuildingManagerSystem.cs
Normal file
323
Source/Riversong/Game/World/Buildings/BuildingManagerSystem.cs
Normal file
@@ -0,0 +1,323 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user