Files
riversong-code-showcase/Source/Riversong/Game/World/Agents/ConstructionAgentSystem.cs
2026-05-21 16:04:49 +02:00

115 lines
4.5 KiB
C#

using System.Collections.Generic;
namespace DanieleMarotta.RiversongCodeShowcase
{
[GameSystemGroup(typeof(DayOnlyAgentSpawnSystemsGroup))]
public class ConstructionAgentSystem : GameSystem, IUpdatable
{
private const int FetchRange = 20;
[InjectService]
private GameConfig _config;
[InjectService]
private World _world;
[InjectService]
private IEntityCollection _entityCollection;
[InjectService]
private IEntityCache _entityCache;
[InjectService]
private IProductCatalog _productCatalog;
[InjectService]
private IAgentFactory _agentFactory;
[InjectService]
private ITileSpace _tileSpace;
[InjectService]
private IBuildingSpatialQuery _buildingSpatialQuery;
public ConstructionAgentSystem(IServiceLocator serviceLocator) : base(serviceLocator)
{
}
public void Update()
{
var agentDefinition = (AgentDefinition)_config.Agents.GenericAgent.Asset;
foreach (ConstructionSite constructionSite in _entityCollection.GetInternalEntityList(typeof(ConstructionSite)))
TryFetch(constructionSite, constructionSite.Rect, constructionSite.Building.BuildingMaterials, agentDefinition);
foreach (var house in _entityCache.GetHouses())
{
ref var needsState = ref house.GetNeedsStateRW();
if (needsState.UpgradeState != TierUpgradeState.FetchingMaterials) continue;
var products = house.Definition.HouseTiers[house.TierIndex].UpgradeMaterials;
TryFetch(house, house.Rect, products, agentDefinition);
}
}
private void TryFetch<T>(T source, TileRect sourceRect, List<ProductAmountAuthoring> products, AgentDefinition agentDefinition)
where T : class, IProductStorageEntity, IAgentSourceEntity
{
if (!_agentFactory.CanSpawnAgent(source)) return;
ref var sourceStorage = ref source.GetStorageRW();
foreach (var (product, needed) in products)
{
var productHandle = _productCatalog.GetHandle(product);
sourceStorage.Get(productHandle, out var count, out var putReservations, out _);
var remaining = needed - count - putReservations;
if (remaining <= 0) continue;
if (!FindFetchDestination(source.Id, sourceRect, productHandle, out var destinationRect, out var destinationEntity)) continue;
sourceStorage.ReservePutting(productHandle, 1);
ref var destinationStorage = ref destinationEntity.GetStorageRW();
destinationStorage.ReserveTaking(productHandle, 1);
var position = _tileSpace.TileToWorld(sourceRect.Center);
var agent = _agentFactory.CreateVillager(agentDefinition, source, position);
IntentQueue.Fetch(
ref agent.GetIntentQueueRW(),
sourceRect,
source.Id,
destinationRect,
destinationEntity.Id,
productHandle,
1,
PathTraversalRules.GenericAgent | PathTraversalRules.UpdateFailedPathCache);
break;
}
}
private bool FindFetchDestination(int sourceId, TileRect sourceRect, int productHandle, out TileRect destinationRect, out IProductStorageEntity destinationEntity)
{
var productStackFound = _world.ProductStacks.FindClosest(sourceRect, FetchRange, productHandle, 1, out var productStack);
var storageFound = _buildingSpatialQuery.FindStorageForFetch(sourceId, sourceRect, FetchRange, productHandle, out var storageBuilding);
if (productStackFound || storageFound)
{
var productStackDistance = productStackFound ? TileMath.StepCount(sourceRect, productStack.Tile) : int.MaxValue;
var storageDistance = storageFound ? TileMath.StepCount(sourceRect, storageBuilding.Rect) : int.MaxValue;
destinationRect = productStackDistance < storageDistance ? TileRect.OneTile(productStack.Tile) : storageBuilding.Rect;
destinationEntity = productStackDistance < storageDistance ? productStack : storageBuilding;
return true;
}
destinationRect = TileRect.Empty;
destinationEntity = null;
return false;
}
}
}