// 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() { PrimaryActorTick.bCanEverTick = true; if (HUDWidget == nullptr) { // Load the HUD widget from the specified path static ConstructorHelpers::FClassFinder HUDWidgetClass(TEXT("/Game/Blueprints/Combat_UI/Combat_UI_RT")); HUDWidget = HUDWidgetClass.Class; } } void ARealTimeCombat::StartCombat() { if (HUD->IsInViewport()) return; HUD->AddToViewport(); bStartTimer = true; //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::OnMouseClick() { ExecuteCast(CurrentComboString); } void ARealTimeCombat::OnQPress() { if (ActiveActionPoints <= 0) { 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::OnEPress() { if (ActiveActionPoints <= 0) { 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::OnBackspacePress() { 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::BeginPlay() { Super::BeginPlay(); TArray AllCharacterActorsInScene; UGameplayStatics::GetAllActorsOfClassWithTag(GetWorld(), AActor::StaticClass(), FName("Enemy"), AllCharacterActorsInScene); for (AActor* Actor : AllCharacterActorsInScene) { EnemyActor = Cast(Actor); } UGameplayStatics::GetAllActorsOfClassWithTag(GetWorld(), AActor::StaticClass(), FName("Player"), AllCharacterActorsInScene); for (AActor* Actor : AllCharacterActorsInScene) { PlayerActor = Cast(Actor); } HUD = CreateWidget(GetWorld(), HUDWidget); 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")); ActionPointsBar = Cast(HUD->GetWidgetFromName("ActionPointsBar")); } 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(); if (!HitEnemy()) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Missed")); return; } 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() { if (HeldActionPoints > 0 && ActiveActionPoints <= HeldActionPoints) { ActiveActionPoints -= 1; UpdateActionPoints(); } } void ARealTimeCombat::ReuseActionPoint() { ActiveActionPoints += 1; UpdateActionPoints(); } void ARealTimeCombat::RevertActionPoints() { ActiveActionPoints = HeldActionPoints; 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); } void ARealTimeCombat::Tick(float DeltaSeconds) { Super::Tick(DeltaSeconds); if (ActionPointsTimer >= 1.0f && HeldActionPoints < MaxActionPoints && bStartTimer) { HeldActionPoints += 1; ActiveActionPoints += 1; UpdateActionPoints(); ActionPointsTimer = 0.0f; } else { ActionPointsTimer += DeltaSeconds; ActionPointsBar->SetPercent(ActionPointsTimer); } } bool ARealTimeCombat::IsValidCombo(FString Combo) const { return ValidCombos.Contains(Combo); } 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); } bool ARealTimeCombat::HitEnemy() const { FHitResult HitResult; FVector Start = PlayerActor->GetActorLocation(); FVector End = PlayerActor->GetActorForwardVector() * 1000.0f + Start; FCollisionQueryParams CollisionParams; CollisionParams.AddIgnoredActor(PlayerActor); if (GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Pawn, CollisionParams)) { DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 5.0f, 0, 10.0f); if (HitResult.GetActor() == EnemyActor) { return true; } } DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 5.0f, 0, 10.0f); return false; }