diff --git a/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset b/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset index 9bb9a87..649a0e6 100644 Binary files a/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset and b/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset differ diff --git a/Content/MainMenu/MainMenuLevel.umap b/Content/MainMenu/MainMenuLevel.umap index ec2d162..533adf3 100644 Binary files a/Content/MainMenu/MainMenuLevel.umap and b/Content/MainMenu/MainMenuLevel.umap differ diff --git a/Content/Troopers/BP_Trooper.uasset b/Content/Troopers/BP_Trooper.uasset index ea7c8ca..ae5af02 100644 Binary files a/Content/Troopers/BP_Trooper.uasset and b/Content/Troopers/BP_Trooper.uasset differ diff --git a/Content/Troopers/TrooperSkeletonMelee.uasset b/Content/Troopers/TrooperSkeletonMelee.uasset index 7f8be99..03c4572 100644 Binary files a/Content/Troopers/TrooperSkeletonMelee.uasset and b/Content/Troopers/TrooperSkeletonMelee.uasset differ diff --git a/Content/Troopers/TrooperWizard.uasset b/Content/Troopers/TrooperWizard.uasset index 89142b8..a204267 100644 Binary files a/Content/Troopers/TrooperWizard.uasset and b/Content/Troopers/TrooperWizard.uasset differ diff --git a/Source/TurnBasedTutorial/EnemyAIController.cpp b/Source/TurnBasedTutorial/EnemyAIController.cpp index 40087bb..2456825 100644 --- a/Source/TurnBasedTutorial/EnemyAIController.cpp +++ b/Source/TurnBasedTutorial/EnemyAIController.cpp @@ -8,7 +8,7 @@ AEnemyAIController::AEnemyAIController() : Super() { PrimaryActorTick.bCanEverTick = true; - SetActorTickInterval(0.3f); + SetActorTickInterval(2.0f); } void AEnemyAIController::Tick(float DeltaSeconds) { @@ -44,6 +44,67 @@ bool AEnemyAIController::IsAITurn() const { return bIsAITurn; } +void AEnemyAIController::SpawnIfNeeded() { + RemoveDeadTroopers(); + while (PossessedTroopers.Num() < MAX_TROOPERS_COUNT) { + const FVector Location = GetFreeLocation(); + const FVector Rotation = {0.0f, 0.0f, 0.0f}; + FTransform SpawnLocationAndRotation(Rotation); + SpawnLocationAndRotation.SetLocation(Location); + const TArray> &LoadedTroopersAssets = GetWorld()-> + GetGameState()->GetTroopersAssets(); + AActor *Spawned = GetWorld()->SpawnActorDeferred( + LoadedTroopersAssets[FMath::RandRange( + 0, LoadedTroopersAssets.Num() - 1)], + SpawnLocationAndRotation); + Cast(Spawned)->Initialize( + 1, Location, TroopersCount++); + Spawned->FinishSpawning(SpawnLocationAndRotation); + Spawned->SetActorLocation(Location); + ATrooper *Trooper = Cast(Spawned); + GetWorld()->GetGameState()->AddTrooper(Trooper); + PossessedTroopers.Add(Trooper); + Trooper->SetAIPossession(this); + Trooper->HighlightAsEnemy(PLAYER_INDEX); + } +} + +// void AEnemyAIController::SetTrooperAssetsAndSpawn( +// TArray TrooperAssets, +// int TrooperCount) { +// LoadedTrooperAssets = MoveTemp(TrooperAssets); +// TroopersCount = TrooperCount; +// SpawnIfNeeded(); +// } + +void AEnemyAIController::RemoveDeadTroopers() { + for (int index = 0; index < PossessedTroopers.Num(); ++index) { + if (!PossessedTroopers[index] || !PossessedTroopers[index]-> + IsValidLowLevel() || + PossessedTroopers[index]->IsDead()) { + PossessedTroopers.RemoveAtSwap(index); + index--; + } + } +} + +FVector AEnemyAIController::GetFreeLocation() const { + for (const auto Location : SpawnPoints) { + bool bIsClose = false; + for (const auto Trooper : PossessedTroopers) { + if ((Location - Trooper->GetLocation()).Size() < 100.0f) { + bIsClose = true; + break; + } + } + if (bIsClose) { + continue; + } + return Location; + } + return {-2000.0f, FMath::RandRange(-2000.0f, 2000.0f), 0.0f}; +} + void AEnemyAIController::MakeMove() { while (TroopersCursor < PossessedTroopers.Num()) { while (TroopersCursor < PossessedTroopers.Num() && ( @@ -111,6 +172,17 @@ bool AEnemyAIController::MoveTo(int TrooperIndex) { return true; } +void AEnemyAIController::InitializeSpawnPoints() { + SpawnPoints.Add(FVector{ + -2000.0f, + -(MAX_TROOPERS_COUNT - 1) * TROOPERS_DISTANCE.Y / 2, + 0.0f + }); + for (int index = 1; index < MAX_TROOPERS_COUNT; ++index) { + SpawnPoints.Add(SpawnPoints[SpawnPoints.Num() - 1] + TROOPERS_DISTANCE); + } +} + int AEnemyAIController::GetClosestTrooper() const { float minDistance = 1000000.0f; int minIndex = 0; @@ -142,6 +214,8 @@ void AEnemyAIController::InitializeTroopers( } } } + InitializeSpawnPoints(); + SpawnIfNeeded(); } void AEnemyAIController::BeginPlay() { diff --git a/Source/TurnBasedTutorial/EnemyAIController.h b/Source/TurnBasedTutorial/EnemyAIController.h index 53162f8..5f8ed75 100644 --- a/Source/TurnBasedTutorial/EnemyAIController.h +++ b/Source/TurnBasedTutorial/EnemyAIController.h @@ -13,8 +13,6 @@ class TURNBASEDTUTORIAL_API AEnemyAIController : public AActor { GENERATED_BODY() public: - static constexpr int AI_INDEX = 1; - AEnemyAIController(); UFUNCTION() @@ -39,7 +37,30 @@ public: UFUNCTION() bool IsAITurn() const; + UFUNCTION() + void SpawnIfNeeded(); + + // UFUNCTION() + // void SetTrooperAssetsAndSpawn(TArray TrooperAssets, int TrooperCount); + private: + static constexpr int AI_INDEX = 1; + + static constexpr int PLAYER_INDEX = 0; + + static constexpr int MAX_TROOPERS_COUNT = 3; + + UPROPERTY() + int TroopersCount = 5; + + // UPROPERTY() + // TArray LoadedTrooperAssets; + + UFUNCTION() + void RemoveDeadTroopers(); + + FVector GetFreeLocation() const; + UFUNCTION() void MakeMove(); @@ -53,6 +74,8 @@ private: bool TryAttack(int TrooperIndex); bool MoveTo(int TrooperIndex); + + void InitializeSpawnPoints(); UPROPERTY() bool bIsAITurn = false; @@ -65,4 +88,8 @@ private: UPROPERTY() TArray PlayerTroopers; + + TArray SpawnPoints; + + const FVector TROOPERS_DISTANCE = {0.0f, 1000.0f, 0.0f}; }; diff --git a/Source/TurnBasedTutorial/MyGameMode.cpp b/Source/TurnBasedTutorial/MyGameMode.cpp index 1de3aef..ef8dd3b 100644 --- a/Source/TurnBasedTutorial/MyGameMode.cpp +++ b/Source/TurnBasedTutorial/MyGameMode.cpp @@ -6,6 +6,7 @@ #include "MyGameState.h" #include "MyPlayerController.h" #include "MyPlayerState.h" +#include "SinglePlayerGS.h" #include "Trooper.h" AMyGameMode::AMyGameMode() @@ -26,7 +27,7 @@ auto AMyGameMode::GetMyGameState() const { } -void AMyGameMode::InitializeBattleField_Implementation() const { +void AMyGameMode::InitializeBattleField_Implementation() { UE_LOG(LogTemp, Warning, TEXT("InitializeBattleField")); FVector Location(2000.0f, -1000.0f, 0.0f); FRotator Rotation(0.0f, 180.0f, 0.0f); @@ -39,7 +40,7 @@ void AMyGameMode::InitializeBattleField_Implementation() const { ), TEXT("Blueprint'/Game/Troopers/TrooperWizard.TrooperWizard_C'") }; - TArray LoadedBpAssets; + // TArray LoadedBpAssets; for (int i = 0; i < bpPaths.Num(); ++i) { TSoftClassPtr ActorBpClass = TSoftClassPtr( FSoftObjectPath(bpPaths[i]) @@ -61,25 +62,31 @@ void AMyGameMode::InitializeBattleField_Implementation() const { GetMyGameState()->AddTrooper(Cast(Spawned)); Location += {0.f, 500.f, 0.0f}; } - Location = {-2000.0f, -1000.0f, 0.0f}; - Rotation = {0.0f, 0.0f, 0.0f}; - for (int i = 0; i < 5; ++i) { - FTransform SpawnLocationAndRotation(Rotation); - SpawnLocationAndRotation.SetLocation(Location); - AActor *Spawned = GetWorld()->SpawnActorDeferred( - LoadedBpAssets[i % 2], SpawnLocationAndRotation); - // AActor *Spawned = GetWorld()->SpawnActorDeferred( - // ATrooper::StaticClass(), SpawnLocationAndRotation); - Cast(Spawned)->Initialize( - 1, Location, TrooperCount++); - Spawned->FinishSpawning(SpawnLocationAndRotation); - Spawned->SetActorLocation(Location); - GetMyGameState()->AddTrooper(Cast(Spawned)); - Location += {0.f, 500.f, 0.0f}; + if (bIsMultiplayer) { + Location = {-2000.0f, -1000.0f, 0.0f}; + Rotation = {0.0f, 0.0f, 0.0f}; + for (int i = 0; i < 5; ++i) { + FTransform SpawnLocationAndRotation(Rotation); + SpawnLocationAndRotation.SetLocation(Location); + AActor *Spawned = GetWorld()->SpawnActorDeferred( + LoadedBpAssets[i % 2], SpawnLocationAndRotation); + // AActor *Spawned = GetWorld()->SpawnActorDeferred( + // ATrooper::StaticClass(), SpawnLocationAndRotation); + Cast(Spawned)->Initialize( + 1, Location, TrooperCount++); + Spawned->FinishSpawning(SpawnLocationAndRotation); + Spawned->SetActorLocation(Location); + GetMyGameState()->AddTrooper(Cast(Spawned)); + Location += {0.f, 500.f, 0.0f}; + } + } else { + // Cast(GetMyGameState())->GetEnemyAIController()-> + // SetTrooperAssetsAndSpawn( + // LoadedBpAssets, + // TrooperCount); } } - AActor *AMyGameMode::ChoosePlayerStart_Implementation(AController *Player) { UE_LOG(LogTemp, Warning, TEXT("GameMode ChoosePlayerStart %d"), GetNumPlayers()); @@ -151,7 +158,7 @@ void AMyGameMode::StartGame_Implementation() { InitializeBattleField(); // PlayerNotInTurn()->SetEnemySelection(Troopers); // PlayerInTurn()->SetEnemySelection(Troopers); - + // PlayerInTurn()->StartTurn(); GetMyGameState()->StartGame(); } @@ -206,4 +213,3 @@ void AMyGameMode::StartGame_Implementation() { // return Cast( // UGameplayStatics::GetPlayerController(GetWorld(), PlayerIndex)); // } - diff --git a/Source/TurnBasedTutorial/MyGameMode.h b/Source/TurnBasedTutorial/MyGameMode.h index 87e26f1..dbff953 100644 --- a/Source/TurnBasedTutorial/MyGameMode.h +++ b/Source/TurnBasedTutorial/MyGameMode.h @@ -28,10 +28,16 @@ public: // void CycleTurns(); protected: + UPROPERTY() + TArray LoadedBpAssets; + + UPROPERTY() + bool bIsMultiplayer = true; + void InitializeSpawnPointsIfNeeded(AController *Player); UFUNCTION(Server, Reliable) - void InitializeBattleField() const; + void InitializeBattleField(); UPROPERTY() TMap SpawnPoints{}; diff --git a/Source/TurnBasedTutorial/MyGameState.cpp b/Source/TurnBasedTutorial/MyGameState.cpp index 7d212d1..ee2bed3 100644 --- a/Source/TurnBasedTutorial/MyGameState.cpp +++ b/Source/TurnBasedTutorial/MyGameState.cpp @@ -84,6 +84,9 @@ void AMyGameState::DecreaseLivingTroopers(int PlayerIndex) { if (bGameIsOver) return; LivingTroopersCount[PlayerIndex]--; + if (!bIsMultiplayer && PlayerIndex == 1) { + return; + } if (LivingTroopersCount[PlayerIndex] <= 0) { UE_LOG(LogTemp, Warning, TEXT("Player %d lose!"), PlayerIndex); bGameIsOver = true; diff --git a/Source/TurnBasedTutorial/MyGameState.h b/Source/TurnBasedTutorial/MyGameState.h index f072a64..00817bb 100644 --- a/Source/TurnBasedTutorial/MyGameState.h +++ b/Source/TurnBasedTutorial/MyGameState.h @@ -48,7 +48,7 @@ public: protected: UPROPERTY() - bool IsMultiplayer = true; + bool bIsMultiplayer = true; UPROPERTY(Replicated) bool bGameIsOver = false; diff --git a/Source/TurnBasedTutorial/SinglePlayerGM.cpp b/Source/TurnBasedTutorial/SinglePlayerGM.cpp index 956b77b..e2372c9 100644 --- a/Source/TurnBasedTutorial/SinglePlayerGM.cpp +++ b/Source/TurnBasedTutorial/SinglePlayerGM.cpp @@ -5,6 +5,7 @@ ASinglePlayerGM::ASinglePlayerGM() : Super() { GameStateClass = ASinglePlayerGS::StaticClass(); + bIsMultiplayer = false; } void ASinglePlayerGM::BeginPlay() { diff --git a/Source/TurnBasedTutorial/SinglePlayerGS.cpp b/Source/TurnBasedTutorial/SinglePlayerGS.cpp index 9f929fc..3b5e1d0 100644 --- a/Source/TurnBasedTutorial/SinglePlayerGS.cpp +++ b/Source/TurnBasedTutorial/SinglePlayerGS.cpp @@ -5,7 +5,7 @@ ASinglePlayerGS::ASinglePlayerGS() : Super() { - IsMultiplayer = false; + bIsMultiplayer = false; PrimaryActorTick.bCanEverTick = true; SetActorTickInterval(0.5f); } @@ -43,12 +43,22 @@ void ASinglePlayerGS::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); if (EnemyAiManager->bIsEnded) { EnemyAiManager->bIsEnded = false; + EnemyAiManager->SpawnIfNeeded(); CycleTurns(); } else if (CurrentPlayerTurn == 1 && !EnemyAiManager->IsAITurn()) { CycleTurns(); } } +AEnemyAIController * ASinglePlayerGS::GetEnemyAIController() const { + return EnemyAiManager; +} + +const TArray> & ASinglePlayerGS:: +GetTroopersAssets() const { + return TrooperBpAssets; +} + void ASinglePlayerGS::GetLifetimeReplicatedProps( TArray &OutLifetimeProps) const { Super::GetLifetimeReplicatedProps(OutLifetimeProps); diff --git a/Source/TurnBasedTutorial/SinglePlayerGS.h b/Source/TurnBasedTutorial/SinglePlayerGS.h index 8d575be..ba86e8b 100644 --- a/Source/TurnBasedTutorial/SinglePlayerGS.h +++ b/Source/TurnBasedTutorial/SinglePlayerGS.h @@ -23,7 +23,14 @@ public: virtual void Tick(float DeltaSeconds) override; + AEnemyAIController *GetEnemyAIController() const; + + const TArray> &GetTroopersAssets() const; + protected: UPROPERTY(Replicated) AEnemyAIController *EnemyAiManager = nullptr; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + TArray> TrooperBpAssets; };