void SectorHelper::GetAvailableFleetSupplyCount(UFlareSimulatedSector* Sector, UFlareCompany* Company, int32& OwnedFS, int32& AvailableFS, int32& AffordableFS) { OwnedFS = 0; int32 NotOwnedFS = 0; FFlareResourceDescription* FleetSupply = Sector->GetGame()->GetScenarioTools()->FleetSupply; for (int32 SpacecraftIndex = 0; SpacecraftIndex < Sector->GetSectorSpacecrafts().Num(); SpacecraftIndex++) { UFlareSimulatedSpacecraft* Spacecraft = Sector->GetSectorSpacecrafts()[SpacecraftIndex]; if (Spacecraft->IsHostile(Company)) { // At war, no trade possible continue; } int32 AvailableQuantity = Spacecraft->GetActiveCargoBay()->GetResourceQuantity(FleetSupply, Company); if (Company == Spacecraft->GetCompany()) { OwnedFS += AvailableQuantity; } else { NotOwnedFS += AvailableQuantity; } } AvailableFS = OwnedFS + NotOwnedFS; int32 ResourcePrice = Sector->GetResourcePrice(FleetSupply, EFlareResourcePriceContext::MaintenanceConsumption); int32 MaxAffordableQuantity = FMath::Max(0, int32(Company->GetMoney() / ResourcePrice)); AffordableFS = OwnedFS + FMath::Min(MaxAffordableQuantity, NotOwnedFS); }
TMap<FFlareResourceDescription*, WorldHelper::FlareResourceStats> SectorHelper::ComputeSectorResourceStats(UFlareSimulatedSector* Sector, bool IncludeStorage) { TMap<FFlareResourceDescription*, WorldHelper::FlareResourceStats> WorldStats; // Init for(int32 ResourceIndex = 0; ResourceIndex < Sector->GetGame()->GetResourceCatalog()->Resources.Num(); ResourceIndex++) { FFlareResourceDescription* Resource = &Sector->GetGame()->GetResourceCatalog()->Resources[ResourceIndex]->Data; WorldHelper::FlareResourceStats ResourceStats; ResourceStats.Production = 0; ResourceStats.Consumption = 0; ResourceStats.Balance = 0; ResourceStats.Stock = 0; ResourceStats.Capacity = 0; WorldStats.Add(Resource, ResourceStats); } for (int SpacecraftIndex = 0; SpacecraftIndex < Sector->GetSectorSpacecrafts().Num(); SpacecraftIndex++) { UFlareSimulatedSpacecraft* Spacecraft = Sector->GetSectorSpacecrafts()[SpacecraftIndex]; if(!IncludeStorage && Spacecraft->HasCapability(EFlareSpacecraftCapability::Storage)) { continue; } // Stock TArray<FFlareCargo>& CargoBaySlots = Spacecraft->GetActiveCargoBay()->GetSlots(); for (int CargoIndex = 0; CargoIndex < CargoBaySlots.Num(); CargoIndex++) { FFlareCargo& Cargo = CargoBaySlots[CargoIndex]; if (!Cargo.Resource) { continue; } WorldHelper::FlareResourceStats *ResourceStats = &WorldStats[Cargo.Resource]; FFlareResourceUsage Usage = Spacecraft->GetResourceUseType(Cargo.Resource); if(Usage.HasUsage(EFlareResourcePriceContext::FactoryInput) || Usage.HasUsage(EFlareResourcePriceContext::ConsumerConsumption) || Usage.HasUsage(EFlareResourcePriceContext::MaintenanceConsumption) || Usage.HasUsage(EFlareResourcePriceContext::HubInput)) { ResourceStats->Capacity += Spacecraft->GetActiveCargoBay()->GetSlotCapacity() - Cargo.Quantity; } if(Usage.HasUsage(EFlareResourcePriceContext::FactoryOutput) || Usage.HasUsage(EFlareResourcePriceContext::MaintenanceConsumption) || Usage.HasUsage(EFlareResourcePriceContext::HubOutput)) { ResourceStats->Stock += Cargo.Quantity; } } for (int32 FactoryIndex = 0; FactoryIndex < Spacecraft->GetFactories().Num(); FactoryIndex++) { UFlareFactory* Factory = Spacecraft->GetFactories()[FactoryIndex]; if(Factory->IsShipyard() && !Factory->IsActive()) { const FFlareProductionData* ProductionData = Spacecraft->GetNextOrderShipProductionData(Factory->IsLargeShipyard()? EFlarePartSize::L : EFlarePartSize::S); if (ProductionData) { for(const FFlareFactoryResource& FactoryResource : ProductionData->InputResources) { const FFlareResourceDescription* Resource = &FactoryResource.Resource->Data; WorldHelper::FlareResourceStats *ResourceStats = &WorldStats[Resource]; int64 ProductionDuration = ProductionData->ProductionTime; float Flow = 0; if (ProductionDuration == 0) { Flow = 1; } else { Flow = (float) FactoryResource.Quantity / float(ProductionDuration); } ResourceStats->Consumption += Flow; } } continue; } if ((!Factory->IsActive() || !Factory->IsNeedProduction())) { // No resources needed continue; } // Input flow for (int32 ResourceIndex = 0; ResourceIndex < Factory->GetInputResourcesCount(); ResourceIndex++) { FFlareResourceDescription* Resource = Factory->GetInputResource(ResourceIndex); WorldHelper::FlareResourceStats *ResourceStats = &WorldStats[Resource]; int64 ProductionDuration = Factory->GetProductionDuration(); float Flow = 0; if (ProductionDuration == 0) { Flow = 1; } else { Flow = (float) Factory->GetInputResourceQuantity(ResourceIndex) / float(ProductionDuration); } ResourceStats->Consumption += Flow; } // Ouput flow for (int32 ResourceIndex = 0; ResourceIndex < Factory->GetOutputResourcesCount(); ResourceIndex++) { FFlareResourceDescription* Resource = Factory->GetOutputResource(ResourceIndex); WorldHelper::FlareResourceStats *ResourceStats = &WorldStats[Resource]; int64 ProductionDuration = Factory->GetProductionDuration(); if (ProductionDuration == 0) { ProductionDuration = 10; } float Flow = (float) Factory->GetOutputResourceQuantity(ResourceIndex) / float(ProductionDuration); ResourceStats->Production+= Flow; } } } // FS FFlareResourceDescription* FleetSupply = Sector->GetGame()->GetScenarioTools()->FleetSupply; WorldHelper::FlareResourceStats *FSResourceStats = &WorldStats[FleetSupply]; FFlareFloatBuffer* Stats = &Sector->GetData()->FleetSupplyConsumptionStats; float MeanConsumption = Stats->GetMean(0, Stats->MaxSize-1); FSResourceStats->Consumption += MeanConsumption; FSResourceStats->Capacity += Stats->GetValue(0); // Customer flow for (int32 ResourceIndex = 0; ResourceIndex < Sector->GetGame()->GetResourceCatalog()->ConsumerResources.Num(); ResourceIndex++) { FFlareResourceDescription* Resource = &Sector->GetGame()->GetResourceCatalog()->ConsumerResources[ResourceIndex]->Data; WorldHelper::FlareResourceStats *ResourceStats = &WorldStats[Resource]; ResourceStats->Consumption += Sector->GetPeople()->GetRessourceConsumption(Resource, false); } // Balance for(int32 ResourceIndex = 0; ResourceIndex < Sector->GetGame()->GetResourceCatalog()->Resources.Num(); ResourceIndex++) { FFlareResourceDescription* Resource = &Sector->GetGame()->GetResourceCatalog()->Resources[ResourceIndex]->Data; WorldHelper::FlareResourceStats *ResourceStats = &WorldStats[Resource]; ResourceStats->Balance = ResourceStats->Production - ResourceStats->Consumption; /*FLOGV("World stats for %s: Production=%f Consumption=%f Balance=%f Stock=%d", *Resource->Name.ToString(), ResourceStats->Production, ResourceStats->Consumption, ResourceStats->Balance, ResourceStats->Stock);*/ } return WorldStats; }
void SectorHelper::ConsumeFleetSupply(UFlareSimulatedSector* Sector, UFlareCompany* Company, int32 ConsumedFS, bool ForRepair) { // First check for owned FS Sector->OnFleetSupplyConsumed(ConsumedFS); FFlareResourceDescription* FleetSupply = Sector->GetGame()->GetScenarioTools()->FleetSupply; for (int32 SpacecraftIndex = 0; SpacecraftIndex < Sector->GetSectorSpacecrafts().Num(); SpacecraftIndex++) { UFlareSimulatedSpacecraft* Spacecraft = Sector->GetSectorSpacecrafts()[SpacecraftIndex]; if (Company != Spacecraft->GetCompany()) { continue; } int TakenQuantity = Spacecraft->GetActiveCargoBay()->TakeResources(FleetSupply, ConsumedFS, Company); ConsumedFS -= TakenQuantity; if(ConsumedFS == 0) { return; } } for (int32 SpacecraftIndex = 0; SpacecraftIndex < Sector->GetSectorSpacecrafts().Num(); SpacecraftIndex++) { UFlareSimulatedSpacecraft* Spacecraft = Sector->GetSectorSpacecrafts()[SpacecraftIndex]; if (Company == Spacecraft->GetCompany()) { continue; } if (Spacecraft->IsHostile(Company)) { // At war, no trade possible continue; } int TakenQuantity = Spacecraft->GetActiveCargoBay()->TakeResources(FleetSupply, ConsumedFS, Company); if(TakenQuantity > 0) { int32 ResourcePrice = Sector->GetResourcePrice(FleetSupply, EFlareResourcePriceContext::MaintenanceConsumption); int64 Cost = TakenQuantity * ResourcePrice; Company->TakeMoney(Cost, true, FFlareTransactionLogEntry::LogPayMaintenance(Spacecraft, TakenQuantity, ForRepair)); Spacecraft->GetCompany()->GiveMoney(Cost, FFlareTransactionLogEntry::LogPaidForMaintenance(Spacecraft, Company, TakenQuantity, ForRepair)); if(Spacecraft->GetCurrentFleet() && Spacecraft->GetCurrentFleet()->IsAutoTrading()) { Spacecraft->GetCurrentFleet()->GetData()->AutoTradeStatsUnloadResources += TakenQuantity; Spacecraft->GetCurrentFleet()->GetData()->AutoTradeStatsMoneySell += Cost; #if DEBUG_AI_TRADING_STATS FLOGV("Auto trading %s sell %d %s to %s for %lld", *Spacecraft->GetImmatriculation().ToString(), TakenQuantity, *FleetSupply->Name.ToString(), *Company->GetCompanyName().ToString(), Cost); FLOGV("AutoTradeStatsDays=%d LoadResources=%d UnloadResources=%d MoneyBuy=%lld MoneySell=%lld", Spacecraft->GetCurrentFleet()->GetData()->AutoTradeStatsDays, Spacecraft->GetCurrentFleet()->GetData()->AutoTradeStatsLoadResources, Spacecraft->GetCurrentFleet()->GetData()->AutoTradeStatsUnloadResources, Spacecraft->GetCurrentFleet()->GetData()->AutoTradeStatsMoneyBuy, Spacecraft->GetCurrentFleet()->GetData()->AutoTradeStatsMoneySell); #endif } ConsumedFS -= TakenQuantity; if(ConsumedFS == 0) { return; } } } }
UFlareSimulatedSpacecraft* SectorHelper::FindTradeStation(FlareTradeRequest Request) { //FLOGV("FindTradeStation of %s for %s (%d)", *Request.Resource->Acronym.ToString(), *Request.Client->GetImmatriculation().ToString(), (Request.Operation + 0)); if(!Request.Client || !Request.Client->GetCurrentSector()) { FLOG("Invalid find trade query"); return NULL; } UFlareCompany* ClientCompany = Request.AllowUseNoTradeForMe ? Request.Client->GetCompany() : nullptr; UFlareSimulatedSector* Sector = Request.Client->GetCurrentSector(); TArray<UFlareSimulatedSpacecraft*>& SectorStations = Sector->GetSectorStations(); float UnloadQuantityScoreMultiplier = 0; float LoadQuantityScoreMultiplier = 0; float SellQuantityScoreMultiplier = 0; float BuyQuantityScoreMultiplier = 0; float FullRatioBonus = 0; float EmptyRatioBonus = 0; bool NeedInput = false; bool NeedOutput = false; switch(Request.Operation) { case EFlareTradeRouteOperation::Buy: BuyQuantityScoreMultiplier = 10.f; FullRatioBonus = 0.1; NeedOutput = true; break; case EFlareTradeRouteOperation::Sell: SellQuantityScoreMultiplier = 10.f; EmptyRatioBonus = 0.1; NeedInput = true; break; case EFlareTradeRouteOperation::Load: LoadQuantityScoreMultiplier = 10.f; FullRatioBonus = 0.1; NeedOutput = true; break; case EFlareTradeRouteOperation::Unload: UnloadQuantityScoreMultiplier = 10.f; EmptyRatioBonus = 0.1; NeedInput = true; break; case EFlareTradeRouteOperation::LoadOrBuy: LoadQuantityScoreMultiplier = 10.f; BuyQuantityScoreMultiplier = 1.f; FullRatioBonus = 0.1; NeedOutput = true; break; case EFlareTradeRouteOperation::UnloadOrSell: UnloadQuantityScoreMultiplier = 10.f; SellQuantityScoreMultiplier = 1.f; EmptyRatioBonus = 0.1; NeedInput = true; break; } float BestScore = 0; UFlareSimulatedSpacecraft* BestStation = NULL; uint32 AvailableQuantity = Request.Client->GetActiveCargoBay()->GetResourceQuantity(Request.Resource, ClientCompany); uint32 FreeSpace = Request.Client->GetActiveCargoBay()->GetFreeSpaceForResource(Request.Resource, ClientCompany); for (int32 StationIndex = 0; StationIndex < SectorStations.Num(); StationIndex++) { UFlareSimulatedSpacecraft* Station = SectorStations[StationIndex]; //FLOGV(" Check trade for %s", *Station->GetImmatriculation().ToString()); FText Unused; if(!Request.Client->CanTradeWith(Station, Unused)) { //FLOG(" cannot trade with"); continue; } if(!Request.AllowStorage && Station->HasCapability(EFlareSpacecraftCapability::Storage)) { continue; } FFlareResourceUsage StationResourceUsage = Station->GetResourceUseType(Request.Resource); if(NeedOutput && (!StationResourceUsage.HasUsage(EFlareResourcePriceContext::FactoryOutput) && !StationResourceUsage.HasUsage(EFlareResourcePriceContext::ConsumerConsumption) && !StationResourceUsage.HasUsage(EFlareResourcePriceContext::HubOutput))) { //FLOG(" need output but dont provide it"); continue; } if(NeedInput && (!StationResourceUsage.HasUsage(EFlareResourcePriceContext::FactoryInput) && !StationResourceUsage.HasUsage(EFlareResourcePriceContext::ConsumerConsumption) && !StationResourceUsage.HasUsage(EFlareResourcePriceContext::MaintenanceConsumption) && !StationResourceUsage.HasUsage(EFlareResourcePriceContext::HubInput))) { //FLOG(" need input but dont provide it"); continue; } int32 StationFreeSpace = Station->GetActiveCargoBay()->GetFreeSpaceForResource(Request.Resource, ClientCompany); int32 StationResourceQuantity = Station->GetActiveCargoBay()->GetResourceQuantity(Request.Resource, ClientCompany); if (!Station->IsUnderConstruction() && Station->IsComplex() && !Request.AllowFullStock) { if(Station->GetActiveCargoBay()->WantBuy(Request.Resource, ClientCompany) && Station->GetActiveCargoBay()->WantSell(Request.Resource, ClientCompany)) { int32 TotalCapacity = Station->GetActiveCargoBay()->GetTotalCapacityForResource(Request.Resource, ClientCompany); StationFreeSpace = FMath::Max(0, StationFreeSpace - TotalCapacity / 2); StationResourceQuantity = FMath::Max(0, StationResourceQuantity - TotalCapacity / 2); } } if (StationFreeSpace == 0 && StationResourceQuantity == 0) { //FLOG(" need quantity or resource"); continue; } float Score = 0; float FullRatio = (float) StationResourceQuantity / (float) (StationResourceQuantity + StationFreeSpace); float EmptyRatio = 1 - FullRatio; uint32 UnloadMaxQuantity = 0; uint32 LoadMaxQuantity = 0; if(!Station->IsUnderConstruction()) { // Check cargo limit if(NeedOutput && Request.CargoLimit != -1 && FullRatio < Request.CargoLimit / Station->GetLevel()) { continue; } if(NeedInput && Request.CargoLimit != -1 && FullRatio > (1.f - (1.f - Request.CargoLimit) / Station->GetLevel())) { continue; } } else if (Station->HasCapability(EFlareSpacecraftCapability::Storage)) { continue; } if(Station->GetActiveCargoBay()->WantBuy(Request.Resource, ClientCompany)) { UnloadMaxQuantity = StationFreeSpace; UnloadMaxQuantity = FMath::Min(UnloadMaxQuantity , AvailableQuantity); } if(Station->GetActiveCargoBay()->WantSell(Request.Resource, ClientCompany)) { LoadMaxQuantity = StationResourceQuantity; LoadMaxQuantity = FMath::Min(LoadMaxQuantity , FreeSpace); } if(Station->GetCompany() == Request.Client->GetCompany()) { Score += UnloadMaxQuantity * UnloadQuantityScoreMultiplier; Score += LoadMaxQuantity * LoadQuantityScoreMultiplier; } else { FFlareResourceUsage ResourceUsage = Station->GetResourceUseType(Request.Resource); int32 ResourcePrice = 0; if(NeedInput) { ResourcePrice = Sector->GetTransfertResourcePrice(NULL, Station, Request.Resource); } else { ResourcePrice = Sector->GetTransfertResourcePrice(Station, NULL, Request.Resource); } uint32 MaxBuyableQuantity = Request.Client->GetCompany()->GetMoney() / SectorHelper::GetBuyResourcePrice(Sector, Request.Resource, ResourceUsage); LoadMaxQuantity = FMath::Min(LoadMaxQuantity , MaxBuyableQuantity); uint32 MaxSellableQuantity = Station->GetCompany()->GetMoney() / SectorHelper::GetSellResourcePrice(Sector, Request.Resource, ResourceUsage); UnloadMaxQuantity = FMath::Min(UnloadMaxQuantity , MaxSellableQuantity); Score += UnloadMaxQuantity * SellQuantityScoreMultiplier; Score += LoadMaxQuantity * BuyQuantityScoreMultiplier; } Score *= 1 + (FullRatio * FullRatioBonus) + (EmptyRatio * EmptyRatioBonus); if(Station->IsUnderConstruction()) { Score *= 10000; /*FLOGV("Station %s is under construction. Score %f, BestScore %f", *Station->GetImmatriculation().ToString(), Score, BestScore)*/ } else if(Station->HasCapability(EFlareSpacecraftCapability::Storage)) { Score *= 0.01; } if(Score > 0 && Score > BestScore) { BestScore = Score; BestStation = Station; } } //FLOGV("FindTradeStation result %p", BestStation); return BestStation; }