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 source, TileRect sourceRect, List 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; } } }