pull/6/head
m4xxx1m 2 years ago
parent f798fd22b1
commit 3e2f409f43

1
.gitignore vendored

@ -1,5 +1,6 @@
# Visual Studio 2015 user specific files
.vs/
.idea/
# Compiled Object files
*.slo

@ -1,7 +1,7 @@
[/Script/EngineSettings.GameMapsSettings]
EditorStartupMap=/Game/BattleField/BattleFieldMap.BattleFieldMap
EditorStartupMap=/Game/MainMenu/MainMenuLevel.MainMenuLevel
LocalMapOptions=
TransitionMap=None
bUseSplitscreen=True

Binary file not shown.

Binary file not shown.

Binary file not shown.

@ -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<ATrooper *> &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();
}

@ -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<ATrooper *> &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<ATrooper *> PossessedTroopers;
UPROPERTY()
TArray<ATrooper *> PlayerTroopers;
};

@ -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"));

@ -2,7 +2,6 @@
#pragma once
#include "Components/SphereComponent.h"
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "MyExplosion.generated.h"

@ -27,7 +27,7 @@ public:
// UFUNCTION(BlueprintCallable)
// void CycleTurns();
private:
protected:
void InitializeSpawnPointsIfNeeded(AController *Player);
UFUNCTION(Server, Reliable)

@ -10,20 +10,27 @@ auto AMyGameState::GetMyPlayerState(uint8 PlayerIndex) const {
return Cast<AMyPlayerState>(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();
@ -76,8 +83,8 @@ bool AMyGameState::IsGameStarted() const {
void AMyGameState::DecreaseLivingTroopers(int PlayerIndex) {
if (bGameIsOver)
return;
LivingTroopers[PlayerIndex]--;
if (LivingTroopers[PlayerIndex] <= 0) {
LivingTroopersCount[PlayerIndex]--;
if (LivingTroopersCount[PlayerIndex] <= 0) {
UE_LOG(LogTemp, Warning, TEXT("Player %d lose!"), PlayerIndex);
bGameIsOver = true;
}

@ -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<ATrooper *> Troopers;
UPROPERTY(Replicated)
TArray<int> LivingTroopers;
TArray<int> LivingTroopersCount;
UPROPERTY(Replicated)
bool bGameStarted = false;

@ -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);

@ -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(

@ -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() {

@ -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<ATrooper *> &
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;

@ -33,8 +33,7 @@ public:
UFUNCTION(Server, Reliable)
void Attack(ATrooper *Attacker,
FVector Location,
int ActionIndex,
const TArray<ATrooper *> &Troopers);
int ActionIndex);
// UFUNCTION(Client, Reliable)
// void CycleTurns() const;

@ -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"));

@ -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);
}

@ -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;
};

@ -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>(
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<FLifetimeProperty> &OutLifetimeProps) const {
Super::GetLifetimeReplicatedProps(OutLifetimeProps);
DOREPLIFETIME(ASinglePlayerGS, EnemyAiManager);
}

@ -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;
};

@ -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<AMyPlayerState>(
// 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<AMyGameState>()->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;

@ -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,11 +70,20 @@ 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()

Loading…
Cancel
Save