diff --git a/Source/the_twilight_abyss/RealTimeCombat/RealTimeCombat.cpp b/Source/the_twilight_abyss/RealTimeCombat/RealTimeCombat.cpp new file mode 100644 index 0000000..0f80131 --- /dev/null +++ b/Source/the_twilight_abyss/RealTimeCombat/RealTimeCombat.cpp @@ -0,0 +1,299 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "RealTimeCombat.h" +#include "CoreMinimal.h" +#include "Blueprint/UserWidget.h" +#include "Components/TextBlock.h" +#include "Components/ProgressBar.h" +#include "Kismet/GameplayStatics.h" + + +ARealTimeCombat::ARealTimeCombat() +{ + + if (HUDWidget == nullptr) + { + // Load the HUD widget from the specified path + static ConstructorHelpers::FClassFinder HUDWidgetClass(TEXT("/Game/Blueprints/Combat_UI/Combat_UI")); + HUDWidget = HUDWidgetClass.Class; + } +} + +void ARealTimeCombat::StartCombat(AActor* Enemy) +{ + if (HUD->IsInViewport()) return; + HUD->AddToViewport(); + EnemyActor = Enemy; + + if (APlayerController* PC = Cast(GetWorld()->GetFirstPlayerController())) + { + PC->bShowMouseCursor = true; + PC->bEnableClickEvents = true; + PC->bEnableMouseOverEvents = true; + } +} + +void ARealTimeCombat::EndCombat() +{ + //Remove the HUD from the viewport + HUD->RemoveFromViewport(); +} + +void ARealTimeCombat::BeginPlay() +{ + Super::BeginPlay(); + + TArray AllCharacterActorsInScene; + UGameplayStatics::GetAllActorsOfClassWithTag(GetWorld(), AActor::StaticClass(), FName("Character"), AllCharacterActorsInScene); + + HUD = CreateWidget(GetWorld(), HUDWidget); + + TurnIndicatorTextBlock = Cast(HUD->GetWidgetFromName("TurnIndicator")); + CurrentComboTextBlock = Cast(HUD->GetWidgetFromName("CurrentCombo")); + ActionPointsTextBlock = Cast(HUD->GetWidgetFromName("ActionPoints")); + BattleLogTextBlock = Cast(HUD->GetWidgetFromName("BattleLog")); + PlayerHealthBar = Cast(HUD->GetWidgetFromName("PlayerHealthBar")); + EnemyHealthBar = Cast(HUD->GetWidgetFromName("EnemyHealthBar")); + IronResourceBar = Cast(HUD->GetWidgetFromName("IronResourceBar")); + SulfurResourceBar = Cast(HUD->GetWidgetFromName("SulfurResourceBar")); + CastButton = Cast(HUD->GetWidgetFromName("CastButton")); + FButton = Cast(HUD->GetWidgetFromName("FButton")); + WButton = Cast(HUD->GetWidgetFromName("WButton")); + BackspaceButton = Cast(HUD->GetWidgetFromName("BackspaceButton")); + CastButton->OnClicked.AddDynamic(this, &ARealTimeCombat::CastButtonOnClick); + FButton->OnClicked.AddDynamic(this, &ARealTimeCombat::FButtonOnClick); + WButton->OnClicked.AddDynamic(this, &ARealTimeCombat::WButtonOnClick); + BackspaceButton->OnClicked.AddDynamic(this, &ARealTimeCombat::BackspaceButtonOnClick); +} + +void ARealTimeCombat::ExecuteCast(FString Combo) +{ + if (!IsValidCombo(Combo)) + { + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Invalid Combo")); + //For each character in the current combo add back the resource + for (int i = 0; i < Combo.Len(); i++) + { + if (Combo[i] == 'F') + { + IronResource += 1; + } + else if (Combo[i] == 'W') + { + SulfurResource += 1; + } + } + CurrentComboString = ""; + UpdateComboString(CurrentComboString); + RevertActionPoints(); + UpdateActionPoints(); + UpdateResourceBars(); + return; + } + + CurrentComboString = ""; + UpdateComboString(CurrentComboString); + RevertActionPoints(); + UpdateActionPoints(); + + switch (bIsPlayerTurn) + { + case true: + // Player Turn + DamageEnemy(*ValidCombos.Find(Combo)); + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString::Printf(TEXT("Enemy Damaged %d"), *ValidCombos.Find(Combo))); + break; + case false: + // Enemy Turn + DamagePlayer(*ValidCombos.Find(Combo)); + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, FString::Printf(TEXT("Player Damaged %d"), *ValidCombos.Find(Combo))); + break; + } + //End Combat if either the player or enemy is dead + if (EnemyHealth <= 0) + { + EndCombat(); + EnemyActor->Destroy(); + return; + } + if (PlayerHealth <= 0) + { + EndCombat(); + return; + } + + SwitchTurn(); +} + +void ARealTimeCombat::UseActionPoint() +{ + ActiveActionPoints += 1; + UpdateActionPoints(); +} + +void ARealTimeCombat::ReuseActionPoint() +{ + ActiveActionPoints -= 1; + UpdateActionPoints(); +} + +void ARealTimeCombat::RevertActionPoints() +{ + ActiveActionPoints = 0; + UpdateActionPoints(); +} + +void ARealTimeCombat::DamagePlayer(int Damage) +{ + PlayerHealth -= FMath::Clamp(Damage, 0, 100); + UpdateProgressBars(); + AddBattleLogMessage("Player was damaged for " + FString::FromInt(Damage) + " damage."); +} + +void ARealTimeCombat::DamageEnemy(int Damage) +{ + EnemyHealth -= FMath::Clamp(Damage, 0, 100); + UpdateProgressBars(); + AddBattleLogMessage("Enemy was damaged for " + FString::FromInt(Damage) + " damage."); +} + +void ARealTimeCombat::UpdateProgressBars() const +{ + PlayerHealthBar->SetPercent(PlayerHealth / 100.0f); + EnemyHealthBar->SetPercent(EnemyHealth / 100.0f); +} + +bool ARealTimeCombat::IsValidCombo(FString Combo) const +{ + return ValidCombos.Contains(Combo); +} + +void ARealTimeCombat::SwitchTurn() +{ + //TurnIndicatorTextBlock->SetText(FText::FromString(bIsPlayerTurn ? "Enemy Turn" : "Player Turn")); + //bIsPlayerTurn = !bIsPlayerTurn; + TurnIndicatorTextBlock->SetText(FText::FromString("Enemy Turn")); + ToggleButtons(); + //wait for 2 seconds + FTimerHandle UnusedHandle; + GetWorldTimerManager().SetTimer(UnusedHandle, this, &ARealTimeCombat::EnemyTurn, 2.0f, false); + + //activeActor = bIsPlayerTurn ? enemyActor : playerActor; +} + +void ARealTimeCombat::CastButtonOnClick() +{ + ExecuteCast(CurrentComboString); +} + +void ARealTimeCombat::FButtonOnClick() +{ + if (ActiveActionPoints >= DefaultActionPoints) + { + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Action Points")); + return; + } + if (IronResource <= 0) + { + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Iron")); + return; + } + UseActionPoint(); + CurrentComboString.AppendChar('F'); + UpdateComboString(CurrentComboString); + IronResource -= 1; + UpdateResourceBars(); +} + +void ARealTimeCombat::WButtonOnClick() +{ + if (ActiveActionPoints >= DefaultActionPoints) + { + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Action Points")); + return; + } + if (SulfurResource <= 0) + { + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Sulfur")); + return; + } + UseActionPoint(); + CurrentComboString.AppendChar('W'); + UpdateComboString(CurrentComboString); + SulfurResource -= 1; + UpdateResourceBars(); +} + +void ARealTimeCombat::BackspaceButtonOnClick() +{ + if (CurrentComboString.Len() <= 0) + { + GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Blank Combo")); + return; + } + ReuseActionPoint(); + if (CurrentComboString.Right(1) == "F") + { + IronResource += 1; + } + else if (CurrentComboString.Right(1) == "W") + { + SulfurResource += 1; + } + CurrentComboString.RemoveAt(CurrentComboString.Len() - 1); + UpdateComboString(CurrentComboString); + UpdateResourceBars(); +} + +void ARealTimeCombat::UpdateComboString(FString NewCombo) const +{ + CurrentComboTextBlock->SetText(FText::FromString(NewCombo)); +} + +void ARealTimeCombat::UpdateActionPoints() const +{ + ActionPointsTextBlock->SetText(FText::FromString(FString::FromInt(ActiveActionPoints))); +} + +void ARealTimeCombat::AddBattleLogMessage(FString Message) +{ + BattleLog.Append(Message + "\n"); + UpdateBattleLog(); +} + +void ARealTimeCombat::ClearBattleLog() +{ + BattleLog = ""; +} + +void ARealTimeCombat::UpdateBattleLog() +{ + TArray TempArray; + if (const int32 LineCount = BattleLog.ParseIntoArray(TempArray, TEXT("\n"), true); LineCount > 10) + { + ClearBattleLog(); + } + BattleLogTextBlock->SetText(FText::FromString(BattleLog)); +} + +void ARealTimeCombat::UpdateResourceBars() const +{ + IronResourceBar->SetPercent(IronResource / 10.0f); + SulfurResourceBar->SetPercent(SulfurResource / 10.0f); +} + +void ARealTimeCombat::ToggleButtons() const +{ + FButton->SetIsEnabled(!FButton->bIsEnabled); + WButton->SetIsEnabled(!WButton->bIsEnabled); + BackspaceButton->SetIsEnabled(!BackspaceButton->bIsEnabled); + CastButton->SetIsEnabled(!CastButton->bIsEnabled); +} + +void ARealTimeCombat::EnemyTurn() +{ + DamagePlayer(10); + TurnIndicatorTextBlock->SetText(FText::FromString("Player Turn")); + ToggleButtons(); +} \ No newline at end of file diff --git a/Source/the_twilight_abyss/RealTimeCombat/RealTimeCombat.h b/Source/the_twilight_abyss/RealTimeCombat/RealTimeCombat.h new file mode 100644 index 0000000..7b75b4c --- /dev/null +++ b/Source/the_twilight_abyss/RealTimeCombat/RealTimeCombat.h @@ -0,0 +1,138 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "Components/Button.h" +#include "Components/ProgressBar.h" +#include "Components/TextBlock.h" +#include "GameFramework/GameStateBase.h" +#include "RealTimeCombat.generated.h" + +UCLASS() +class THE_TWILIGHT_ABYSS_API ARealTimeCombat : public AActor +{ + GENERATED_BODY() + ARealTimeCombat(); + + UPROPERTY(EditDefaultsOnly) + int PlayerHealth = 100; + UPROPERTY(EditDefaultsOnly) + int EnemyHealth = 100; + UPROPERTY(EditDefaultsOnly) + int DefaultActionPoints = 3; + UPROPERTY(EditDefaultsOnly) + int ActiveActionPoints = 0; + UPROPERTY(EditDefaultsOnly) + int IronResource = 10; // F + UPROPERTY(EditDefaultsOnly) + int SulfurResource = 10; // W + + // AActor* PlayerActor; + UPROPERTY(VisibleAnywhere) + AActor* EnemyActor; + // AActor* ActiveActor; + + /* + TODO: + Reference Player Inventory + */ + UPROPERTY(EditAnywhere) + TSubclassOf HUDWidget; + UPROPERTY(EditAnywhere) + TMap ValidCombos = + { + {"F", 10}, + {"W", 10}, + {"WW", 15}, + {"FW", 20}, + {"FFW", 30} + }; + + FString BattleLog; + + UFUNCTION(BlueprintCallable) + void StartCombat(AActor* Enemy); + UFUNCTION(BlueprintCallable) + void EndCombat(); + +protected: + virtual void BeginPlay() override; + void ExecuteCast(FString Combo); + void UseActionPoint(); + void ReuseActionPoint(); + void RevertActionPoints(); + void DamagePlayer(int Damage); + void DamageEnemy(int Damage); + void UpdateProgressBars() const; + +private: + bool IsValidCombo(FString Combo) const; + UUserWidget* HUD; + + UPROPERTY(VisibleAnywhere) + bool bIsPlayerTurn = true; + + UPROPERTY(VisibleAnywhere) + FString CurrentComboString = ""; + + void SwitchTurn(); + + UPROPERTY(VisibleAnywhere) + UTextBlock* TurnIndicatorTextBlock; + + UPROPERTY(VisibleAnywhere) + UTextBlock* CurrentComboTextBlock; + + UPROPERTY(VisibleAnywhere) + UTextBlock* BattleLogTextBlock; + + UPROPERTY(VisibleAnywhere) + UTextBlock* ActionPointsTextBlock; + + UPROPERTY(VisibleAnywhere) + UProgressBar* PlayerHealthBar; + + UPROPERTY(VisibleAnywhere) + UProgressBar* EnemyHealthBar; + + UPROPERTY(VisibleAnywhere) + UProgressBar* IronResourceBar; + + UPROPERTY(VisibleAnywhere) + UProgressBar* SulfurResourceBar; + + UPROPERTY(VisibleAnywhere) + UButton* CastButton; + + UPROPERTY(VisibleAnywhere) + UButton* FButton; + + UPROPERTY(VisibleAnywhere) + UButton* WButton; + + UPROPERTY(VisibleAnywhere) + UButton* BackspaceButton; + + UFUNCTION() + void CastButtonOnClick(); + + UFUNCTION() + void FButtonOnClick(); + + UFUNCTION() + void WButtonOnClick(); + + UFUNCTION() + void BackspaceButtonOnClick(); + + void UpdateComboString(FString NewCombo) const; + void UpdateActionPoints() const; + + void AddBattleLogMessage(FString Message); + void ClearBattleLog(); + void UpdateBattleLog(); + void UpdateResourceBars() const; + void ToggleButtons() const; + void EnemyTurn(); +};