// Fill out your copyright notice in the Description page of Project Settings. #include "MGameMode.h" #include "Kismet/GameplayStatics.h" #include "MPlayerController.h" #include "EngineUtils.h" #include "GameFramework/Character.h" #include "MAICharacterBase.h" #include "GameplayTagsManager.h" #include "MHealthComponent.h" AMGameMode::AMGameMode() { PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.TickInterval = 1.f; WaveCount = 0; LevelEndViewTargetBlendTime = 2.f; TimeBetweenWaves = 60.f; NumEnemiesKilled = 0; MaxEnemiesInWave = 10; MinEnemiesInWave = 3; } void AMGameMode::StartPlay() { Super::StartPlay(); PrepareForNextWave(); } void AMGameMode::HandlePlayerKilled() { GameOver(); } void AMGameMode::HandlePlayerVehicleDestroyed(AActor* Vehicle) { GameOver(); } void AMGameMode::GameOver() { if(SpectatingViewPointClass) { TArray ReturnedActors; UGameplayStatics::GetAllActorsOfClass(this, SpectatingViewPointClass, ReturnedActors); AActor* NewViewTarget = nullptr; if(ReturnedActors.Num() > 0) { NewViewTarget = ReturnedActors[0]; for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; It++) { AMPlayerController* PC = Cast(It->Get()); if(PC) { PC->SetViewTargetWithBlend(NewViewTarget, LevelEndViewTargetBlendTime, EViewTargetBlendFunction::VTBlend_Cubic); PC->OnGameOver(); SetWaveState(EWaveState::GameOver); } } } } } void AMGameMode::LevelCompleted() { if(SpectatingViewPointClass) { TArray ReturnedActors; UGameplayStatics::GetAllActorsOfClass(this, SpectatingViewPointClass, ReturnedActors); AActor* NewViewTarget = nullptr; if(ReturnedActors.Num() > 0) { NewViewTarget = ReturnedActors[0]; for(FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; It++) { AMPlayerController* PC = Cast(It->Get()); if(PC) { PC->SetViewTargetWithBlend(NewViewTarget, LevelEndViewTargetBlendTime, EViewTargetBlendFunction::VTBlend_Cubic); PC->OnLevelComplete(); SetWaveState(EWaveState::LevelComplete); } } } } } void AMGameMode::PrepareForNextWave() { GetWorldTimerManager().SetTimer(TimerHandle_NextWaveStart, this, &AMGameMode::StartWave, TimeBetweenWaves, false); SetWaveState(EWaveState::WaitingToStart); } void AMGameMode::StartWave() { FTimerManager& TimerManager = GetWorldTimerManager(); // Cancel Timer if it is still active. This needs to be done if // the wave is triggered manually by a console command to make sure wave isn't // triggered twice if(TimerManager.IsTimerActive(TimerHandle_NextWaveStart)) TimerManager.ClearTimer(TimerHandle_NextWaveStart); WaveCount++; NumEnemiesToSpawn = FMath::RandRange(MinEnemiesInWave, MaxEnemiesInWave); NumEnemiesToSpawn = WaveCount * NumEnemiesToSpawn; TimerManager.SetTimer(TimerHandle_EnemySpawn, this, &AMGameMode::SpawnEnemyTimerElapsed, 1.f, true, 0.f); SetWaveState(EWaveState::WaveInProgress); UE_LOG(LogTemp, Log, TEXT("Enemies to spawn %d"), NumEnemiesToSpawn); } void AMGameMode::EndWave() { GetWorldTimerManager().ClearTimer(TimerHandle_EnemySpawn); SetWaveState(EWaveState::WaitingToComplete); } void AMGameMode::CheckWaveState() { bool IsPreparingForNextWave = GetWorldTimerManager().IsTimerActive(TimerHandle_NextWaveStart); if(NumEnemiesToSpawn > 0 || IsPreparingForNextWave) return; bool IsAnyEnemyAlive = false; for(TActorIterator It(GetWorld()); It; ++It) { AMAICharacterBase* EnemyChar = *It; if(EnemyChar->HasAllMatchingGameplayTags(SpawnedEnemyTags)) { UMHealthComponent* HealthComponent = Cast(EnemyChar->GetComponentByClass(UMHealthComponent::StaticClass())); if(HealthComponent && HealthComponent->GetHealth() > 0.f) { IsAnyEnemyAlive = true; break; } } } if(!IsAnyEnemyAlive) { SetWaveState(EWaveState::WaveComplete); PrepareForNextWave(); } } void AMGameMode::SetWaveState(EWaveState NewWaveState) { WaveStateChanged(NewWaveState, WaveState); WaveState = NewWaveState; } void AMGameMode::SpawnEnemyTimerElapsed() { SpawnNewEnemy(); NumEnemiesToSpawn--; if(NumEnemiesToSpawn <= 0) { EndWave(); } } void AMGameMode::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); CheckWaveState(); }