// Copyright Epic Games, Inc. All Rights Reserved. #include "MassEntityEQSSpawnPointsGenerator.h" #include "MassSpawnerTypes.h" #include "MassSpawnLocationProcessor.h" #include "VisualLogger/VisualLogger.h" #include "MassGameplaySettings.h" #include "MassCommonUtils.h" UMassEntityEQSSpawnPointsGenerator::UMassEntityEQSSpawnPointsGenerator() { EQSRequest.RunMode = EEnvQueryRunMode::AllMatching; } void UMassEntityEQSSpawnPointsGenerator::Generate(UObject& QueryOwner, TConstArrayView EntityTypes, const int32 Count, FFinishedGeneratingSpawnDataSignature& FinishedGeneratingSpawnPointsDelegate) const { if (Count <= 0) { FinishedGeneratingSpawnPointsDelegate.Execute(TArray()); return; } // Need to copy the request as it is called inside a CDO and CDO states cannot be changed. FEQSParametrizedQueryExecutionRequest EQSRequestInstanced = EQSRequest; if (EQSRequestInstanced.IsValid() == false) { EQSRequestInstanced.InitForOwnerAndBlackboard(QueryOwner, /*BBAsset=*/nullptr); if (!ensureMsgf(EQSRequestInstanced.IsValid(), TEXT("Query request initialization can fail due to missing parameters. See the runtime log for details"))) { return; } } // Build array of entity types to spawn. // @todo: I dont like that this get's passed by value to OnEQSQueryFinished, but seemed like the cleanest solution. TArray Results; BuildResultsFromEntityTypes(Count, EntityTypes, Results); FQueryFinishedSignature Delegate = FQueryFinishedSignature::CreateUObject(this, &UMassEntityEQSSpawnPointsGenerator::OnEQSQueryFinished, Results, FinishedGeneratingSpawnPointsDelegate); EQSRequestInstanced.Execute(QueryOwner, /*BlackboardComponent=*/nullptr, Delegate); } void UMassEntityEQSSpawnPointsGenerator::OnEQSQueryFinished(TSharedPtr EQSResult, TArray Results, FFinishedGeneratingSpawnDataSignature FinishedGeneratingSpawnPointsDelegate) const { if (EQSResult.IsValid() == false || EQSResult->IsSuccessful() == false) { UE_VLOG_UELOG(this, LogMassSpawner, Error, TEXT("EQS query failed or result is invalid")); // Return empty result. Results.Reset(); FinishedGeneratingSpawnPointsDelegate.Execute(Results); return; } TArray Locations; EQSResult->GetAllAsLocations(Locations); // Randomize them FRandomStream RandomStream(UE::Mass::Utils::OverrideRandomSeedForTesting(GetRandomSelectionSeed())); for (int32 I = 0; I < Locations.Num(); ++I) { const int32 J = RandomStream.RandHelper(Locations.Num()); Locations.Swap(I, J); } const int32 LocationCount = Locations.Num(); int32 LocationIndex = 0; // Distribute points amongst the entities to spawn. for (FMassEntitySpawnDataGeneratorResult& Result : Results) { Result.SpawnDataProcessor = UMassSpawnLocationProcessor::StaticClass(); Result.SpawnData.InitializeAs(); FMassTransformsSpawnData& Transforms = Result.SpawnData.GetMutable(); Transforms.Transforms.Reserve(Result.NumEntities); for (int i = 0; i < Result.NumEntities; i++) { FTransform& Transform = Transforms.Transforms.AddDefaulted_GetRef(); Transform.SetLocation(Locations[LocationIndex % LocationCount]); LocationIndex++; } } #if ENABLE_VISUAL_LOG UE_VLOG(this, LogMassSpawner, Log, TEXT("Spawning at %d locations"), LocationIndex); if (GetDefault()->bLogSpawnLocations) { FVisualLogger::Get().ExecuteOnLastEntryForObject(this, [LocationIndex, &Results](FVisualLogEntry& LogEntry) { FVisualLogShapeElement Element(TEXT(""), FColor::Orange, /*Thickness*/20, LogMassSpawner.GetCategoryName()); Element.Points.Reserve(LocationIndex); for (const FMassEntitySpawnDataGeneratorResult& Result : Results) { const FMassTransformsSpawnData& Transforms = Result.SpawnData.Get(); for (int i = 0; i < Result.NumEntities; i++) { Element.Points.Add(Transforms.Transforms[i].GetLocation()); } } Element.Type = EVisualLoggerShapeElement::SinglePoint; Element.Verbosity = ELogVerbosity::Display; LogEntry.AddElement(Element); }); } #endif // ENABLE_VISUAL_LOG FinishedGeneratingSpawnPointsDelegate.Execute(Results); } #if WITH_EDITOR void UMassEntityEQSSpawnPointsGenerator::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) { static const FName EQSRequestName = GET_MEMBER_NAME_CHECKED(UMassEntityEQSSpawnPointsGenerator, EQSRequest); Super::PostEditChangeProperty(PropertyChangedEvent); if (PropertyChangedEvent.MemberProperty && PropertyChangedEvent.MemberProperty->GetFName() == EQSRequestName) { EQSRequest.PostEditChangeProperty(*this, PropertyChangedEvent); } } #endif