diff --git a/.gitignore b/.gitignore index 6582eaf..faf6bd0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Visual Studio 2015 user specific files .vs/ +.idea/ # Compiled Object files *.slo diff --git a/Config/DefaultEngine.ini b/Config/DefaultEngine.ini index 33eb9b3..c12c8fa 100644 --- a/Config/DefaultEngine.ini +++ b/Config/DefaultEngine.ini @@ -1,7 +1,7 @@ [/Script/EngineSettings.GameMapsSettings] -EditorStartupMap=/Game/BattleField/BattleFieldMap.BattleFieldMap +EditorStartupMap=/Game/MainMenu/MainMenuLevel.MainMenuLevel LocalMapOptions= TransitionMap=None bUseSplitscreen=True diff --git a/Content/BP_MyGameMode.uasset b/Content/BP_MyGameMode.uasset index f1ac113..204d94f 100644 Binary files a/Content/BP_MyGameMode.uasset and b/Content/BP_MyGameMode.uasset differ diff --git a/Content/BP_MyPawn.uasset b/Content/BP_MyPawn.uasset deleted file mode 100644 index 255d436..0000000 Binary files a/Content/BP_MyPawn.uasset and /dev/null differ diff --git a/Content/BattleField/BP_BattleUI.uasset b/Content/BattleField/BP_BattleUI.uasset index 346c66a..74315dc 100644 Binary files a/Content/BattleField/BP_BattleUI.uasset and b/Content/BattleField/BP_BattleUI.uasset differ diff --git a/Content/BattleField/BP_MyGameMode.uasset b/Content/BattleField/BP_MyGameMode.uasset new file mode 100644 index 0000000..ef3b99a Binary files /dev/null and b/Content/BattleField/BP_MyGameMode.uasset differ diff --git a/Content/BattleField/BP_MyPawn.uasset b/Content/BattleField/BP_MyPawn.uasset new file mode 100644 index 0000000..6454e04 Binary files /dev/null and b/Content/BattleField/BP_MyPawn.uasset differ diff --git a/Content/BP_MyPlayerController.uasset b/Content/BattleField/BP_MyPlayerController.uasset similarity index 75% rename from Content/BP_MyPlayerController.uasset rename to Content/BattleField/BP_MyPlayerController.uasset index 3006c01..b400662 100644 Binary files a/Content/BP_MyPlayerController.uasset and b/Content/BattleField/BP_MyPlayerController.uasset differ diff --git a/Content/BP_MyPlayerState.uasset b/Content/BattleField/BP_MyPlayerState.uasset similarity index 68% rename from Content/BP_MyPlayerState.uasset rename to Content/BattleField/BP_MyPlayerState.uasset index 6e5c02f..6aa028c 100644 Binary files a/Content/BP_MyPlayerState.uasset and b/Content/BattleField/BP_MyPlayerState.uasset differ diff --git a/Content/BattleField/Multiplayer/BP_MyGameMode.uasset b/Content/BattleField/Multiplayer/BP_MyGameMode.uasset new file mode 100644 index 0000000..a01d968 Binary files /dev/null and b/Content/BattleField/Multiplayer/BP_MyGameMode.uasset differ diff --git a/Content/BP_MyGameState.uasset b/Content/BattleField/Multiplayer/BP_MyGameState.uasset similarity index 68% rename from Content/BP_MyGameState.uasset rename to Content/BattleField/Multiplayer/BP_MyGameState.uasset index 459f885..b40b693 100644 Binary files a/Content/BP_MyGameState.uasset and b/Content/BattleField/Multiplayer/BP_MyGameState.uasset differ diff --git a/Content/BattleField/BattleFieldMap.umap b/Content/BattleField/Multiplayer/BattleFieldMap.umap similarity index 98% rename from Content/BattleField/BattleFieldMap.umap rename to Content/BattleField/Multiplayer/BattleFieldMap.umap index 250219a..9284f7d 100644 Binary files a/Content/BattleField/BattleFieldMap.umap and b/Content/BattleField/Multiplayer/BattleFieldMap.umap differ diff --git a/Content/BattleField/SinglePlayer/BP_SinglePlayerGM.uasset b/Content/BattleField/SinglePlayer/BP_SinglePlayerGM.uasset new file mode 100644 index 0000000..a9efbb5 Binary files /dev/null and b/Content/BattleField/SinglePlayer/BP_SinglePlayerGM.uasset differ diff --git a/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset b/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset new file mode 100644 index 0000000..9bb9a87 Binary files /dev/null and b/Content/BattleField/SinglePlayer/BP_SinglePlayerGS.uasset differ diff --git a/Content/BattleField/SinglePlayer/OfflineBattleFieldMap.umap b/Content/BattleField/SinglePlayer/OfflineBattleFieldMap.umap new file mode 100644 index 0000000..a70b900 Binary files /dev/null and b/Content/BattleField/SinglePlayer/OfflineBattleFieldMap.umap differ diff --git a/Content/MainMenu/MainMenu.uasset b/Content/MainMenu/MainMenu.uasset index 8a95a1f..e409e8c 100644 Binary files a/Content/MainMenu/MainMenu.uasset and b/Content/MainMenu/MainMenu.uasset differ diff --git a/Content/Troopers/BP_Trooper.uasset b/Content/Troopers/BP_Trooper.uasset index 7b9e42d..ea7c8ca 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 c8753e4..7f8be99 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 7fb6aec..89142b8 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 new file mode 100644 index 0000000..40087bb --- /dev/null +++ b/Source/TurnBasedTutorial/EnemyAIController.cpp @@ -0,0 +1,149 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "EnemyAIController.h" +#include "SinglePlayerGS.h" +#include "Kismet/GameplayStatics.h" +#include "GenericPlatform/GenericPlatformMath.h" + +AEnemyAIController::AEnemyAIController() + : Super() { + PrimaryActorTick.bCanEverTick = true; + SetActorTickInterval(0.3f); +} + +void AEnemyAIController::Tick(float DeltaSeconds) { + Super::Tick(DeltaSeconds); + if (bIsAITurn && !bIsActing) { + if (TroopersCursor >= PossessedTroopers.Num()) { + EndTurn(); + } else { + MakeMove(); + } + } +} + + +void AEnemyAIController::StartTurn() { + UE_LOG(LogTemp, Warning, TEXT("Enemy AI StartTurn")); + bIsAITurn = true; + bIsActing = false; + // MakeMove(); +} + +void AEnemyAIController::EndTurn() { + bIsAITurn = false; + TroopersCursor = 0; + bIsEnded = true; +} + +void AEnemyAIController::ActionDone() { + bIsActing = false; +} + +bool AEnemyAIController::IsAITurn() const { + return bIsAITurn; +} + +void AEnemyAIController::MakeMove() { + while (TroopersCursor < PossessedTroopers.Num()) { + while (TroopersCursor < PossessedTroopers.Num() && ( + !PossessedTroopers[TroopersCursor] || + !PossessedTroopers[TroopersCursor]->IsValidLowLevel() + || PossessedTroopers[TroopersCursor]->IsDead())) { + ++TroopersCursor; + } + if (TroopersCursor >= PossessedTroopers.Num()) { + return; + } + const int Index = GetClosestTrooper(); + bool failed; + if (!IsCloseEnough(Index)) { + failed = MoveTo(Index); + } else { + failed = TryAttack(Index); + } + if (failed) { + TroopersCursor++; + } else { + return; + } + } +} + +bool AEnemyAIController::IsCloseEnough(int TrooperIndex) const { + const ATrooper *CurrentTrooper = PossessedTroopers[TroopersCursor]; + const ATrooper *OtherTrooper = PlayerTroopers[TrooperIndex]; + return (CurrentTrooper->GetLocation() - OtherTrooper->GetLocation()).Size() + <= CurrentTrooper->GetRealActionRadius(1); +} + +bool AEnemyAIController::TryAttack(int TrooperIndex) { + ATrooper *Attacker = PossessedTroopers[TroopersCursor]; + const auto Location = PlayerTroopers[TrooperIndex]->GetLocation(); + constexpr int ActionIndex = 1; + if (Attacker->CheckAttackCorrectness(Location, ActionIndex)) { + bIsActing = true; + Attacker->Attack(ActionIndex, Location); + return false; + } + return true; +} + +bool AEnemyAIController::MoveTo(int TrooperIndex) { + ATrooper *Trooper = PossessedTroopers[TroopersCursor]; + const FVector CurrentLocation = Trooper->GetLocation(); + const FVector Destination = PlayerTroopers[TrooperIndex]->GetLocation(); + constexpr int ActionIndex = 1; + constexpr int MoveActionIndex = 0; + const float AttackRadius = Trooper->GetRealActionRadius(ActionIndex); + const FVector Vector = Destination - CurrentLocation; + const float MaxLength = Trooper->GetRealActionRadius(MoveActionIndex); + float PathLength = Vector.Size() - AttackRadius + 10.f; + PathLength = FMath::Min(PathLength, MaxLength); + FVector Location = CurrentLocation + Vector.GetSafeNormal2D() * + PathLength; + Location.Z = 0.0f; + if (PathLength > 1.0f && Trooper->CheckMoveCorrectness(Location)) { + bIsActing = true; + Trooper->MoveTrooper(Location); + return false; + } + return true; +} + +int AEnemyAIController::GetClosestTrooper() const { + float minDistance = 1000000.0f; + int minIndex = 0; + const ATrooper *CurrentTrooper = PossessedTroopers[TroopersCursor]; + for (int index = 0; index < PlayerTroopers.Num(); ++index) { + const ATrooper *OtherTrooper = PlayerTroopers[index]; + if (OtherTrooper->IsValidLowLevel() && !OtherTrooper->IsDead()) { + const float distance = ( + CurrentTrooper->GetLocation() - OtherTrooper-> + GetLocation()).Size(); + if (distance < minDistance) { + minDistance = distance; + minIndex = index; + } + } + } + return minIndex; +} + +void AEnemyAIController::InitializeTroopers( + const TArray &Troopers) { + for (const auto Trooper : Troopers) { + if (Trooper != nullptr) { + if (Trooper->GetPlayerIndex() == AI_INDEX) { + Trooper->SetAIPossession(this); + PossessedTroopers.Add(Trooper); + } else { + PlayerTroopers.Add(Trooper); + } + } + } +} + +void AEnemyAIController::BeginPlay() { + Super::BeginPlay(); +} diff --git a/Source/TurnBasedTutorial/EnemyAIController.h b/Source/TurnBasedTutorial/EnemyAIController.h new file mode 100644 index 0000000..53162f8 --- /dev/null +++ b/Source/TurnBasedTutorial/EnemyAIController.h @@ -0,0 +1,68 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "EnemyAIController.generated.h" + +/** + * + */ +UCLASS() +class TURNBASEDTUTORIAL_API AEnemyAIController : public AActor { + GENERATED_BODY() + +public: + static constexpr int AI_INDEX = 1; + + AEnemyAIController(); + + UFUNCTION() + void StartTurn(); + + UFUNCTION() + void EndTurn(); + + UFUNCTION() + void ActionDone(); + + UFUNCTION() + void InitializeTroopers(const TArray &Troopers); + + virtual void BeginPlay() override; + + virtual void Tick(float DeltaSeconds) override; + + UPROPERTY() + bool bIsEnded = false; + + UFUNCTION() + bool IsAITurn() const; + +private: + UFUNCTION() + void MakeMove(); + + UPROPERTY() + int TroopersCursor = 0; + + int GetClosestTrooper() const; + + bool IsCloseEnough(int TrooperIndex) const; + + bool TryAttack(int TrooperIndex); + + bool MoveTo(int TrooperIndex); + + UPROPERTY() + bool bIsAITurn = false; + + UPROPERTY() + bool bIsActing = false; + + UPROPERTY() + TArray PossessedTroopers; + + UPROPERTY() + TArray PlayerTroopers; +}; diff --git a/Source/TurnBasedTutorial/MyExplosion.cpp b/Source/TurnBasedTutorial/MyExplosion.cpp index 4d3caf1..0398377 100644 --- a/Source/TurnBasedTutorial/MyExplosion.cpp +++ b/Source/TurnBasedTutorial/MyExplosion.cpp @@ -4,7 +4,6 @@ #include "MyExplosion.h" #include "Trooper.h" -#include "Components/SphereComponent.h" #include "Net/UnrealNetwork.h" #include "Particles/ParticleSystemComponent.h" @@ -51,7 +50,7 @@ void AMyExplosion::NotifyActorBeginOverlap(AActor *OtherActor) { PlayerIndex); if (PlayerIndex != -1 && PlayerIndex != OtherTrooper-> GetPlayerIndex()) { - OtherTrooper->TakeDamage(Damage); + OtherTrooper->TrooperTakeDamage(Damage); } } else { UE_LOG(LogTemp, Warning, TEXT("Overlapped not a trooper")); diff --git a/Source/TurnBasedTutorial/MyExplosion.h b/Source/TurnBasedTutorial/MyExplosion.h index 535cf0f..38f45a5 100644 --- a/Source/TurnBasedTutorial/MyExplosion.h +++ b/Source/TurnBasedTutorial/MyExplosion.h @@ -2,7 +2,6 @@ #pragma once -#include "Components/SphereComponent.h" #include "CoreMinimal.h" #include "GameFramework/Actor.h" #include "MyExplosion.generated.h" diff --git a/Source/TurnBasedTutorial/MyGameMode.h b/Source/TurnBasedTutorial/MyGameMode.h index 7e68aa4..87e26f1 100644 --- a/Source/TurnBasedTutorial/MyGameMode.h +++ b/Source/TurnBasedTutorial/MyGameMode.h @@ -27,7 +27,7 @@ public: // UFUNCTION(BlueprintCallable) // void CycleTurns(); -private: +protected: void InitializeSpawnPointsIfNeeded(AController *Player); UFUNCTION(Server, Reliable) diff --git a/Source/TurnBasedTutorial/MyGameState.cpp b/Source/TurnBasedTutorial/MyGameState.cpp index e219a74..7d212d1 100644 --- a/Source/TurnBasedTutorial/MyGameState.cpp +++ b/Source/TurnBasedTutorial/MyGameState.cpp @@ -10,20 +10,27 @@ auto AMyGameState::GetMyPlayerState(uint8 PlayerIndex) const { return Cast(PlayerArray[PlayerIndex]); } +AMyGameState::AMyGameState() : Super() { +} + void AMyGameState::BeginPlay() { Super::BeginPlay(); - LivingTroopers.SetNum(2); + LivingTroopersCount.SetNum(2); } void AMyGameState::AddTrooper_Implementation(ATrooper *Trooper) { - if (Trooper->GetPlayerIndex() >= 0 && Trooper->GetPlayerIndex() <= LivingTroopers.Num()) { - LivingTroopers[Trooper->GetPlayerIndex()]++; + if (Trooper->GetPlayerIndex() >= 0 && Trooper->GetPlayerIndex() <= + LivingTroopersCount.Num()) { + if (LivingTroopersCount.Num() < 2) { + LivingTroopersCount.SetNum(2); + } + LivingTroopersCount[Trooper->GetPlayerIndex()]++; } Troopers.Add(Trooper); } void AMyGameState::StartGame_Implementation() { - PlayerNotInTurn()->SetEnemySelection(); + // PlayerNotInTurn()->SetEnemySelection(); PlayerInTurn()->SetEnemySelection(); bGameStarted = true; PlayerInTurn()->StartTurn(); @@ -75,9 +82,9 @@ bool AMyGameState::IsGameStarted() const { void AMyGameState::DecreaseLivingTroopers(int PlayerIndex) { if (bGameIsOver) - return; - LivingTroopers[PlayerIndex]--; - if (LivingTroopers[PlayerIndex] <= 0) { + return; + LivingTroopersCount[PlayerIndex]--; + 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 1b4bf8a..f072a64 100644 --- a/Source/TurnBasedTutorial/MyGameState.h +++ b/Source/TurnBasedTutorial/MyGameState.h @@ -15,16 +15,18 @@ class TURNBASEDTUTORIAL_API AMyGameState : public AGameState { GENERATED_BODY() public: + AMyGameState(); + virtual void BeginPlay() override; UFUNCTION(Server, Reliable) - void AddTrooper(ATrooper *Trooper); + virtual void AddTrooper(ATrooper *Trooper); UFUNCTION(Server, Reliable) void StartGame(); UFUNCTION(BlueprintCallable, Server, Reliable) - void CycleTurns(); + virtual void CycleTurns(); UFUNCTION(BlueprintPure) AMyPlayerState *PlayerInTurn() const; @@ -44,7 +46,10 @@ public: UFUNCTION() void DecreaseLivingTroopers(int PlayerIndex); -private: +protected: + UPROPERTY() + bool IsMultiplayer = true; + UPROPERTY(Replicated) bool bGameIsOver = false; @@ -52,7 +57,7 @@ private: TArray Troopers; UPROPERTY(Replicated) - TArray LivingTroopers; + TArray LivingTroopersCount; UPROPERTY(Replicated) bool bGameStarted = false; diff --git a/Source/TurnBasedTutorial/MyPawn.cpp b/Source/TurnBasedTutorial/MyPawn.cpp index 22cccff..b8698e3 100644 --- a/Source/TurnBasedTutorial/MyPawn.cpp +++ b/Source/TurnBasedTutorial/MyPawn.cpp @@ -29,11 +29,6 @@ void AMyPawn::MoveForward(float Val) { } } -// Called every frame -void AMyPawn::Tick(float DeltaTime) { - Super::Tick(DeltaTime); -} - // Called to bind functionality to input void AMyPawn::SetupPlayerInputComponent(UInputComponent *PlayerInputComponent) { Super::SetupPlayerInputComponent(PlayerInputComponent); diff --git a/Source/TurnBasedTutorial/MyPawn.h b/Source/TurnBasedTutorial/MyPawn.h index ad9b445..c8ba2ea 100644 --- a/Source/TurnBasedTutorial/MyPawn.h +++ b/Source/TurnBasedTutorial/MyPawn.h @@ -22,8 +22,6 @@ protected: virtual void MoveForward(float Val) override; public: - // Called every frame - virtual void Tick(float DeltaTime) override; // Called to bind functionality to input virtual void SetupPlayerInputComponent( diff --git a/Source/TurnBasedTutorial/MyPlayerController.cpp b/Source/TurnBasedTutorial/MyPlayerController.cpp index 3f7332b..508ee1a 100644 --- a/Source/TurnBasedTutorial/MyPlayerController.cpp +++ b/Source/TurnBasedTutorial/MyPlayerController.cpp @@ -12,6 +12,7 @@ AMyPlayerController::AMyPlayerController() : Super()/*, bIsMyTurn(false), SelectedTrooper(nullptr)*/ { UE_LOG(LogTemp, Warning, TEXT("Player controller created")); SetShowMouseCursor(true); + PlayerIndex = 0; } void AMyPlayerController::BeginPlay() { diff --git a/Source/TurnBasedTutorial/MyPlayerState.cpp b/Source/TurnBasedTutorial/MyPlayerState.cpp index 179fbff..ca50779 100644 --- a/Source/TurnBasedTutorial/MyPlayerState.cpp +++ b/Source/TurnBasedTutorial/MyPlayerState.cpp @@ -1,8 +1,6 @@ // Fill out your copyright notice in the Description page of Project Settings. - #include "MyPlayerState.h" - #include "MyGameState.h" #include "Kismet/GameplayStatics.h" #include "Net/UnrealNetwork.h" @@ -81,9 +79,7 @@ void AMyPlayerState::MoveTrooper_Implementation(ATrooper *Trooper, void AMyPlayerState::Attack_Implementation(ATrooper *Attacker, FVector Location, - int ActionIndex, - const TArray & - Troopers) { + int ActionIndex) { if (Attacker->CheckAttackCorrectness(Location, ActionIndex)) { Attacker->Attack(ActionIndex, Location); // for (const auto Trooper : Troopers) { @@ -169,7 +165,7 @@ void AMyPlayerState::OnPlayerAction(const FHitResult &HitResult) { // ATTACK! ATTACK! UE_LOG(LogTemp, Warning, TEXT("Do attack")); Attack(SelectedTrooper, NewlySelectedLocation, - CurrentAction, GetMyGameState()->GetTroopers()); + CurrentAction); SelectedTrooper->SetSelection(false, CurrentAction); SelectedTrooper = nullptr; break; diff --git a/Source/TurnBasedTutorial/MyPlayerState.h b/Source/TurnBasedTutorial/MyPlayerState.h index a3beef1..62109fa 100644 --- a/Source/TurnBasedTutorial/MyPlayerState.h +++ b/Source/TurnBasedTutorial/MyPlayerState.h @@ -33,8 +33,7 @@ public: UFUNCTION(Server, Reliable) void Attack(ATrooper *Attacker, FVector Location, - int ActionIndex, - const TArray &Troopers); + int ActionIndex); // UFUNCTION(Client, Reliable) // void CycleTurns() const; diff --git a/Source/TurnBasedTutorial/MyProjectile.cpp b/Source/TurnBasedTutorial/MyProjectile.cpp index 301ea77..5e15645 100644 --- a/Source/TurnBasedTutorial/MyProjectile.cpp +++ b/Source/TurnBasedTutorial/MyProjectile.cpp @@ -71,7 +71,7 @@ void AMyProjectile::NotifyActorBeginOverlap(AActor *OtherActor) { PlayerIndex); if (PlayerIndex != -1 && PlayerIndex != OtherTrooper-> GetPlayerIndex()) { - OtherTrooper->TakeDamage(Damage); + OtherTrooper->TrooperTakeDamage(Damage); } } else { UE_LOG(LogTemp, Warning, TEXT("Overlapped not a trooper")); diff --git a/Source/TurnBasedTutorial/SinglePlayerGM.cpp b/Source/TurnBasedTutorial/SinglePlayerGM.cpp new file mode 100644 index 0000000..956b77b --- /dev/null +++ b/Source/TurnBasedTutorial/SinglePlayerGM.cpp @@ -0,0 +1,19 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "SinglePlayerGM.h" +#include "SinglePlayerGS.h" + +ASinglePlayerGM::ASinglePlayerGM() : Super() { + GameStateClass = ASinglePlayerGS::StaticClass(); +} + +void ASinglePlayerGM::BeginPlay() { + Super::BeginPlay(); + UE_LOG(LogTemp, Warning, TEXT("SinglePlayer GameMode BeginPlay")); + GameStateClass = ASinglePlayerGS::StaticClass(); + StartGame(); +} + +void ASinglePlayerGM::PostLogin(APlayerController *NewPlayer) { + AGameMode::PostLogin(NewPlayer); +} diff --git a/Source/TurnBasedTutorial/SinglePlayerGM.h b/Source/TurnBasedTutorial/SinglePlayerGM.h new file mode 100644 index 0000000..f304336 --- /dev/null +++ b/Source/TurnBasedTutorial/SinglePlayerGM.h @@ -0,0 +1,23 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "MyGameMode.h" +#include "SinglePlayerGM.generated.h" + +/** + * + */ +UCLASS() +class TURNBASEDTUTORIAL_API ASinglePlayerGM : public AMyGameMode { + GENERATED_BODY() + +public: + ASinglePlayerGM(); + + virtual void BeginPlay() override; + + virtual void PostLogin(APlayerController *NewPlayer) override; + +}; diff --git a/Source/TurnBasedTutorial/SinglePlayerGS.cpp b/Source/TurnBasedTutorial/SinglePlayerGS.cpp new file mode 100644 index 0000000..9f929fc --- /dev/null +++ b/Source/TurnBasedTutorial/SinglePlayerGS.cpp @@ -0,0 +1,56 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "SinglePlayerGS.h" +#include "Net/UnrealNetwork.h" + +ASinglePlayerGS::ASinglePlayerGS() + : Super() { + IsMultiplayer = false; + PrimaryActorTick.bCanEverTick = true; + SetActorTickInterval(0.5f); +} + +void ASinglePlayerGS::BeginPlay() { + Super::BeginPlay(); + UE_LOG(LogTemp, Warning, TEXT("SinglePlayer GameState BeginPlay")); + if (LivingTroopersCount.Num() < 2) { + LivingTroopersCount.SetNum(2); + } + EnemyAiManager = GetWorld()->SpawnActor( + AEnemyAIController::StaticClass(), FVector(0.0f, 0.0f, 1000.0f), + FRotator(0.0f, 0.0f, 0.0f), FActorSpawnParameters()); + EnemyAiManager->InitializeTroopers(Troopers); +} + +void ASinglePlayerGS::CycleTurns() { + if (CurrentPlayerTurn == 0) { + PlayerInTurn()->EndTurn(); + } + for (const auto Trooper : Troopers) { + if (Trooper != nullptr) { + Trooper->ResetActionPoints(); + } + } + CurrentPlayerTurn = !CurrentPlayerTurn; + if (CurrentPlayerTurn == 0) { + PlayerInTurn()->StartTurn(); + } else { + EnemyAiManager->StartTurn(); + } +} + +void ASinglePlayerGS::Tick(float DeltaSeconds) { + Super::Tick(DeltaSeconds); + if (EnemyAiManager->bIsEnded) { + EnemyAiManager->bIsEnded = false; + CycleTurns(); + } else if (CurrentPlayerTurn == 1 && !EnemyAiManager->IsAITurn()) { + CycleTurns(); + } +} + +void ASinglePlayerGS::GetLifetimeReplicatedProps( + TArray &OutLifetimeProps) const { + Super::GetLifetimeReplicatedProps(OutLifetimeProps); + DOREPLIFETIME(ASinglePlayerGS, EnemyAiManager); +} diff --git a/Source/TurnBasedTutorial/SinglePlayerGS.h b/Source/TurnBasedTutorial/SinglePlayerGS.h new file mode 100644 index 0000000..8d575be --- /dev/null +++ b/Source/TurnBasedTutorial/SinglePlayerGS.h @@ -0,0 +1,29 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "EnemyAIController.h" +#include "MyGameState.h" +#include "SinglePlayerGS.generated.h" + +/** + * + */ +UCLASS() +class TURNBASEDTUTORIAL_API ASinglePlayerGS : public AMyGameState { + GENERATED_BODY() + +public: + ASinglePlayerGS(); + + virtual void BeginPlay() override; + + virtual void CycleTurns() override; + + virtual void Tick(float DeltaSeconds) override; + +protected: + UPROPERTY(Replicated) + AEnemyAIController *EnemyAiManager = nullptr; +}; diff --git a/Source/TurnBasedTutorial/Trooper.cpp b/Source/TurnBasedTutorial/Trooper.cpp index b560e27..9c1b4ed 100644 --- a/Source/TurnBasedTutorial/Trooper.cpp +++ b/Source/TurnBasedTutorial/Trooper.cpp @@ -3,8 +3,6 @@ #include "HealthBar.h" #include "MyGameState.h" -#include "MyPlayerController.h" -#include "MyPlayerState.h" #include "MyProjectile.h" #include "Components/WidgetComponent.h" #include "Net/UnrealNetwork.h" @@ -89,14 +87,16 @@ void ATrooper::Tick(float const DeltaTime) { } if (AttackPlayedTime >= AttackDuration) { AttackPlayedTime = 0.0f; - bIsAttacking = false; + // bIsAttacking = false; + SetIsAttacking(false); } } if (bIsTakingDamage) { TakingDamagePlayedTime += DeltaTime; if (TakingDamagePlayedTime >= TakingDamageDuration) { TakingDamagePlayedTime = 0.0f; - bIsTakingDamage = false; + // bIsTakingDamage = false; + SetIsTakingDamage(false); } } if (bIsMoving) { @@ -106,7 +106,8 @@ void ATrooper::Tick(float const DeltaTime) { if (PositionVector.Size() >= (TargetLocation - CurrentLocation). Size()) { CurrentLocation = TargetLocation; - bIsMoving = false; + // bIsMoving = false; + SetIsMoving(false); } else { CurrentLocation += PositionVector; } @@ -114,6 +115,42 @@ void ATrooper::Tick(float const DeltaTime) { } } +void ATrooper::SetIsAttacking(bool IsAttacking) { + bIsAttacking = IsAttacking; + if (IsAttacking) { + SetActorTickEnabled(true); + } else { + TryDisableTick(); + } +} + +void ATrooper::SetIsTakingDamage(bool IsTakingDamage) { + bIsTakingDamage = IsTakingDamage; + if (IsTakingDamage) { + SetActorTickEnabled(true); + } else { + TryDisableTick(); + } +} + +void ATrooper::SetIsMoving(bool IsMoving) { + bIsMoving = IsMoving; + if (IsMoving) { + SetActorTickEnabled(true); + } else { + TryDisableTick(); + } +} + +void ATrooper::TryDisableTick() { + if (!bIsAttacking && !bIsTakingDamage && !bIsMoving) { + SetActorTickEnabled(false); + if (AIController && AIController->IsValidLowLevel()) { + AIController->ActionDone(); + } + } +} + // void ATrooper::OnRepNotify_PlayerIndex() const { // const AMyPlayerState *player = Cast( // GetPlayerState()); @@ -131,7 +168,8 @@ void ATrooper::Tick(float const DeltaTime) { void ATrooper::MoveTrooper_Implementation(FVector const NewPos) { TargetLocation = NewPos; - bIsMoving = true; + // bIsMoving = true; + SetIsMoving(true); ActionPoints -= (NewPos - CurrentLocation).Size() * MoveCost; } @@ -196,6 +234,17 @@ float ATrooper::GetActionRadius(int action) const { } } +float ATrooper::GetRealActionRadius(int action) const { + switch (action) { + case 1: + return AttackAbility->ActionRadius; + case 2: + return SpecialAbility->ActionRadius; + default: + return ActionPoints / MoveCost; + } +} + float ATrooper::GetHitPoints() const { return HitPoints; } @@ -247,7 +296,15 @@ UAbility *ATrooper::GetAbility(int AbilityIndex) const { } } -void ATrooper::TakeDamage_Implementation(float Damage) { +void ATrooper::SetAIPossession(AEnemyAIController *EnemyController) { + AIController = EnemyController; +} + +bool ATrooper::IsDead() const { + return bIsDead; +} + +void ATrooper::TrooperTakeDamage_Implementation(float Damage) { if (bIsTakingDamage || bIsDead) { return; } @@ -257,7 +314,8 @@ void ATrooper::TakeDamage_Implementation(float Damage) { SetLifeSpan(DyingAnimationDuration); GetWorld()->GetGameState()->DecreaseLivingTroopers(PlayerIndex); } else { - bIsTakingDamage = true; + // bIsTakingDamage = true; + SetIsTakingDamage(true); } } @@ -315,7 +373,8 @@ int ATrooper::GetAnimationValue() { } void ATrooper::Attack_Implementation(int AbilityIndex, FVector ToLocation) { - bIsAttacking = true; + // bIsAttacking = true; + SetIsAttacking(true); bIsWaitingForFire = true; ActionPoints -= GetAbility(AbilityIndex)->ActionCost; CurrentAbilityIndex = AbilityIndex; diff --git a/Source/TurnBasedTutorial/Trooper.h b/Source/TurnBasedTutorial/Trooper.h index 0ca380f..64b13d2 100644 --- a/Source/TurnBasedTutorial/Trooper.h +++ b/Source/TurnBasedTutorial/Trooper.h @@ -2,6 +2,7 @@ #include "CoreMinimal.h" #include "Ability.h" +#include "EnemyAIController.h" #include "GameFramework/Character.h" #include "Trooper.generated.h" @@ -44,6 +45,9 @@ public: UFUNCTION() float GetActionRadius(int action) const; + UFUNCTION() + float GetRealActionRadius(int action) const; + UFUNCTION() float GetHitPoints() const; @@ -66,10 +70,19 @@ public: UAbility *GetAbility(int AbilityIndex) const; UFUNCTION(Server, Reliable) - void TakeDamage(float Damage); + void TrooperTakeDamage(float Damage); + + UFUNCTION() + void SetAIPossession(AEnemyAIController *EnemyController); + + UFUNCTION() + bool IsDead() const; protected: constexpr static float PIXELS_IN_RADIUS = 50; + + UPROPERTY() + AEnemyAIController *AIController = nullptr; UPROPERTY(EditAnywhere) UMaterialInterface *GreenMaterial = nullptr; @@ -179,6 +192,14 @@ protected: UPROPERTY(Replicated) bool bIsMoving = false; + + void SetIsAttacking(bool IsAttacking); + + void SetIsTakingDamage(bool IsTakingDamage); + + void SetIsMoving(bool IsMoving); + + void TryDisableTick(); }; // UCLASS()