diff --git a/Content/BattleField/BP_BattleUI.uasset b/Content/BattleField/BP_BattleUI.uasset new file mode 100644 index 0000000..c601229 Binary files /dev/null and b/Content/BattleField/BP_BattleUI.uasset differ diff --git a/Content/BattleField/BP_InventoryUI.uasset b/Content/BattleField/BP_InventoryUI.uasset new file mode 100644 index 0000000..21470fe Binary files /dev/null and b/Content/BattleField/BP_InventoryUI.uasset differ diff --git a/Content/BattleField/BattleFieldMap.umap b/Content/BattleField/BattleFieldMap.umap index edac1f0..b06ca50 100644 Binary files a/Content/BattleField/BattleFieldMap.umap and b/Content/BattleField/BattleFieldMap.umap differ diff --git a/Content/BattleField/UI_images/384-3845034_magic-wand-magic-wands-clipart-black-and-white.uasset b/Content/BattleField/UI_images/384-3845034_magic-wand-magic-wands-clipart-black-and-white.uasset new file mode 100644 index 0000000..4046bae Binary files /dev/null and b/Content/BattleField/UI_images/384-3845034_magic-wand-magic-wands-clipart-black-and-white.uasset differ diff --git a/Content/BattleField/UI_images/69-698852_footprints-png-136006.uasset b/Content/BattleField/UI_images/69-698852_footprints-png-136006.uasset new file mode 100644 index 0000000..1fe4cee Binary files /dev/null and b/Content/BattleField/UI_images/69-698852_footprints-png-136006.uasset differ diff --git a/Content/BattleField/UI_images/explosion-clip-art-33.uasset b/Content/BattleField/UI_images/explosion-clip-art-33.uasset new file mode 100644 index 0000000..8c4fda6 Binary files /dev/null and b/Content/BattleField/UI_images/explosion-clip-art-33.uasset differ diff --git a/Content/BattleField/UI_images/img_525444.uasset b/Content/BattleField/UI_images/img_525444.uasset new file mode 100644 index 0000000..7007f6d Binary files /dev/null and b/Content/BattleField/UI_images/img_525444.uasset differ diff --git a/Content/CityofBrass_Enemies/Static/SkeletonMelee_StaticMesh.uasset b/Content/CityofBrass_Enemies/Static/SkeletonMelee_StaticMesh.uasset deleted file mode 100644 index 4377c2b..0000000 Binary files a/Content/CityofBrass_Enemies/Static/SkeletonMelee_StaticMesh.uasset and /dev/null differ diff --git a/Content/CityofBrass_Enemies/Static/Wizard_StaticMesh.uasset b/Content/CityofBrass_Enemies/Static/Wizard_StaticMesh.uasset deleted file mode 100644 index 2cb8a10..0000000 Binary files a/Content/CityofBrass_Enemies/Static/Wizard_StaticMesh.uasset and /dev/null differ diff --git a/Content/MainMenu/MainMenu.uasset b/Content/MainMenu/MainMenu.uasset index 8420306..e515d16 100644 Binary files a/Content/MainMenu/MainMenu.uasset and b/Content/MainMenu/MainMenu.uasset differ diff --git a/Content/MainMenu/fotor-ai-2023050222244.uasset b/Content/MainMenu/fotor-ai-2023050222244.uasset new file mode 100644 index 0000000..d0de5c1 Binary files /dev/null and b/Content/MainMenu/fotor-ai-2023050222244.uasset differ diff --git a/Content/Troopers/BP_Trooper.uasset b/Content/Troopers/BP_Trooper.uasset new file mode 100644 index 0000000..fdf9fc5 Binary files /dev/null and b/Content/Troopers/BP_Trooper.uasset differ diff --git a/Content/Troopers/GreenSelectionMaterial.uasset b/Content/Troopers/GreenSelectionMaterial.uasset new file mode 100644 index 0000000..0249cb3 Binary files /dev/null and b/Content/Troopers/GreenSelectionMaterial.uasset differ diff --git a/Content/Troopers/RedSelectionMaterial.uasset b/Content/Troopers/RedSelectionMaterial.uasset new file mode 100644 index 0000000..2c7e4ac Binary files /dev/null and b/Content/Troopers/RedSelectionMaterial.uasset differ diff --git a/Content/Troopers/TrooperSkeletonMelee.uasset b/Content/Troopers/TrooperSkeletonMelee.uasset index bd93177..16c0cb5 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 47fb9a7..8c1e804 100644 Binary files a/Content/Troopers/TrooperWizard.uasset and b/Content/Troopers/TrooperWizard.uasset differ diff --git a/Content/Troopers/WBP_HealthBar.uasset b/Content/Troopers/WBP_HealthBar.uasset new file mode 100644 index 0000000..053eef5 Binary files /dev/null and b/Content/Troopers/WBP_HealthBar.uasset differ diff --git a/Source/TurnBasedTutorial/Ability.cpp b/Source/TurnBasedTutorial/Ability.cpp new file mode 100644 index 0000000..9d83356 --- /dev/null +++ b/Source/TurnBasedTutorial/Ability.cpp @@ -0,0 +1,22 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#include "Ability.h" + +// Sets default values for this component's properties +UAbility::UAbility() { + PrimaryComponentTick.bCanEverTick = false; + +} + +// Called when the game starts +void UAbility::BeginPlay() { + Super::BeginPlay(); + +} + +// void UAbility::TickComponent(float DeltaTime, +// ELevelTick TickType, +// FActorComponentTickFunction *ThisTickFunction) { +// Super::TickComponent(DeltaTime, TickType, ThisTickFunction); +// +// } diff --git a/Source/TurnBasedTutorial/Ability.h b/Source/TurnBasedTutorial/Ability.h new file mode 100644 index 0000000..282921e --- /dev/null +++ b/Source/TurnBasedTutorial/Ability.h @@ -0,0 +1,41 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/ActorComponent.h" +#include "Ability.generated.h" + + +UCLASS(ClassGroup=(Custom), meta=(BlueprintSpawnableComponent)) +class TURNBASEDTUTORIAL_API UAbility : public UActorComponent { + GENERATED_BODY() + +public: + // Sets default values for this component's properties + UAbility(); + + // Called when the game starts + virtual void BeginPlay() override; + + UPROPERTY(EditAnywhere) + float ActionCost = 100.0f; + + UPROPERTY(EditAnywhere) + float Damage = 50.0f; + + UPROPERTY(EditAnywhere) + float ActionRadius = 1000.0f; + + UPROPERTY(EditAnywhere) + float SplashRadius = 100.0f; + + UPROPERTY(EditAnywhere) + float LinearWidth = 50.0f; + + // Called every frame + // virtual void TickComponent(float DeltaTime, + // ELevelTick TickType, + // FActorComponentTickFunction * + // ThisTickFunction) override; +}; diff --git a/Source/TurnBasedTutorial/HealthBar.cpp b/Source/TurnBasedTutorial/HealthBar.cpp new file mode 100644 index 0000000..aea4196 --- /dev/null +++ b/Source/TurnBasedTutorial/HealthBar.cpp @@ -0,0 +1,18 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "HealthBar.h" + +#include "Components/ProgressBar.h" + +void UHealthBar::SetOwnerTrooper(ATrooper *Trooper) { + OwnerTrooper = Trooper; +} + +void UHealthBar::NativeTick(const FGeometry &MyGeometry, float InDeltaTime) { + Super::NativeTick(MyGeometry, InDeltaTime); + if (!OwnerTrooper.IsValid()) + return; + + HealthBar->SetPercent(OwnerTrooper->GetHitPoints() / OwnerTrooper->GetMaxHitPoints()); +} diff --git a/Source/TurnBasedTutorial/HealthBar.h b/Source/TurnBasedTutorial/HealthBar.h new file mode 100644 index 0000000..fa22426 --- /dev/null +++ b/Source/TurnBasedTutorial/HealthBar.h @@ -0,0 +1,26 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Trooper.h" +#include "Blueprint/UserWidget.h" +#include "HealthBar.generated.h" + +/** + * + */ +UCLASS(Abstract) +class TURNBASEDTUTORIAL_API UHealthBar : public UUserWidget { + GENERATED_BODY() +public: + void SetOwnerTrooper(ATrooper *Trooper); + +protected: + TWeakObjectPtr OwnerTrooper; + + UPROPERTY(meta=(BindWidget)) + class UProgressBar *HealthBar; + + virtual void NativeTick(const FGeometry &MyGeometry, float InDeltaTime) override; +}; diff --git a/Source/TurnBasedTutorial/MyGameMode.cpp b/Source/TurnBasedTutorial/MyGameMode.cpp index b64b8a8..dfb44fd 100644 --- a/Source/TurnBasedTutorial/MyGameMode.cpp +++ b/Source/TurnBasedTutorial/MyGameMode.cpp @@ -17,6 +17,7 @@ void AMyGameMode::BeginPlay() { Super::BeginPlay(); } + void AMyGameMode::InitializeBattleField() const { UE_LOG(LogTemp, Warning, TEXT("InitializeBattleField")); FVector Location(2000.0f, -1000.0f, 0.0f); @@ -49,6 +50,7 @@ void AMyGameMode::InitializeBattleField() const { 0, Location, TrooperCount++); Spawned->FinishSpawning(SpawnLocationAndRotation); Spawned->SetActorLocation(Location); + Troopers.Add(dynamic_cast(Spawned)); Location += {0.f, 500.f, 0.0f}; } Location = {-2000.0f, -1000.0f, 0.0f}; @@ -64,10 +66,12 @@ void AMyGameMode::InitializeBattleField() const { 1, Location, TrooperCount++); Spawned->FinishSpawning(SpawnLocationAndRotation); Spawned->SetActorLocation(Location); + Troopers.Add(dynamic_cast(Spawned)); Location += {0.f, 500.f, 0.0f}; } } + AActor *AMyGameMode::ChoosePlayerStart_Implementation(AController *Player) { UE_LOG(LogTemp, Warning, TEXT("GameMode ChoosePlayerStart %d"), GetNumPlayers()); @@ -111,6 +115,7 @@ void AMyGameMode::InitializeSpawnPointsIfNeeded(AController *Player) { void AMyGameMode::PostLogin(APlayerController *NewPlayer) { Super::PostLogin(NewPlayer); UE_LOG(LogTemp, Warning, TEXT("PostLogin")); + // PlayerControllers.Add(dynamic_cast(NewPlayer)); // const auto World = GetWorld(); const auto CurrentNumberOfPlayers = GetNumPlayers(); @@ -121,6 +126,9 @@ void AMyGameMode::PostLogin(APlayerController *NewPlayer) { if (CurrentNumberOfPlayers == 2) { UE_LOG(LogTemp, Warning, TEXT("Game Start")); // start the game + // dynamic_cast( + // GetWorld()->GetGameState())->StartGame(); + // InitializeBattleField(); StartGame(); } else { // delay the game @@ -131,6 +139,10 @@ void AMyGameMode::PostLogin(APlayerController *NewPlayer) { void AMyGameMode::StartGame() { InitializeBattleField(); + // PlayerNotInTurn()->SetEnemySelection(Troopers); + // PlayerInTurn()->SetEnemySelection(Troopers); + // PlayerControllers[0]->SetEnemySelection(Troopers); + // PlayerControllers[1]->SetEnemySelection(Troopers); PlayerInTurn()->StartTurn(); } @@ -140,22 +152,31 @@ AMyPlayerController *AMyGameMode::PlayerInTurn() const { } AMyPlayerController *AMyGameMode::PlayerNotInTurn() const { - uint8 PlayerControllerIndexNotInTurn; - if (CurrentPlayerTurn == 0) { - PlayerControllerIndexNotInTurn = 1; - } else { - PlayerControllerIndexNotInTurn = 0; - } - return GetMyPlayerController(PlayerControllerIndexNotInTurn); + // uint8 PlayerControllerIndexNotInTurn; + // if (CurrentPlayerTurn == 0) { + // PlayerControllerIndexNotInTurn = 1; + // } else { + // PlayerControllerIndexNotInTurn = 0; + // } + // return GetMyPlayerController(PlayerControllerIndexNotInTurn); + return GetMyPlayerController(!CurrentPlayerTurn); } void AMyGameMode::CycleTurns() { - PlayerInTurn()->EndTurn(); - if (CurrentPlayerTurn == 0) { - CurrentPlayerTurn = 1; - } else { - CurrentPlayerTurn = 0; + if (!this) + return; + // PlayerInTurn()->EndTurn(); + for (const auto Trooper : Troopers) { + if (Trooper != nullptr) { + Trooper->ResetActionPoints(); + } } + CurrentPlayerTurn = !CurrentPlayerTurn; + // if (CurrentPlayerTurn == 0) { + // CurrentPlayerTurn = 1; + // } else { + // CurrentPlayerTurn = 0; + // } PlayerInTurn()->StartTurn(); } diff --git a/Source/TurnBasedTutorial/MyGameMode.h b/Source/TurnBasedTutorial/MyGameMode.h index 72ca264..e7f19ab 100644 --- a/Source/TurnBasedTutorial/MyGameMode.h +++ b/Source/TurnBasedTutorial/MyGameMode.h @@ -23,6 +23,7 @@ public: virtual void BeginPlay() override; + UFUNCTION(BlueprintCallable) void CycleTurns(); @@ -34,7 +35,8 @@ private: UPROPERTY() TMap SpawnPoints{}; - AMyPlayerController *GetMyPlayerController(uint8 const PlayerIndex) const; + UPROPERTY() + mutable TArray Troopers; UFUNCTION(BlueprintCallable) void StartGame(); @@ -47,4 +49,7 @@ private: UPROPERTY() uint8 CurrentPlayerTurn{0}; + + UFUNCTION() + AMyPlayerController *GetMyPlayerController(uint8 const PlayerIndex) const; }; diff --git a/Source/TurnBasedTutorial/MyGameState.cpp b/Source/TurnBasedTutorial/MyGameState.cpp index 83327fd..b1538d3 100644 --- a/Source/TurnBasedTutorial/MyGameState.cpp +++ b/Source/TurnBasedTutorial/MyGameState.cpp @@ -1,8 +1,87 @@ // Fill out your copyright notice in the Description page of Project Settings. - #include "MyGameState.h" +// #include "MyPlayerController.h" +// #include "Trooper.h" +// #include "Kismet/GameplayStatics.h" +// void AMyGameState::InitializeBattleField() const { +// UE_LOG(LogTemp, Warning, TEXT("InitializeBattleField")); +// FVector Location(2000.0f, -1000.0f, 0.0f); +// FRotator Rotation(0.0f, 180.0f, 0.0f); +// +// uint8 TrooperCount = 0; +// +// TArray bpPaths{ +// TEXT( +// "Blueprint'/Game/Troopers/TrooperSkeletonMelee.TrooperSkeletonMelee_C'" +// ), +// TEXT("Blueprint'/Game/Troopers/TrooperWizard.TrooperWizard_C'") +// }; +// TArray LoadedBpAssets; +// for (int i = 0; i < bpPaths.Num(); ++i) { +// TSoftClassPtr ActorBpClass = TSoftClassPtr( +// FSoftObjectPath(bpPaths[i]) +// ); +// LoadedBpAssets.Push(ActorBpClass.LoadSynchronous()); +// } +// +// 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); +// dynamic_cast(Spawned)->Initialize( +// 0, Location, TrooperCount++); +// Spawned->FinishSpawning(SpawnLocationAndRotation); +// Spawned->SetActorLocation(Location); +// 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); +// dynamic_cast(Spawned)->Initialize( +// 1, Location, TrooperCount++); +// Spawned->FinishSpawning(SpawnLocationAndRotation); +// Spawned->SetActorLocation(Location); +// Location += {0.f, 500.f, 0.0f}; +// } +// } - +// AMyPlayerController *AMyGameState::GetMyPlayerController( +// uint8 const PlayerIndex) const { +// return dynamic_cast( +// UGameplayStatics::GetPlayerController(GetWorld(), PlayerIndex)); +// } +// +// void AMyGameState::StartGame() { +// // InitializeBattleField(); +// PlayerInTurn()->StartTurn(); +// } +// +// void AMyGameState::CycleTurns() { +// PlayerInTurn()->EndTurn(); +// if (CurrentPlayerTurn == 0) { +// CurrentPlayerTurn = 1; +// } else { +// CurrentPlayerTurn = 0; +// } +// PlayerInTurn()->StartTurn(); +// } +// +// AMyPlayerController *AMyGameState::PlayerInTurn() const { +// return GetMyPlayerController(CurrentPlayerTurn); +// } +// +// AMyPlayerController *AMyGameState::PlayerNotInTurn() const { +// return GetMyPlayerController(CurrentPlayerTurn == 0 ? 1 : 0); +// } diff --git a/Source/TurnBasedTutorial/MyGameState.h b/Source/TurnBasedTutorial/MyGameState.h index 20280f0..d9220da 100644 --- a/Source/TurnBasedTutorial/MyGameState.h +++ b/Source/TurnBasedTutorial/MyGameState.h @@ -10,11 +10,28 @@ * */ UCLASS() -class TURNBASEDTUTORIAL_API AMyGameState : public AGameState -{ - GENERATED_BODY() - - - - +class TURNBASEDTUTORIAL_API AMyGameState : public AGameState { + GENERATED_BODY() +// public: +// UFUNCTION(BlueprintCallable) +// void CycleTurns(); +// +// UFUNCTION(BlueprintCallable) +// void StartGame(); +// +// private: +// // void InitializeBattleField() const; +// +// UFUNCTION() +// AMyPlayerController *GetMyPlayerController(uint8 const PlayerIndex) const; +// +// +// UFUNCTION(BlueprintPure) +// AMyPlayerController *PlayerInTurn() const; +// +// UFUNCTION(BlueprintPure) +// AMyPlayerController *PlayerNotInTurn() const; +// +// UPROPERTY() +// uint8 CurrentPlayerTurn{0}; }; diff --git a/Source/TurnBasedTutorial/MyPlayerController.cpp b/Source/TurnBasedTutorial/MyPlayerController.cpp index 5dab816..c1ef308 100644 --- a/Source/TurnBasedTutorial/MyPlayerController.cpp +++ b/Source/TurnBasedTutorial/MyPlayerController.cpp @@ -8,6 +8,7 @@ AMyPlayerController::AMyPlayerController() : Super(), bIsMyTurn(false), SelectedTrooper(nullptr) { UE_LOG(LogTemp, Warning, TEXT("Player controller created")); + SetShowMouseCursor(true); } void AMyPlayerController::SetupInputComponent() { @@ -32,22 +33,36 @@ void AMyPlayerController::StartTurn_Implementation() { UE_LOG(LogTemp, Warning, TEXT("Your turn, %d"), PlayerIndex); } -void AMyPlayerController::EndTurn_Implementation() { - SetMyTurn(false); - UE_LOG(LogTemp, Warning, TEXT("Not your turn, %d"), PlayerIndex); -} - auto AMyPlayerController::GetMyGameMode() const { return dynamic_cast( UGameplayStatics::GetGameMode(GetWorld())); } +void AMyPlayerController::EndTurn_Implementation() { + if (bIsMyTurn) { + UE_LOG(LogTemp, Warning, TEXT("End Turn from player %d"), PlayerIndex); + SetMyTurn(false); + if (SelectedTrooper) { + SelectedTrooper->SetSelection(false); + SelectedTrooper = nullptr; + } + UE_LOG(LogTemp, Warning, TEXT("Not your turn, %d"), PlayerIndex); + AMyGameMode *gameMode = GetMyGameMode(); + gameMode->CycleTurns(); + } +} + +// AMyGameState *AMyPlayerController::GetMyGameState() const { +// return dynamic_cast( +// GetWorld()->GetGameState()); +// } + void AMyPlayerController::MoveTrooper_Implementation( ATrooper *Trooper, FVector Location) { if (Trooper->CheckMoveCorrectness(Location)) { Trooper->MoveTrooper(Location); - GetMyGameMode()->CycleTurns(); + // GetMyGameMode()->CycleTurns(); } else { GEngine->AddOnScreenDebugMessage(-1, 3.0f, FColor::Red, FString::Printf( @@ -55,33 +70,89 @@ void AMyPlayerController::MoveTrooper_Implementation( } } -void AMyPlayerController::AttackTrooper_Implementation( - ATrooper *Attacker, - ATrooper *Victim) { - if (Attacker->CheckAttackCorrectness(Victim->GetLocation())) { - Attacker->Attack(); - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, - FString::Printf( - TEXT("ATTACK!! %d attacked %d"), - Attacker->GetId(), - Victim->GetId())); - GetMyGameMode()->CycleTurns(); +// void AMyPlayerController::AttackTrooper_Implementation( +// ATrooper *Attacker, +// ATrooper *Victim) { +// if (Attacker->CheckAttackCorrectness(Victim->GetLocation())) { +// Attacker->Attack(); +// // GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, +// // FString::Printf( +// // TEXT("ATTACK!! %d attacked %d"), +// // Attacker->GetId(), +// // Victim->GetId())); +// // GetMyGameMode()->CycleTurns(); +// } else { +// GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, +// FString::Printf( +// TEXT( +// "Attack failed! Out of attack radius!"))); +// } +// } + + +void AMyPlayerController::Attack_Implementation(ATrooper *Attacker, + FVector Location, + int ActionIndex) { + if (Attacker && CurrentAction >= 1 && CurrentAction <= 2 && + Attacker->CheckAttackCorrectness(Location, CurrentAction)) { + Attacker->Attack(CurrentAction); } else { - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, - FString::Printf( - TEXT( - "Attack failed! Out of attack radius!"))); + UE_LOG(LogTemp, Warning, + TEXT("Out of radius or not enough Action Points")); } } - void AMyPlayerController::SetPlayerIndex(uint8 NewPlayerIndex) { PlayerIndex = NewPlayerIndex; } +float AMyPlayerController::SetCurrentActionAndReturnRadius(int action) { + CurrentAction = action; + UE_LOG(LogTemp, Warning, TEXT("SetCurrentAction: %d on Player Controller " + "with index %d"), CurrentAction, PlayerIndex); + if (SelectedTrooper) { + return SelectedTrooper->GetActionRadius(CurrentAction); + } + return 0.0f; +} + +void AMyPlayerController::SetEnemySelection_Implementation( + const TArray &Troopers) const { + for (const auto Trooper : Troopers) { + if (Trooper != nullptr && Trooper->GetPlayerIndex() != PlayerIndex) { + Trooper->HighlightAsEnemy(); + } + } +} + + +// void AMyPlayerController::SetEnemySelection_Implementation() { +// if (!GetMyGameMode()) { +// UE_LOG(LogTemp, Warning, TEXT("Failed to get Game mode, Index: %d"), PlayerIndex); +// return; +// } +// const auto &Troopers = GetMyGameMode()->GetTroopers(); +// for (const auto Trooper : Troopers) { +// if (Trooper != nullptr && Trooper->GetPlayerIndex() != PlayerIndex) { +// Trooper->HighlightAsEnemy(); +// } +// } +// } + +// void AMyPlayerController::SetEnemySelection(TArray &Troopers) const { +// for (const auto Trooper : Troopers) { +// if (Trooper->GetPlayerIndex() != PlayerIndex) { +// Trooper->HighlightAsEnemy(); +// } +// } +// } + void AMyPlayerController::GetLifetimeReplicatedProps( TArray &OutLifetimeProps) const { DOREPLIFETIME(AMyPlayerController, PlayerIndex); + DOREPLIFETIME(AMyPlayerController, CurrentAction); + DOREPLIFETIME(AMyPlayerController, bIsMyTurn); + DOREPLIFETIME(AMyPlayerController, SelectedTrooper); } void AMyPlayerController::OnLeftMouseClick() { @@ -89,6 +160,7 @@ void AMyPlayerController::OnLeftMouseClick() { return; } UE_LOG(LogTemp, Warning, TEXT("Mouse clicked")); + UE_LOG(LogTemp, Warning, TEXT("Current action: %d"), CurrentAction); FHitResult HitResult; bool const IsHitResult = GetHitResultUnderCursorForObjects( TArray>{ObjectTypeQuery1}, false, @@ -103,43 +175,97 @@ void AMyPlayerController::OnLeftMouseClick() { ATrooper *NewlySelectedTrooper = dynamic_cast( HitResult.GetActor()); + // skip re-selection + if (SelectedTrooper == NewlySelectedTrooper) { + UE_LOG(LogTemp, Warning, TEXT("Skip reselection")); + return; + } + if (NewlySelectedTrooper == nullptr || !NewlySelectedTrooper-> - IsValidLowLevel()) { + IsValidLowLevel() || NewlySelectedTrooper->GetPlayerIndex() != + PlayerIndex) { // we selected something that is not a trooper (or trooper in shitty state...) // probably we should move to it if we can... - UE_LOG(LogTemp, Warning, TEXT("Not a trooper")); + // UE_LOG(LogTemp, Warning, TEXT("Not a trooper")); // if initial trooper is valid... if (SelectedTrooper != nullptr && SelectedTrooper->IsValidLowLevel()) { - UE_LOG(LogTemp, Warning, TEXT("Do move")); - // move this mf - MoveTrooper(SelectedTrooper, NewlySelectedLocation); - // and reset the selection.... - SelectedTrooper = nullptr; + switch (CurrentAction) { + case 0: + UE_LOG(LogTemp, Warning, TEXT("Do move")); + // move this mf + MoveTrooper(SelectedTrooper, NewlySelectedLocation); + // and reset the selection.... + SelectedTrooper->SetSelection(false); + SelectedTrooper = nullptr; + break; + default: + // ATTACK! ATTACK! + UE_LOG(LogTemp, Warning, TEXT("Do attack")); + Attack(SelectedTrooper, NewlySelectedLocation, + CurrentAction); + SelectedTrooper->SetSelection(false); + SelectedTrooper = nullptr; + break; + } } - return; - } - UE_LOG(LogTemp, Warning, TEXT("New Selected Player Index %d"), - NewlySelectedTrooper->GetPlayerIndex()); - // skip re-selection - if (SelectedTrooper == NewlySelectedTrooper) { - UE_LOG(LogTemp, Warning, TEXT("Skip reselection")); - return; - } - // we selected valid trooper... - if (NewlySelectedTrooper->GetPlayerIndex() == PlayerIndex) { + } else if (NewlySelectedTrooper != nullptr && NewlySelectedTrooper-> + IsValidLowLevel() && NewlySelectedTrooper->GetPlayerIndex() == + PlayerIndex) { UE_LOG(LogTemp, Warning, TEXT("Do reselect")); // our move, selection + if (SelectedTrooper) { + SelectedTrooper->SetSelection(false); + } SelectedTrooper = NewlySelectedTrooper; - } else { - UE_LOG(LogTemp, Warning, TEXT("Attack or skip...")); - // maybe selected trooper had gone crazy... - if (SelectedTrooper == nullptr || !SelectedTrooper->IsValidLowLevel()) - return; - UE_LOG(LogTemp, Warning, TEXT("Do attack")); - // ATTACK!!! ATTACK!!!!!! - AttackTrooper(SelectedTrooper, NewlySelectedTrooper); - SelectedTrooper = nullptr; + SelectedTrooper->SetSelection(true); } + + // + // if (NewlySelectedTrooper == nullptr || !NewlySelectedTrooper-> + // IsValidLowLevel()) { + // // we selected something that is not a trooper (or trooper in shitty state...) + // // probably we should move to it if we can... + // + // UE_LOG(LogTemp, Warning, TEXT("Not a trooper")); + // + // // if initial trooper is valid... + // if (SelectedTrooper != nullptr && SelectedTrooper->IsValidLowLevel()) { + // UE_LOG(LogTemp, Warning, TEXT("Do move")); + // // move this mf + // MoveTrooper(SelectedTrooper, NewlySelectedLocation); + // // and reset the selection.... + // SelectedTrooper->SetSelection(false); + // SelectedTrooper = nullptr; + // } + // return; + // } + // UE_LOG(LogTemp, Warning, TEXT("New Selected Player Index %d"), + // NewlySelectedTrooper->GetPlayerIndex()); + // // skip re-selection + // if (SelectedTrooper == NewlySelectedTrooper) { + // UE_LOG(LogTemp, Warning, TEXT("Skip reselection")); + // return; + // } + // we selected valid trooper... + // if (NewlySelectedTrooper->GetPlayerIndex() == PlayerIndex) { + // UE_LOG(LogTemp, Warning, TEXT("Do reselect")); + // // our move, selection + // if (SelectedTrooper) { + // SelectedTrooper->SetSelection(false); + // } + // SelectedTrooper = NewlySelectedTrooper; + // SelectedTrooper->SetSelection(true); + // } else { + // UE_LOG(LogTemp, Warning, TEXT("Attack or skip...")); + // // maybe selected trooper had gone crazy... + // if (SelectedTrooper == nullptr || !SelectedTrooper->IsValidLowLevel()) + // return; + // UE_LOG(LogTemp, Warning, TEXT("Do attack")); + // // ATTACK!!! ATTACK!!!!!! + // AttackTrooper(SelectedTrooper, NewlySelectedTrooper); + // SelectedTrooper->SetSelection(false); + // SelectedTrooper = nullptr; + // } } diff --git a/Source/TurnBasedTutorial/MyPlayerController.h b/Source/TurnBasedTutorial/MyPlayerController.h index 8b171b8..6b56c55 100644 --- a/Source/TurnBasedTutorial/MyPlayerController.h +++ b/Source/TurnBasedTutorial/MyPlayerController.h @@ -2,6 +2,7 @@ #pragma once #include "CoreMinimal.h" +// #include "MyGameState.h" #include "Trooper.h" #include "GameFramework/PlayerController.h" #include "MyPlayerController.generated.h" @@ -22,25 +23,38 @@ public: UFUNCTION(Client, Reliable) void StartTurn(); - UFUNCTION(Client, Reliable) + UFUNCTION(Client, Reliable, BlueprintCallable) void EndTurn(); UFUNCTION(Server, Reliable) void MoveTrooper(ATrooper *Trooper, FVector Location); + // UFUNCTION(Server, Reliable) + // void AttackTrooper(ATrooper *Attacker, ATrooper *Victim); + UFUNCTION(Server, Reliable) - void AttackTrooper(ATrooper *Attacker, ATrooper *Victim); + void Attack(ATrooper *Attacker, FVector Location, int ActionIndex); UFUNCTION() void SetPlayerIndex(uint8 NewPlayerIndex); + UFUNCTION(BlueprintCallable) + float SetCurrentActionAndReturnRadius(int action); + + UFUNCTION(Client, Reliable) + void SetEnemySelection(const TArray &Troopers) const; + private: + UPROPERTY(Replicated) bool bIsMyTurn; + UPROPERTY(Replicated) + int CurrentAction = 0; + UPROPERTY(Replicated) uint8 PlayerIndex; - UPROPERTY() + UPROPERTY(Replicated) ATrooper *SelectedTrooper; void OnLeftMouseClick(); @@ -48,4 +62,6 @@ private: void SetMyTurn(bool bMyTurn); auto GetMyGameMode() const; + + // AMyGameState *GetMyGameState() const; }; diff --git a/Source/TurnBasedTutorial/Trooper.cpp b/Source/TurnBasedTutorial/Trooper.cpp index e8091b1..e999987 100644 --- a/Source/TurnBasedTutorial/Trooper.cpp +++ b/Source/TurnBasedTutorial/Trooper.cpp @@ -1,13 +1,42 @@ #include "Trooper.h" #include + +#include "HealthBar.h" +#include "Components/WidgetComponent.h" #include "Net/UnrealNetwork.h" // Sets default values -ATrooper::ATrooper() { +ATrooper::ATrooper() + : HitPoints(StartHitPoints), ActionPoints(StartActionPoints) { bReplicates = true; PrimaryActorTick.bCanEverTick = true; Tags.Add(FName("Trooper")); + AttackAbility = CreateDefaultSubobject("AttackAbility"); + SpecialAbility = CreateDefaultSubobject("SpecialAbility"); + + HealthWidgetComponent = CreateDefaultSubobject( + "HealthBar"); + HealthWidgetComponent->AttachToComponent(RootComponent, + FAttachmentTransformRules::KeepRelativeTransform); + + SelectionStaticMesh = CreateDefaultSubobject( + "SelectionMesh"); + static ConstructorHelpers::FObjectFinder MeshToUse(TEXT( + "StaticMesh'/Game/StarterContent/Shapes/Shape_Cylinder.Shape_Cylinder'")); + SelectionStaticMesh->AttachToComponent(RootComponent, + FAttachmentTransformRules::KeepRelativeTransform); + SelectionStaticMesh->SetRelativeScale3D({1.8, 1.8, 0.01}); + SelectionStaticMesh->SetVisibility(false); + + if (MeshToUse.Object) { + SelectionStaticMesh->SetStaticMesh(MeshToUse.Object); + SelectionStaticMesh->SetStaticMesh(MeshToUse.Object); + } + // SelectionStaticMesh->SetRelativeTransform(FTransform({1000,1000,100}, {0, 0, 0}), false, + // nullptr, ETeleportType::TeleportPhysics); + // SelectionStaticMesh-> + // MyStaticMesh = CreateDefaultSubobject("Mesh"); // RootComponent = MyStaticMesh; // MeshPath = TEXT("StaticMesh'/Game/StarterContent/Props/SM_Chair.SM_Chair'"); @@ -31,6 +60,8 @@ ATrooper::ATrooper() { // Called when the game starts or when spawned void ATrooper::BeginPlay() { Super::BeginPlay(); + Cast(HealthWidgetComponent->GetUserWidgetObject())-> + SetOwnerTrooper(this); } void ATrooper::Initialize(uint8 const NewPlayerIndex, @@ -55,7 +86,8 @@ void ATrooper::Tick(float const DeltaTime) { FVector PositionVector = (TargetLocation - CurrentLocation); PositionVector.Normalize(); PositionVector *= (Speed * DeltaTime); - if (PositionVector.Size() >= (TargetLocation - CurrentLocation).Size()) { + if (PositionVector.Size() >= (TargetLocation - CurrentLocation). + Size()) { CurrentLocation = TargetLocation; bIsMoving = false; } else { @@ -68,6 +100,7 @@ void ATrooper::Tick(float const DeltaTime) { void ATrooper::MoveTrooper(FVector const NewPos) { TargetLocation = NewPos; bIsMoving = true; + ActionPoints -= (NewPos - CurrentLocation).Size() * MoveCost; } uint8 ATrooper::GetId() const { @@ -83,6 +116,9 @@ void ATrooper::GetLifetimeReplicatedProps( DOREPLIFETIME(ATrooper, bIsMoving); DOREPLIFETIME(ATrooper, Id); DOREPLIFETIME(ATrooper, bIsAttacking); + DOREPLIFETIME(ATrooper, HitPoints); + DOREPLIFETIME(ATrooper, ActionPoints); + DOREPLIFETIME(ATrooper, HealthWidgetComponent); DOREPLIFETIME(ATrooper, AttackPlayedTime); } @@ -108,8 +144,52 @@ FVector ATrooper::GetLocation() const { return CurrentLocation; } -void ATrooper::Attack() { - bIsAttacking = true; + +float ATrooper::GetActionRadius(int action) const { + switch (action) { + case 1: + return AttackAbility->ActionRadius; + case 2: + return SpecialAbility->ActionRadius; + default: + return ActionPoints; + } +} + +float ATrooper::GetHitPoints() const { + return HitPoints; +} + +float ATrooper::GetMaxHitPoints() const { + return StartHitPoints; +} + +void ATrooper::SetSelection(bool Selection) const { + if (SelectionStaticMesh) { + if (SelectionStaticMesh->GetMaterial(0) != GreenMaterial) { + SelectionStaticMesh->SetMaterial(0, GreenMaterial); + } + SelectionStaticMesh->SetVisibility(Selection); + } +} + +void ATrooper::HighlightAsEnemy() const { + SelectionStaticMesh->SetVisibility(true); +} + +void ATrooper::ResetActionPoints() { + ActionPoints = StartActionPoints; +} + +UAbility *ATrooper::GetAbility(int AbilityIndex) const { + switch (AbilityIndex) { + case 1: + return AttackAbility; + case 2: + return SpecialAbility; + default: + return nullptr; + } } float ATrooper::GetAnimationValue() { @@ -122,10 +202,20 @@ float ATrooper::GetAnimationValue() { return 0.0f; } +void ATrooper::Attack(int abilityIndex) { + bIsAttacking = true; + ActionPoints -= GetAbility(abilityIndex)->ActionCost; +} + bool ATrooper::CheckMoveCorrectness(const FVector newPos) const { - return (newPos - CurrentLocation).Size() <= MoveRadius; + return (newPos - CurrentLocation).Size() * MoveCost <= ActionPoints; + // return (newPos - CurrentLocation).Size() <= MoveRadius; } -bool ATrooper::CheckAttackCorrectness(const FVector attackLocation) const { - return (attackLocation - CurrentLocation).Size() <= AttackRadius; +bool ATrooper::CheckAttackCorrectness(const FVector attackLocation, + int abilityIndex) const { + return (attackLocation - CurrentLocation).Size() <= + GetAbility(abilityIndex)->ActionRadius && ActionPoints >= + GetAbility(abilityIndex)->ActionCost; + // return (attackLocation - CurrentLocation).Size() <= AttackRadius; } diff --git a/Source/TurnBasedTutorial/Trooper.h b/Source/TurnBasedTutorial/Trooper.h index c27efd7..8446480 100644 --- a/Source/TurnBasedTutorial/Trooper.h +++ b/Source/TurnBasedTutorial/Trooper.h @@ -1,6 +1,7 @@ #pragma once #include "CoreMinimal.h" +#include "Ability.h" #include "GameFramework/Character.h" #include "Trooper.generated.h" @@ -29,38 +30,92 @@ public: bool CheckMoveCorrectness(const FVector newPos) const; UFUNCTION() - bool CheckAttackCorrectness(const FVector attackLocation) const; + bool CheckAttackCorrectness(const FVector attackLocation, int abilityIndex) const; UFUNCTION() FVector GetLocation() const; UFUNCTION(BlueprintCallable) float GetAnimationValue(); - + UFUNCTION() - void Attack(); + void Attack(int abilityIndex); + UFUNCTION() + float GetActionRadius(int action) const; + + UFUNCTION() + float GetHitPoints() const; + + UFUNCTION() + float GetMaxHitPoints() const; + + UFUNCTION() + void SetSelection(bool Selected) const; + + UFUNCTION() + void HighlightAsEnemy() const; + + UFUNCTION() + void ResetActionPoints(); + + UFUNCTION() + UAbility *GetAbility(int AbilityIndex) const; + protected: + UPROPERTY(EditAnywhere) + UMaterialInterface *GreenMaterial = nullptr; + + UPROPERTY(EditAnywhere) + UMaterialInterface *RedMaterial = nullptr; + + UPROPERTY(EditAnywhere) + UAbility *AttackAbility; + + UPROPERTY(EditAnywhere) + UAbility *SpecialAbility; + const float MoveRadius = 1500.f; - const float AttackRadius = 1000.f; + float AttackRadius = 1000.f; + + UPROPERTY(EditAnywhere) + float Speed = 300.0f; + + UPROPERTY(EditAnywhere) + float StartHitPoints = 100.0f; + + UPROPERTY(EditAnywhere) + float MoveCost = 0.0667f; + + UPROPERTY(EditAnywhere) + float StartActionPoints = 100.0f; + + UPROPERTY(Replicated) + float HitPoints; + + UPROPERTY(Replicated) + float ActionPoints; UPROPERTY(Replicated) bool bIsAttacking = false; UPROPERTY(Replicated) float AttackPlayedTime; - + const float AttackDuration = 1.16667f; virtual void BeginPlay() override; virtual void Tick(float const DeltaTime) override; + UPROPERTY(VisibleAnywhere, Replicated) + class UWidgetComponent *HealthWidgetComponent; + // void SetStaticMesh() const; - // UPROPERTY(VisibleAnywhere, BlueprintReadOnly) - // UStaticMeshComponent *MyStaticMesh; + UPROPERTY(VisibleAnywhere, BlueprintReadOnly) + UStaticMeshComponent *SelectionStaticMesh; // UPROPERTY(EditAnywhere, BlueprintReadWrite) // USkeletalMeshComponent *MySkeletalMesh; @@ -72,10 +127,7 @@ protected: UPROPERTY(Replicated) uint8 Id; - - UPROPERTY() - float Speed = 300.0f; - + UPROPERTY(Replicated) FVector CurrentLocation; diff --git a/TurnBased.uproject b/TurnBased.uproject index d0fc005..fd638db 100644 --- a/TurnBased.uproject +++ b/TurnBased.uproject @@ -1,6 +1,6 @@ { "FileVersion": 3, - "EngineAssociation": "4.27", + "EngineAssociation": "{B4FE6467-4579-3D84-B22A-558A3D607596}", "Category": "", "Description": "", "Modules": [ @@ -9,7 +9,8 @@ "Type": "Runtime", "LoadingPhase": "Default", "AdditionalDependencies": [ - "Engine" + "Engine", + "UMG" ] } ]