// Copyright Pacificao. All Rights Reserved. #include "AgrarianWildlifeSpawnManager.h" #include "AgrarianWildlifeBase.h" #include "NavigationSystem.h" #include "Net/UnrealNetwork.h" AAgrarianWildlifeSpawnManager::AAgrarianWildlifeSpawnManager() { PrimaryActorTick.bCanEverTick = true; bReplicates = true; SetNetCullDistanceSquared(FMath::Square(8000.0f)); SetReplicatingMovement(false); } void AAgrarianWildlifeSpawnManager::BeginPlay() { Super::BeginPlay(); SpawnTimer = SpawnIntervalSeconds; if (HasAuthority() && bSpawnOnBeginPlay) { SpawnInitialWildlife(); } } void AAgrarianWildlifeSpawnManager::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); if (!HasAuthority() || !WildlifeClass) { return; } RemoveInvalidSpawnedWildlife(); if (GetActiveWildlifeCount() >= MaxActiveWildlife) { return; } SpawnTimer -= DeltaSeconds; if (SpawnTimer <= 0.0f) { SpawnTimer = SpawnIntervalSeconds; SpawnWildlife(); } } void AAgrarianWildlifeSpawnManager::GetLifetimeReplicatedProps(TArray& OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); DOREPLIFETIME(AAgrarianWildlifeSpawnManager, SpawnedWildlife); } AAgrarianWildlifeBase* AAgrarianWildlifeSpawnManager::SpawnWildlife() { if (!HasAuthority() || !WildlifeClass || GetActiveWildlifeCount() >= MaxActiveWildlife) { return nullptr; } FVector SpawnLocation = GetActorLocation(); if (!ChooseSpawnLocation(SpawnLocation)) { return nullptr; } FActorSpawnParameters SpawnParameters; SpawnParameters.Owner = this; SpawnParameters.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; AAgrarianWildlifeBase* SpawnedActor = GetWorld()->SpawnActor( WildlifeClass, SpawnLocation, FRotator::ZeroRotator, SpawnParameters); if (SpawnedActor) { SpawnedWildlife.Add(SpawnedActor); } return SpawnedActor; } int32 AAgrarianWildlifeSpawnManager::GetActiveWildlifeCount() const { int32 ActiveCount = 0; for (AAgrarianWildlifeBase* Wildlife : SpawnedWildlife) { if (IsValid(Wildlife)) { ++ActiveCount; } } return ActiveCount; } void AAgrarianWildlifeSpawnManager::SpawnInitialWildlife() { const int32 TargetCount = FMath::Min(InitialSpawnCount, MaxActiveWildlife); for (int32 SpawnIndex = GetActiveWildlifeCount(); SpawnIndex < TargetCount; ++SpawnIndex) { if (!SpawnWildlife()) { break; } } } bool AAgrarianWildlifeSpawnManager::ChooseSpawnLocation(FVector& OutLocation) const { const FVector RandomOffset( FMath::FRandRange(-SpawnRadius, SpawnRadius), FMath::FRandRange(-SpawnRadius, SpawnRadius), 0.0f); const FVector CandidateLocation = GetActorLocation() + RandomOffset; if (!bProjectSpawnsToNavigation) { OutLocation = CandidateLocation; return true; } const UWorld* World = GetWorld(); const UNavigationSystemV1* NavigationSystem = World ? FNavigationSystem::GetCurrent(World) : nullptr; if (!NavigationSystem) { OutLocation = CandidateLocation; return true; } FNavLocation ProjectedLocation; if (!NavigationSystem->ProjectPointToNavigation(CandidateLocation, ProjectedLocation, NavigationProjectionExtent)) { return false; } OutLocation = ProjectedLocation.Location; return true; } void AAgrarianWildlifeSpawnManager::RemoveInvalidSpawnedWildlife() { SpawnedWildlife.RemoveAll([](const TObjectPtr& Wildlife) { return !IsValid(Wildlife); }); }