// Fill out your copyright notice in the Description page of Project Settings. #include "TurnBaseCombatV2.h" #include "CoreMinimal.h" #include "AIController.h" #include "StatusSystem.h" #include "Blueprint/UserWidget.h" #include "BehaviorTree/BlackboardComponent.h" #include "Components/TextBlock.h" #include "Components/ProgressBar.h" #include "Kismet/GameplayStatics.h" #include "Kismet/KismetMathLibrary.h" #include "Misc/OutputDeviceNull.h" #include "the_twilight_abyss/Dialogue/InteractNPC.h" #include "the_twilight_abyss/PlayerTemp/TempCharacter.h" #include "the_twilight_abyss/Quest/QuestSystem.h" // Sets default values ATurnBaseCombatV2::ATurnBaseCombatV2() { PrimaryActorTick.bCanEverTick = true; if (HUDWidget == nullptr) { static ConstructorHelpers::FClassFinder HUDWidgetClass(TEXT("/Game/Blueprints/Combat_UI/Combat_UI")); HUDWidget = HUDWidgetClass.Class; static ConstructorHelpers::FClassFinder StatusEffectThornsClassFinder(TEXT("/Game/Blueprints/StatusEffects/BP_Thorns")); ThornsStatusEffect = StatusEffectThornsClassFinder.Class; static ConstructorHelpers::FClassFinder StatusEffectDotClassFinder(TEXT("/Game/Blueprints/StatusEffects/BP_DamageOverTime")); DOTStatusEffect = StatusEffectDotClassFinder.Class; static ConstructorHelpers::FClassFinder StatusEffectDamageDownClassFinder(TEXT("/Game/Blueprints/StatusEffects/BP_DamageDown")); DamageDownStatusEffect = StatusEffectDamageDownClassFinder.Class; static ConstructorHelpers::FClassFinder StatusEffectDamageDownPlayerClassFinder(TEXT("/Game/Blueprints/StatusEffects/BP_DamageDownPlayer")); DamageDownPlayerStatusEffect = StatusEffectDamageDownPlayerClassFinder.Class; static ConstructorHelpers::FClassFinder DeathScreenWidgetClass(TEXT("/Game/Blueprints/Death_UI/Death_UI")); DeathScreenWidgetSubclass = DeathScreenWidgetClass.Class; static ConstructorHelpers::FClassFinder CombatTutorialWidgetClass(TEXT("/Game/Blueprints/Combat_UI/CombatTutorial")); CombatTutorialWidget = CombatTutorialWidgetClass.Class; } } void ATurnBaseCombatV2::StartCombat(AActor* Enemy, const bool bWasShot) { if (Enemy == nullptr) return; if (bIsInCombat) return; EnemyActor = Enemy; StatusTextBlock->SetRenderOpacity(0.0f); BookHUD->SetVisibility(ESlateVisibility::SelfHitTestInvisible); HUD->AddToViewport(-1); BookStaticMeshComponent->SetVisibility(true); EscapePercentage = CalculateEscapePercentage(); EscapePercentageTextBlock->SetText(FText::Join(FText::FromString(""), FText::FromString(FString::FromInt(EscapePercentage * 100)), FText::FromString("%"))); bIsInCombat = true; UBlackboardComponent* EnemyBlackboard = Cast(EnemyActor->GetInstigatorController())->GetBlackboardComponent(); Cast(PlayerActor->GetComponentByClass(UQuestSystem::StaticClass()))->QuestWidgetInstance->SetVisibility(ESlateVisibility::Hidden); HealingJellyAmountTextBlock->SetText(FText::FromString(FString::FromInt(FMath::Clamp(Cast(PlayerActor)->Inventory->GetItemAmount(0), 0, 99)))); if (Cast(PlayerActor)->bIsInDialogue) { Cast(PlayerActor->GetComponentByClass(UInteractNPC::StaticClass()))->EndInteract(); } if (Cast(PlayerActor)->bShopKeeperText) { Cast(PlayerActor)->ExitMerchant(); } FOutputDeviceNull AR; const FString Command = FString::Printf(TEXT("CloseInventory")); PlayerActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true); const FString Command69 = FString::Printf(TEXT("CloseEscapeMenu")); PlayerActor->CallFunctionByNameWithArguments(*Command69, AR, nullptr, true); //Disable Character Movement APlayerController* PlayerController = GetWorld()->GetFirstPlayerController(); PlayerController->SetIgnoreMoveInput(true); PlayerController->SetIgnoreLookInput(true); PlayerController->SetInputMode(FInputModeGameAndUI()); PlayerController->bShowMouseCursor = true; EnemyBlackboard->SetValueAsBool("IsInCombat", true); const FProperty* HealthProperty = Enemy->GetClass()->FindPropertyByName(FName("Health")); int32* EnemyHealthPtr = HealthProperty->ContainerPtrToValuePtr(Enemy); EnemyHealth = EnemyHealthPtr; FProperty* IsBossProperty = FindFieldChecked(EnemyActor->GetClass(), "IsBoss"); if (const FBoolProperty* IsBossBoolProperty = CastFieldChecked(IsBossProperty); IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor)) { const FString Command123 = FString::Printf(TEXT("TriggerCombatAnimation true")); EnemyActor->CallFunctionByNameWithArguments(*Command123, AR, nullptr, true); FVector Direction = EnemyActor->GetActorLocation() - PlayerActor->GetActorLocation(); Direction.Normalize(); FRotator LookAtRotation = FRotationMatrix::MakeFromX(Direction).Rotator(); const FRotator NewRotation = UKismetMathLibrary::FindLookAtRotation(Cast(PlayerActor->FindComponentByClass())->GetComponentLocation(), EnemyActor->GetActorLocation()); LookAtRotation.Pitch = NewRotation.Pitch - PlayerActor->GetActorRotation().Pitch; LookAtRotation.Pitch += 10; //Offset PlayerController->SetControlRotation(LookAtRotation); EnemyName = FText::FromString("Crystal Mutant"); RunButton->SetIsEnabled(false); } else EnemyName = FText::FromString("Feral Goat"); if (IsValid(CombatTutorialWidgetInstance) && CombatTutorialWidgetInstance->IsInViewport()) return; if (!HasSeenTutorial) { DisableButtons(); CombatTutorialWidgetInstance->AddToViewport(); FProperty* Property = FindFieldChecked(CombatTutorialWidgetInstance->GetClass(), "WasShot"); const FBoolProperty* WasShotProperty = CastFieldChecked(Property); WasShotProperty->SetPropertyValue_InContainer(CombatTutorialWidgetInstance, bWasShot); HasSeenTutorial = true; return; } CombatCheck(bWasShot); //DrawDebugPoint(GetWorld(), Enemy->GetActorLocation(), 10, FColor::Red, false, 10); } void ATurnBaseCombatV2::CombatCheck(const bool bWasShot) { const UBlackboardComponent* EnemyBlackboard = Cast(EnemyActor->GetInstigatorController())->GetBlackboardComponent(); ChainDamageMultiplier = 0; BaseDamageMultiplier = DefaultBaseDamageMultiplier; EnemyBaseDamageMultiplier = DefaultEnemyBaseDamageMultiplier; if (bIsBuffed) BaseDamageMultiplier += 1.0f; DamageMultiplierTextBlock->SetText(FText::FromString("")); const FProperty* ReactionSpeedProperty = EnemyActor->GetClass()->FindPropertyByName(FName("ReactionSpeed")); const float* EnemyReactionSpeedPtr = ReactionSpeedProperty->ContainerPtrToValuePtr(EnemyActor); CurrentComboString = ""; UpdateComboString(CurrentComboString); RevertActionPoints(); UpdateActionPoints(); UpdateResourceBars(); UpdateProgressBars(); ClearBattleLog(); EnableButtons(); if (EnemyBlackboard->GetValueAsBool("Sight") && !bWasShot) { //bEnemyHasExtraTurn = true; SwitchTurn(); return; } else if (Cast(PlayerActor)->ReactionSpeed > *EnemyReactionSpeedPtr && bWasShot) { TurnIndicatorTextBlock->SetText(FText::FromString("Player Turn\nExtra")); bPlayerHasExtraTurn = true; return; } TurnIndicatorTextBlock->SetText(FText::FromString("Player Turn")); } void ATurnBaseCombatV2::EndCombat() { GetWorldTimerManager().ClearTimer(EnemyTurnTimerHandle); BookHUD->SetVisibility(ESlateVisibility::Hidden); HUD->RemoveFromParent(); BookStaticMeshComponent->SetVisibility(false); Cast(PlayerActor->GetComponentByClass(UQuestSystem::StaticClass()))->QuestWidgetInstance->SetVisibility(ESlateVisibility::HitTestInvisible); Cast(PlayerActor)->ResetWidgetPointer(); TurnIndicatorTextBlock->SetText(FText::FromString("")); bEnemyHasExtraTurn = false; bPlayerHasExtraTurn = false; bIsInCombat = false; BaseDamageMultiplier -= ChainDamageMultiplier; PreviousComboString = ""; for (UStatusEffect* StatusEffect : StatusEffects) { StatusEffect->OnExpiry(PlayerActor); } StatusEffects.Empty(); HUD->RemoveFromParent(); APawn* PlayerPawn = Cast(GetWorld()->GetFirstPlayerController()->GetPawn()); PlayerPawn->bUseControllerRotationYaw = true; PlayerPawn->bUseControllerRotationPitch = true; //Enable Character Movement //Set to Game Mode Only APlayerController* PlayerController = GetWorld()->GetFirstPlayerController(); PlayerController->SetInputMode(FInputModeGameOnly()); PlayerController->SetIgnoreMoveInput(false); PlayerController->SetIgnoreLookInput(false); PlayerController->bShowMouseCursor = false; //Enable Character Movement if (ACharacter* PlayerCharacter = Cast(GetWorld()->GetFirstPlayerController()->GetPawn())) { PlayerCharacter->EnableInput(GetWorld()->GetFirstPlayerController()); } if (IsValid(EnemyActor)) { FProperty* IsBossProperty = FindFieldChecked(EnemyActor->GetClass(), "IsBoss"); const FBoolProperty* IsBossBoolProperty = CastFieldChecked(IsBossProperty); if (IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor)) { FOutputDeviceNull AR; const FString Command = FString::Printf(TEXT("TriggerCombatAnimation false")); EnemyActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true); } } } void ATurnBaseCombatV2::BeginPlay() { Super::BeginPlay(); TArray AllCharacterActorsInScene; UGameplayStatics::GetAllActorsOfClassWithTag(GetWorld(), AActor::StaticClass(), FName("Player"), AllCharacterActorsInScene); for (AActor* Actor : AllCharacterActorsInScene) { PlayerActor = Cast(Actor); } PlayerHealth = &Cast(PlayerActor)->Health; TArray AllActorsInScene; UGameplayStatics::GetAllActorsOfClass(GetWorld(), APostProcessVolume::StaticClass(), AllActorsInScene); if (ensureMsgf(AllActorsInScene.Num() > 0, TEXT("No Post Processing Volume in scene"))) { PostProcessVolume = Cast(AllActorsInScene[0]); } if (IsValid(RedVignetteMaterialInstance)) { FWeightedBlendable WeightedBlendable; WeightedBlendable.Object = RedVignetteMaterialInstance; WeightedBlendable.Weight = 1.0f; if (IsValid(PostProcessVolume)) PostProcessVolume->Settings.WeightedBlendables.Array.Add(WeightedBlendable); } TArray StaticMeshComponents; PlayerActor->GetComponents(StaticMeshComponents); for (UStaticMeshComponent* StaticMeshComponent : StaticMeshComponents) { if (StaticMeshComponent->ComponentTags.Num() == 0) continue; if (StaticMeshComponent->ComponentTags[0] == "Book") { BookStaticMeshComponent = StaticMeshComponent; BookStaticMeshComponent->SetVisibility(false); } } CombatTutorialWidgetInstance = CreateWidget(GetWorld(), CombatTutorialWidget); HUD = CreateWidget(GetWorld(), HUDWidget); TArray PlayerChildActors; PlayerActor->GetAllChildActors(PlayerChildActors, false); PlayerWidget = Cast(PlayerChildActors[0]->GetComponentByClass(UWidgetComponent::StaticClass())); PlayerWidget->InitWidget(); BookHUD = PlayerWidget->GetWidget(); TurnIndicatorTextBlock = Cast(BookHUD->GetWidgetFromName("TurnIndicator")); CurrentComboTextBlock = Cast(BookHUD->GetWidgetFromName("CurrentCombo")); CurrentComboTextBlock1 = Cast(BookHUD->GetWidgetFromName("CurrentCombo_1")); CurrentComboTextBlock2 = Cast(BookHUD->GetWidgetFromName("CurrentCombo_2")); BattleLogTextBlock = Cast(HUD->GetWidgetFromName("BattleLog")); EscapePercentageTextBlock = Cast(BookHUD->GetWidgetFromName("EscapePercentage_Text")); DamageMultiplierTextBlock = Cast(BookHUD->GetWidgetFromName("DamageMultiplier_Text")); HealingJellyAmountTextBlock = Cast(BookHUD->GetWidgetFromName("HealingAmount_Text")); DamageAmountEnemyTextBlock = Cast(HUD->GetWidgetFromName("DamageAmountEnemy_Text")); DamageAmountPlayerTextBlock = Cast(BookHUD->GetWidgetFromName("DamageAmountPlayer_Text")); StatusTextBlock = Cast(HUD->GetWidgetFromName("Status_Text")); PlayerHealthBar = Cast(BookHUD->GetWidgetFromName("PlayerHealthBar")); EnemyHealthBar = Cast(HUD->GetWidgetFromName("EnemyHealthBar")); ProbertiumResourceBar = Cast(BookHUD->GetWidgetFromName("ProbertiumResourceBar")); EisResourceBar = Cast(BookHUD->GetWidgetFromName("EisResourceBar")); AzosResourceBar = Cast(BookHUD->GetWidgetFromName("AzosResourceBar")); IroquoidResourceBar = Cast(BookHUD->GetWidgetFromName("IroquoidResourceBar")); CastButton = Cast(BookHUD->GetWidgetFromName("Cast_Button")); PButton = Cast(BookHUD->GetWidgetFromName("Probertium_Button")); EButton = Cast(BookHUD->GetWidgetFromName("Eis_Button")); AButton = Cast(BookHUD->GetWidgetFromName("Azos_Button")); IButton = Cast(BookHUD->GetWidgetFromName("Iroquoid_Button")); BackspaceButton = Cast(BookHUD->GetWidgetFromName("Clear_Button")); RunButton = Cast(BookHUD->GetWidgetFromName("Escape_Button")); HealButton = Cast(BookHUD->GetWidgetFromName("Heal_Button")); CastButton->OnClicked.AddDynamic(this, &ATurnBaseCombatV2::CastButtonOnClick); PButton->OnClicked.AddDynamic(this, &ATurnBaseCombatV2::PButtonOnClick); EButton->OnClicked.AddDynamic(this, &ATurnBaseCombatV2::EButtonOnClick); AButton->OnClicked.AddDynamic(this, &ATurnBaseCombatV2::AButtonOnClick); IButton->OnClicked.AddDynamic(this, &ATurnBaseCombatV2::IButtonOnClick); BackspaceButton->OnClicked.AddDynamic(this, &ATurnBaseCombatV2::BackspaceButtonOnClick); RunButton->OnClicked.AddDynamic(this, &ATurnBaseCombatV2::RunButtonOnClick); HealButton->OnClicked.AddDynamic(this, &ATurnBaseCombatV2::HealButtonOnClick); } void ATurnBaseCombatV2::Tick(const float DeltaTime) { Super::Tick(DeltaTime); if (bIsInCombat) { APlayerController* PlayerController = GetWorld()->GetFirstPlayerController(); FProperty* IsBossProperty = FindFieldChecked(EnemyActor->GetClass(), "IsBoss"); if (const FBoolProperty* IsBossBoolProperty = CastFieldChecked(IsBossProperty); !IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor)) { FVector Direction = EnemyActor->GetActorLocation() - PlayerActor->GetActorLocation(); Direction.Normalize(); FRotator LookAtRotation = FRotationMatrix::MakeFromX(Direction).Rotator(); const FRotator NewRotation = UKismetMathLibrary::FindLookAtRotation(Cast(PlayerActor->FindComponentByClass())->GetComponentLocation(), Cast(EnemyActor->FindComponentByClass())->GetComponentLocation()); LookAtRotation.Pitch = NewRotation.Pitch - PlayerActor->GetActorRotation().Pitch; PlayerController->SetControlRotation(LookAtRotation); } } } void ATurnBaseCombatV2::ExecuteCast(FString Combo) { if (!IsValidCombo(Combo)) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Invalid Combo")); for (int i = 0; i < Combo.Len(); i++) { if (Combo[i] == 'P') { ProbertiumResource += 1; } else if (Combo[i] == 'E') { EisResource += 1; } else if (Combo[i] == 'A') { AzosResource += 1; } else if (Combo[i] == 'I') { IroquoidResource += 1; } } CurrentComboString = ""; UpdateComboString(CurrentComboString); RevertActionPoints(); UpdateActionPoints(); UpdateResourceBars(); return; } if (IsSpecialCombo(Combo)) { FOutputDeviceNull AR; UStatusSystem* StatusSystem = Cast(PlayerActor->GetComponentByClass(UStatusSystem::StaticClass())); if (Combo == "PA" || Combo == "AP") { UStatusEffect* TempThornsStatusEffect = NewObject(PlayerActor, ThornsStatusEffect); StatusSystem->AddStatusEffect(TempThornsStatusEffect, 1, false); StatusEffects.Add(TempThornsStatusEffect); AddBattleLogMessage("Player Casted Thorns"); StatusTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); StatusTextBlock->SetText(FText::FromString("Casted Thorns")); const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation")); HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); } else if (Combo == "PI" || Combo == "IP") { UStatusEffect* TempDOTStatusEffect = NewObject(PlayerActor, DOTStatusEffect); StatusSystem->AddStatusEffect(TempDOTStatusEffect, 1, false); StatusEffects.Add(TempDOTStatusEffect); AddBattleLogMessage("Player Casted DOT"); StatusTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); StatusTextBlock->SetText(FText::FromString("Casted DOT")); const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation")); HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); } else if (Combo == "AI" || Combo == "IA") { UStatusEffect* TempDamageDownStatusEffect = NewObject(PlayerActor, DamageDownStatusEffect); StatusSystem->AddStatusEffect(TempDamageDownStatusEffect, 1, true); StatusEffects.Add(TempDamageDownStatusEffect); AddBattleLogMessage("Player Casted DamageDown"); StatusTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); StatusTextBlock->SetText(FText::FromString("Enemy Damage Down")); const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation")); HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); } } if (GunEffect) { const UStaticMeshComponent* GunComponent = Cast(PlayerActor->GetComponentsByTag(UPrimitiveComponent::StaticClass(), FName("Gun"))[0]); const FVector GunLocationOffset = GunComponent->GetSocketTransform("Muzzle").GetLocation(); UNiagaraFunctionLibrary::SpawnSystemAtLocation(GetWorld(), GunEffect, GunLocationOffset, Cast(PlayerActor)->Controller->GetControlRotation()); FOutputDeviceNull AR; const FString Command = FString::Printf(TEXT("PlayCameraShakeShoot")); PlayerActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true); } if (ChainDamageMultiplier == 1) BaseDamageMultiplier = 1; if ((CurrentComboString == "II" || CurrentComboString == "PP" || CurrentComboString == "EE" || CurrentComboString == "AA") && (PreviousComboString == "II" || PreviousComboString == "PP" || PreviousComboString == "EE" || PreviousComboString == "AA")) { ChainDamageMultiplier += ChainDamageMultiplierIncrease; DamageMultiplierTextBlock->SetText(FText::Join(FText::FromString(""), FText::FromString("x"), FText::FromString(FString::SanitizeFloat(ChainDamageMultiplier + BaseDamageMultiplier)))); } else if (CurrentComboString == "EI") { BaseDamageMultiplier += ChainDamageMultiplier; DamageMultiplierTextBlock->SetText(FText::FromString("")); } PreviousComboString = CurrentComboString; CurrentComboString = ""; UpdateComboString(CurrentComboString); RevertActionPoints(); UpdateActionPoints(); //Damage Calculation switch (bIsPlayerTurn) { case true: // Player Turn DamageEnemy(ValidCombos.Contains(Combo) ? *ValidCombos.Find(Combo) : *ValidCombos.Find(Combo.Reverse())); OnPlayerTurn.Broadcast(PlayerActor, EnemyActor); break; case false: // Enemy Turn DamagePlayer(*ValidCombos.Find(Combo)); OnEnemyTurn.Broadcast(EnemyActor, PlayerActor); break; } if (!bPlayerHasExtraTurn) { FProperty* IsBossProperty = FindFieldChecked(EnemyActor->GetClass(), "IsBoss"); const FBoolProperty* IsBossBoolProperty = CastFieldChecked(IsBossProperty); if (IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor) && EnemyHealth <= nullptr) return; SwitchTurn(); } else { TurnIndicatorTextBlock->SetText(FText::FromString("Player Turn")); bPlayerHasExtraTurn = false; } } void ATurnBaseCombatV2::UseActionPoint() { ActiveActionPoints += 1; UpdateActionPoints(); } void ATurnBaseCombatV2::ReuseActionPoint() { ActiveActionPoints -= 1; UpdateActionPoints(); } void ATurnBaseCombatV2::RevertActionPoints() { ActiveActionPoints = 0; UpdateActionPoints(); } void ATurnBaseCombatV2::DamagePlayer(int Damage, const FString& DamageType) { FOutputDeviceNull AR; *PlayerHealth -= FMath::Clamp(Damage * EnemyBaseDamageMultiplier, 0, 100); DamageAmountPlayerTextBlock->SetText(FText::FromString("-" + FString::FromInt(Damage * EnemyBaseDamageMultiplier))); const FString Command3 = FString::Printf(TEXT("PlayDamagePlayerTextAnimation")); BookHUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); UpdateProgressBars(); AddBattleLogMessage("Player was damaged for " + FString::FromInt(Damage * EnemyBaseDamageMultiplier) + " HP by " + DamageType + "."); if (*EnemyHealth <= 0) { FProperty* IsBossProperty = FindFieldChecked(EnemyActor->GetClass(), "IsBoss"); const FBoolProperty* IsBossBoolProperty = CastFieldChecked(IsBossProperty); if (IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor)) { const FString Command = FString::Printf(TEXT("TriggerDeathAnimation")); EnemyActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true); for (UStatusEffect* StatusEffect : StatusEffects) { StatusEffect->OnExpiry(PlayerActor); } StatusEffects.Empty(); return; } EndCombat(); Cast(PlayerActor)->GoldBalance += 100; EnemyActor->Destroy(); return; } if (*PlayerHealth <= 0) { //EndCombat(); DeathScreenWidget = CreateWidget(GetWorld(), DeathScreenWidgetSubclass); DeathScreenWidget->AddToViewport(); return; } const FString Command = FString::Printf(TEXT("PlayCameraShakeHurt")); PlayerActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true); RedVignetteMaterialInstance->SetScalarParameterValue(FName("BlendWeight"), 1.0f); const FString Command2 = FString::Printf(TEXT("TriggerAttackAnimation")); EnemyActor->CallFunctionByNameWithArguments(*Command2, AR, nullptr, true); } void ATurnBaseCombatV2::DamageEnemy(int Damage, const FString& DamageType) { *EnemyHealth -= FMath::Clamp(Damage * BaseDamageMultiplier, 0, 100); UpdateProgressBars(); DamageAmountEnemyTextBlock->SetText(FText::FromString("-" + FString::FromInt(Damage * BaseDamageMultiplier))); FOutputDeviceNull AR; const FString Command = FString::Printf(TEXT("PlayDamageEnemyTextAnimation")); HUD->CallFunctionByNameWithArguments(*Command, AR, nullptr, true); AddBattleLogMessage("Enemy was damaged for " + FString::FromInt(Damage * BaseDamageMultiplier) + " HP by " + DamageType + "."); if (DamageMultiplierTextBlock->GetText().ToString() == "") { BaseDamageMultiplier = DefaultBaseDamageMultiplier; if (bIsBuffed) BaseDamageMultiplier += 1.0f; ChainDamageMultiplier = 0; } FProperty* IsBossProperty = FindFieldChecked(EnemyActor->GetClass(), "IsBoss"); const FBoolProperty* IsBossBoolProperty = CastFieldChecked(IsBossProperty); if (IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor)) { const FString Command2 = FString::Printf(TEXT("TriggerDamageAnimation")); EnemyActor->CallFunctionByNameWithArguments(*Command2, AR, nullptr, true); } //Ends Combat if either the player or enemy is dead if (*EnemyHealth <= 0) { if (IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor)) { const FString Command2 = FString::Printf(TEXT("TriggerDeathAnimation")); EnemyActor->CallFunctionByNameWithArguments(*Command2, AR, nullptr, true); for (UStatusEffect* StatusEffect : StatusEffects) { StatusEffect->OnExpiry(PlayerActor); } StatusEffects.Empty(); return; } EndCombat(); Cast(PlayerActor)->GoldBalance += 100; EnemyActor->Destroy(); return; } if (*PlayerHealth <= 0) { //EndCombat(); DeathScreenWidget = CreateWidget(GetWorld(), DeathScreenWidgetSubclass); DeathScreenWidget->AddToViewport(); return; } } void ATurnBaseCombatV2::UpdateProgressBars() const { const FProperty* MaxHealthProperty = EnemyActor->GetClass()->FindPropertyByName(FName("MaxHealth")); const int32* EnemyMaxHealthPtr = MaxHealthProperty->ContainerPtrToValuePtr(EnemyActor); EnemyHealthBar->SetPercent(static_cast(*EnemyHealth) / static_cast(*EnemyMaxHealthPtr)); } float ATurnBaseCombatV2::CalculateEscapePercentage() const { return FMath::RandRange(0.1f, 0.9f); } bool ATurnBaseCombatV2::IsValidCombo(const FString& Combo) const { if (ValidCombos.Contains(Combo) || ValidCombos.Contains(Combo.Reverse())) return true; return false; } bool ATurnBaseCombatV2::IsSpecialCombo(const FString& Combo) const { if (SpecialCombos.Contains(Combo) || SpecialCombos.Contains(Combo.Reverse())) return true; return false; } void ATurnBaseCombatV2::SwitchTurn() { //TurnIndicatorTextBlock->SetText(FText::FromString(bIsPlayerTurn ? "Enemy Turn" : "Player Turn")); //bIsPlayerTurn = !bIsPlayerTurn; TurnIndicatorTextBlock->SetText(FText::FromString("Enemy Turn")); DisableButtons(); GetWorldTimerManager().SetTimer(EnemyTurnTimerHandle, this, &ATurnBaseCombatV2::EnemyTurn, 2.0f, false); //activeActor = bIsPlayerTurn ? enemyActor : playerActor; } void ATurnBaseCombatV2::CastButtonOnClick() { ExecuteCast(CurrentComboString); } void ATurnBaseCombatV2::PButtonOnClick() { if (ActiveActionPoints >= DefaultActionPoints) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Action Points")); return; } if (ProbertiumResource <= 0) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Probertium")); return; } UseActionPoint(); CurrentComboString.AppendChar('P'); UpdateComboString(CurrentComboString); ProbertiumResource -= 1; UpdateResourceBars(); } void ATurnBaseCombatV2::EButtonOnClick() { if (ActiveActionPoints >= DefaultActionPoints) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Action Points")); return; } if (EisResource <= 0) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Eis")); return; } UseActionPoint(); CurrentComboString.AppendChar('E'); UpdateComboString(CurrentComboString); EisResource -= 1; UpdateResourceBars(); } void ATurnBaseCombatV2::AButtonOnClick() { if (ActiveActionPoints >= DefaultActionPoints) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Action Points")); return; } if (AzosResource <= 0) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Azos")); return; } UseActionPoint(); CurrentComboString.AppendChar('A'); UpdateComboString(CurrentComboString); AzosResource -= 1; UpdateResourceBars(); } void ATurnBaseCombatV2::IButtonOnClick() { if (ActiveActionPoints >= DefaultActionPoints) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Action Points")); return; } if (IroquoidResource <= 0) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("No More Iroquoid")); return; } UseActionPoint(); CurrentComboString.AppendChar('I'); UpdateComboString(CurrentComboString); IroquoidResource -= 1; UpdateResourceBars(); } void ATurnBaseCombatV2::BackspaceButtonOnClick() { if (CurrentComboString.Len() <= 0) { GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("Blank Combo")); return; } for (int i = 0; i < CurrentComboString.Len(); i++) { if (CurrentComboString[i] == 'P') { ProbertiumResource += 1; } else if (CurrentComboString[i] == 'E') { EisResource += 1; } else if (CurrentComboString[i] == 'A') { AzosResource += 1; } else if (CurrentComboString[i] == 'I') { IroquoidResource += 1; } } CurrentComboString = ""; UpdateComboString(CurrentComboString); RevertActionPoints(); UpdateActionPoints(); UpdateResourceBars(); } void ATurnBaseCombatV2::RunButtonOnClick() { if (FMath::RandRange(0.0f, 1.0f) >= EscapePercentage) { StatusTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor(1.0f, 0.0f, 0.0f, 1.0f))); StatusTextBlock->SetText(FText::FromString("Escape Failed")); FOutputDeviceNull AR; const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation")); HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); EscapePercentage = CalculateEscapePercentage(); EscapePercentageTextBlock->SetText(FText::Join(FText::FromString(""), FText::FromString(FString::FromInt(EscapePercentage * 100)), FText::FromString("%"))); SwitchTurn(); return; } GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Green, TEXT("Escape Successful")); UBlackboardComponent* EnemyBlackboard = Cast(EnemyActor->GetInstigatorController())->GetBlackboardComponent(); EscapePercentageTextBlock->SetText(FText::Join(FText::FromString(""), FText::FromString("--"), FText::FromString("%"))); EnemyBlackboard->SetValueAsBool("IsInCombat", false); EnemyBlackboard->SetValueAsBool("WasInCombat", true); EndCombat(); } void ATurnBaseCombatV2::HealButtonOnClick() { if (Cast(PlayerActor)->Inventory->GetItemAmount(0) >= 1) { Cast(PlayerActor)->Inventory->GetItemByID(0)->Use(Cast(PlayerActor)); HealingJellyAmountTextBlock->SetText(FText::FromString(FString::FromInt(FMath::Clamp(Cast(PlayerActor)->Inventory->GetItemAmount(0), 0, 99)))); UpdateResourceBars(); SwitchTurn(); } } void ATurnBaseCombatV2::UpdateComboString(const FString& NewCombo) const { CurrentComboTextBlock->SetText(FText::FromString("?")); CurrentComboTextBlock1->SetText(FText::FromString("?")); CurrentComboTextBlock2->SetText(FText::FromString("?")); if (NewCombo.Len() > 0) BackspaceButton->SetVisibility(ESlateVisibility::Visible); else BackspaceButton->SetVisibility(ESlateVisibility::Hidden); if (IsValidCombo(NewCombo)) { CurrentComboTextBlock->SetText(FText::FromString(NewCombo)); CastButton->SetVisibility(ESlateVisibility::Visible); } else { CastButton->SetVisibility(ESlateVisibility::Hidden); } if (NewCombo.Len() == 1) { CurrentComboTextBlock1->SetText(FText::FromString(NewCombo)); } else if (NewCombo.Len() == 2) { CurrentComboTextBlock1->SetText(FText::FromString(NewCombo.LeftChop(1))); CurrentComboTextBlock2->SetText(FText::FromString(NewCombo.RightChop(1))); } } void ATurnBaseCombatV2::UpdateActionPoints() { return; } void ATurnBaseCombatV2::AddBattleLogMessage(const FString& Message) { BattleLog.Append(Message + "\n"); UpdateBattleLog(); } void ATurnBaseCombatV2::ClearBattleLog() { BattleLog = ""; } void ATurnBaseCombatV2::UpdateBattleLog() { TArray TempArray; if (const int32 LineCount = BattleLog.ParseIntoArray(TempArray, TEXT("\n"), true); LineCount > 10) { ClearBattleLog(); } BattleLogTextBlock->SetText(FText::FromString(BattleLog)); } void ATurnBaseCombatV2::UpdateResourceBars() const { ProbertiumResourceBar->SetPercent(ProbertiumResource / 20.0f); EisResourceBar->SetPercent(EisResource / 20.0f); AzosResourceBar->SetPercent(AzosResource / 20.0f); IroquoidResourceBar->SetPercent(IroquoidResource / 20.0f); ToggleButtonIfResourceAvailable(); } void ATurnBaseCombatV2::DisableButtons() const { PButton->SetIsEnabled(false); EButton->SetIsEnabled(false); AButton->SetIsEnabled(false); IButton->SetIsEnabled(false); BackspaceButton->SetIsEnabled(false); CastButton->SetIsEnabled(false); RunButton->SetIsEnabled(false); HealButton->SetIsEnabled(false); } void ATurnBaseCombatV2::EnableButtons() const { ToggleButtonIfResourceAvailable(); BackspaceButton->SetIsEnabled(true); CastButton->SetIsEnabled(true); FProperty* IsBossProperty = FindFieldChecked(EnemyActor->GetClass(), "IsBoss"); const FBoolProperty* IsBossBoolProperty = CastFieldChecked(IsBossProperty); if (!IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor)) RunButton->SetIsEnabled(true); } void ATurnBaseCombatV2::EnemyTurn() { if (!IsValid(EnemyActor)) return; if (EnemyHealth <= nullptr) return; int ChanceToMiss; FProperty* IsBossProperty = FindFieldChecked(EnemyActor->GetClass(), "IsBoss"); const FBoolProperty* IsBossBoolProperty = CastFieldChecked(IsBossProperty); if (IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor)) ChanceToMiss = 5; else ChanceToMiss = 30; if (FMath::RandRange(1, 100) > ChanceToMiss) { if (IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor) && FMath::RandRange(1, 100) <= 10) { FOutputDeviceNull AR; UStatusSystem* StatusSystem = Cast(PlayerActor->GetComponentByClass(UStatusSystem::StaticClass())); UStatusEffect* TempPlayerDamageDownStatusEffect = NewObject(PlayerActor, DamageDownPlayerStatusEffect); StatusSystem->AddStatusEffect(TempPlayerDamageDownStatusEffect, 1, true); StatusEffects.Add(TempPlayerDamageDownStatusEffect); AddBattleLogMessage("Player Damage Down"); StatusTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor(1.0f, 0.0f, 0.0f, 1.0f))); StatusTextBlock->SetText(FText::FromString("Player Damage Down")); const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation")); HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); const FString Command4 = FString::Printf(TEXT("TriggerScreamAnimation")); EnemyActor->CallFunctionByNameWithArguments(*Command4, AR, nullptr, true); } else { const FProperty* EnemyBaseDamageProperty = EnemyActor->GetClass()->FindPropertyByName(FName("BaseDamage")); const int* EnemyBaseDamagePtr = EnemyBaseDamageProperty->ContainerPtrToValuePtr(EnemyActor); DamagePlayer(*EnemyBaseDamagePtr); } } else { StatusTextBlock->SetColorAndOpacity(FSlateColor(FLinearColor(1.0f, 1.0f, 1.0f, 1.0f))); StatusTextBlock->SetText(FText::FromString("Missed")); FOutputDeviceNull AR; const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation")); HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); const FString Command2 = FString::Printf(TEXT("TriggerAttackAnimation")); EnemyActor->CallFunctionByNameWithArguments(*Command2, AR, nullptr, true); } OnEnemyTurn.Broadcast(EnemyActor, PlayerActor); TurnIndicatorTextBlock->SetText(FText::FromString("Player Turn")); EnableButtons(); } void ATurnBaseCombatV2::ToggleButtonIfResourceAvailable() const { if (ProbertiumResource >= 1) PButton->SetIsEnabled(true); else PButton->SetIsEnabled(false); if (EisResource >= 1) EButton->SetIsEnabled(true); else EButton->SetIsEnabled(false); if (AzosResource >= 1) AButton->SetIsEnabled(true); else AButton->SetIsEnabled(false); if (IroquoidResource >= 1) IButton->SetIsEnabled(true); else IButton->SetIsEnabled(false); if (Cast(PlayerActor)->Inventory->GetItemAmount(0) >= 1 && *PlayerHealth < 100.0f) HealButton->SetIsEnabled(true); else HealButton->SetIsEnabled(false); }