Compare commits

..

No commits in common. "cf881a36cd39889828d3a31e010a9788501e4b58" and "81a306f7b0d9870cab306610931854c44357c815" have entirely different histories.

17 changed files with 43 additions and 122 deletions

BIN
Content/BlueprintAI/AI/AIBruh.uasset (Stored with Git LFS)

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
Content/Levels/Build.umap (Stored with Git LFS)

Binary file not shown.

BIN
Content/Levels/Top_layer_level.umap (Stored with Git LFS)

Binary file not shown.

BIN
Content/Levels/Tutorial.umap (Stored with Git LFS)

Binary file not shown.

BIN
Content/Music/Blue/SFX/Coins.uasset (Stored with Git LFS)

Binary file not shown.

View File

@ -49,7 +49,6 @@ void UInteractNPC::Interact()
{ {
//DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 5.0f, 0, 10.0f); //DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 5.0f, 0, 10.0f);
if (Cast<ATempCharacter>(GetOwner())->bShopKeeperText) return; if (Cast<ATempCharacter>(GetOwner())->bShopKeeperText) return;
if (Cast<ATurnBaseCombatV2>(GetWorld()->GetGameState())->bIsInCombat) return;
UDialogueNPC* DialogueNPC = HitResult.GetActor()->FindComponentByClass<UDialogueNPC>(); UDialogueNPC* DialogueNPC = HitResult.GetActor()->FindComponentByClass<UDialogueNPC>();
if (DialogueNPC->bIsInDialogue) if (DialogueNPC->bIsInDialogue)
{ {
@ -62,22 +61,3 @@ void UInteractNPC::Interact()
} }
} }
void UInteractNPC::EndInteract()
{
FVector Start = Cast<UCameraComponent>(GetOwner()->FindComponentByClass<UCameraComponent>())->GetComponentLocation();
FVector End = GetOwner()->GetActorForwardVector() * 300.0f + Start;
FCollisionQueryParams CollisionParams;
CollisionParams.AddIgnoredActor(GetOwner());
if (FHitResult HitResult; GetWorld()->LineTraceSingleByChannel(HitResult, Start, End, ECC_Pawn, CollisionParams))
{
//UE_LOG(LogTemp, Warning, TEXT("Hit: %s"), *HitResult.GetActor()->GetName());
if (HitResult.GetActor()->Tags.Contains("NPC"))
{
//DrawDebugLine(GetWorld(), Start, End, FColor::Green, false, 5.0f, 0, 10.0f);
UDialogueNPC* DialogueNPC = HitResult.GetActor()->FindComponentByClass<UDialogueNPC>();
DialogueNPC->EndDialogue();
}
//DrawDebugLine(GetWorld(), Start, End, FColor::Red, false, 5.0f, 0, 10.0f);
}
}

View File

@ -26,7 +26,4 @@ public:
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void Interact(); void Interact();
UFUNCTION(BlueprintCallable)
void EndInteract();
}; };

View File

@ -114,8 +114,6 @@ void ATempCharacter::Tick(float DeltaTime)
FVector WidgetLocation; FVector WidgetLocation;
FirstPlayerController->DeprojectMousePositionToWorld(WidgetLocation, VectorRotation); FirstPlayerController->DeprojectMousePositionToWorld(WidgetLocation, VectorRotation);
WidgetPointer->SetWorldLocationAndRotation(WidgetLocation, VectorRotation.Rotation().Quaternion()); WidgetPointer->SetWorldLocationAndRotation(WidgetLocation, VectorRotation.Rotation().Quaternion());
CrossHair->SetBrush(FSlateBrush());
CrossHair->SetDesiredSizeOverride(FVector2D(5, 5));
} }
else else
{ {
@ -244,28 +242,6 @@ void ATempCharacter::LineTraceLogic()
} }
} }
void ATempCharacter::ExitMerchant()
{
ThisCamera = Cast<UCameraComponent>(this->FindComponentByClass<UCameraComponent>());
const float GlobalTrace = TraceDistance;
const FVector Start = ThisCamera->GetComponentLocation();
const FVector End = Start + GlobalTrace * ThisCamera->GetForwardVector();
FCollisionQueryParams TraceParams;
TraceParams.AddIgnoredActor(this);
bHit = GetWorld()->LineTraceSingleByChannel(OutHit, Start, End, ECC_Pawn, TraceParams);
if (bHit)
{
if (OutHit.GetActor() == nullptr) return;
if (AInteraction* MyInteractable = Cast<AInteraction>(OutHit.GetActor()))
{
if (MyInteractable->ShopDialogWidget->IsVisible())
{
MyInteractable->ExitScreen();
}
}
}
}
void ATempCharacter::AddToInventory() void ATempCharacter::AddToInventory()
{ {
if (OutHit.GetActor()->FindComponentByClass<UInventoryComponent>()) if (OutHit.GetActor()->FindComponentByClass<UInventoryComponent>())
@ -286,7 +262,7 @@ void ATempCharacter::AddToInventory()
} }
} }
void ATempCharacter::InputDisabler(const bool SetCamera) void ATempCharacter::InputDisabler()
{ {
CrossHairWidget->SetVisibility(ESlateVisibility::Hidden); CrossHairWidget->SetVisibility(ESlateVisibility::Hidden);
@ -298,7 +274,7 @@ void ATempCharacter::InputDisabler(const bool SetCamera)
PlayerController->SetIgnoreLookInput(true); PlayerController->SetIgnoreLookInput(true);
disableTab = true; disableTab = true;
if (ThisCamera != nullptr && SetCamera) if (ThisCamera != nullptr)
{ {
OriginalCameraLocation = ThisCamera->GetComponentLocation(); OriginalCameraLocation = ThisCamera->GetComponentLocation();
OriginalCameraRotation = ThisCamera->GetComponentRotation(); OriginalCameraRotation = ThisCamera->GetComponentRotation();
@ -307,7 +283,7 @@ void ATempCharacter::InputDisabler(const bool SetCamera)
} }
} }
void ATempCharacter::InputEnabler(const bool SetCamera) void ATempCharacter::InputEnabler()
{ {
//Reset UI Mode //Reset UI Mode
APlayerController* PlayerController = GetWorld()->GetFirstPlayerController(); APlayerController* PlayerController = GetWorld()->GetFirstPlayerController();
@ -326,7 +302,7 @@ void ATempCharacter::InputEnabler(const bool SetCamera)
UE_LOG(LogTemp, Display, TEXT("Enabling Inputs")); UE_LOG(LogTemp, Display, TEXT("Enabling Inputs"));
disableTab = true; disableTab = true;
TraceDistance = 300; TraceDistance = 300;
if (ThisCamera != nullptr && SetCamera) if (ThisCamera != nullptr)
{ {
ThisCamera->SetWorldLocation(OriginalCameraLocation); ThisCamera->SetWorldLocation(OriginalCameraLocation);
ThisCamera->SetWorldRotation(OriginalCameraRotation); ThisCamera->SetWorldRotation(OriginalCameraRotation);

View File

@ -56,6 +56,7 @@ public:
UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Inventory, meta = (AllowPrivateAccess = "true")) UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Inventory, meta = (AllowPrivateAccess = "true"))
class UInventoryComponent* Inventory; //Using the InventoryComponent class class UInventoryComponent* Inventory; //Using the InventoryComponent class
// Called to bind functionality to input // Called to bind functionality to input
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
@ -65,13 +66,10 @@ public:
float TraceDistance = 200; float TraceDistance = 200;
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void InputDisabler(bool SetCamera = true); void InputDisabler();
void LineTraceLogic(); void LineTraceLogic();
UFUNCTION(BlueprintCallable)
void ExitMerchant();
UPROPERTY() UPROPERTY()
class UPawnNoiseEmitterComponent* NoiseEmitter; class UPawnNoiseEmitterComponent* NoiseEmitter;
@ -89,7 +87,7 @@ public:
void UseItem(class UBaseItem* Item); // Overriding the BaseItem Class void UseItem(class UBaseItem* Item); // Overriding the BaseItem Class
UFUNCTION(BlueprintCallable) UFUNCTION(BlueprintCallable)
void InputEnabler(bool SetCamera = true); void InputEnabler();
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera") UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Camera")
UCameraComponent* ThisCamera; UCameraComponent* ThisCamera;

View File

@ -52,8 +52,7 @@ void UStatusEffect::TickDown(AActor* Character)
void UStatusEffect::CheckForExpiry(const float TimeOfExpiry, AActor* Character) void UStatusEffect::CheckForExpiry(const float TimeOfExpiry, AActor* Character)
{ {
if (!IsValid(this)) return; if (IsValid(Character)) return;
if (!IsValid(Character)) return;
if (TimeOfExpiry <= UGameplayStatics::GetRealTimeSeconds(GetWorld())) OnExpiry(Character); if (TimeOfExpiry <= UGameplayStatics::GetRealTimeSeconds(GetWorld())) OnExpiry(Character);
UStatusSystem* StatusSystem = Cast<UStatusSystem>(Character->GetComponentByClass(UStatusSystem::StaticClass())); UStatusSystem* StatusSystem = Cast<UStatusSystem>(Character->GetComponentByClass(UStatusSystem::StaticClass()));
if (StatusSystem->GetActiveStatusEffect(this).StatusIcon == nullptr) return; if (StatusSystem->GetActiveStatusEffect(this).StatusIcon == nullptr) return;

View File

@ -40,19 +40,17 @@ void UStatusSystem::TickComponent(float DeltaTime, ELevelTick TickType, FActorCo
void UStatusSystem::AddStatusEffect(UStatusEffect* StatusEffect, const float DurationMultiplier, const bool Invoke) void UStatusSystem::AddStatusEffect(UStatusEffect* StatusEffect, const float DurationMultiplier, const bool Invoke)
{ {
TArray<UStatusEffect*> ToExpire; for (FActiveStatusEffect ActiveStatusEffect : ActiveStatusEffects)
for (const FActiveStatusEffect& ActiveStatusEffect : ActiveStatusEffects)
{ {
if (ActiveStatusEffect.StatusEffect->Name == StatusEffect->Name) if (ActiveStatusEffect.StatusEffect->Name == StatusEffect->Name)
{ {
ToExpire.Add(ActiveStatusEffect.StatusEffect); ActiveStatusEffect.TimeTillExpiry = StatusEffect->BaseDuration * DurationMultiplier;
UTextBlock* StatusText = Cast<UTextBlock>(ActiveStatusEffect.StatusIcon->GetWidgetFromName(TEXT("DurationText")));
StatusText->SetText(FText::FromString(FString::FromInt(ActiveStatusEffect.TimeTillExpiry)));
if (Invoke) ActiveStatusEffect.StatusEffect->Invoke(GetOwner(), ActiveStatusEffect.TimeTillExpiry);
return;
} }
} }
for (UStatusEffect* StatusEffectToExpire : ToExpire)
{
StatusEffectToExpire->OnExpiry(GetOwner());
}
ToExpire.Empty();
FActiveStatusEffect NewStatusEffect; FActiveStatusEffect NewStatusEffect;
NewStatusEffect.StatusEffect = StatusEffect; NewStatusEffect.StatusEffect = StatusEffect;
NewStatusEffect.TimeInitiated = UGameplayStatics::GetRealTimeSeconds(GetWorld()); NewStatusEffect.TimeInitiated = UGameplayStatics::GetRealTimeSeconds(GetWorld());

View File

@ -12,7 +12,6 @@
#include "Kismet/GameplayStatics.h" #include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h" #include "Kismet/KismetMathLibrary.h"
#include "Misc/OutputDeviceNull.h" #include "Misc/OutputDeviceNull.h"
#include "the_twilight_abyss/Dialogue/InteractNPC.h"
#include "the_twilight_abyss/PlayerTemp/TempCharacter.h" #include "the_twilight_abyss/PlayerTemp/TempCharacter.h"
#include "the_twilight_abyss/Quest/QuestSystem.h" #include "the_twilight_abyss/Quest/QuestSystem.h"
@ -54,19 +53,10 @@ void ATurnBaseCombatV2::StartCombat(AActor* Enemy, const bool bWasShot)
EscapePercentage = CalculateEscapePercentage(); EscapePercentage = CalculateEscapePercentage();
EscapePercentageTextBlock->SetText(FText::Join(FText::FromString(""), FText::FromString(FString::FromInt(EscapePercentage * 100)), FText::FromString("%"))); EscapePercentageTextBlock->SetText(FText::Join(FText::FromString(""), FText::FromString(FString::FromInt(EscapePercentage * 100)), FText::FromString("%")));
bIsInCombat = true; bIsInCombat = true;
UBlackboardComponent* EnemyBlackboard = Cast<AAIController>(EnemyActor->GetInstigatorController())->GetBlackboardComponent(); UBlackboardComponent* EnemyBlackboard = Cast<AAIController>(Enemy->GetInstigatorController())->GetBlackboardComponent();
Cast<UQuestSystem>(PlayerActor->GetComponentByClass(UQuestSystem::StaticClass()))->QuestWidgetInstance->SetVisibility(ESlateVisibility::Hidden); Cast<UQuestSystem>(PlayerActor->GetComponentByClass(UQuestSystem::StaticClass()))->QuestWidgetInstance->SetVisibility(ESlateVisibility::Hidden);
HealingJellyAmountTextBlock->SetText(FText::FromString(FString::FromInt(FMath::Clamp(Cast<ATempCharacter>(PlayerActor)->Inventory->GetItemAmount(0), 0, 99)))); HealingJellyAmountTextBlock->SetText(FText::FromString(FString::FromInt(FMath::Clamp(Cast<ATempCharacter>(PlayerActor)->Inventory->GetItemAmount(0), 0, 99))));
if (Cast<ATempCharacter>(PlayerActor)->bIsInDialogue)
{
Cast<UInteractNPC>(PlayerActor->GetComponentByClass(UInteractNPC::StaticClass()))->EndInteract();
}
if (Cast<ATempCharacter>(PlayerActor)->bShopKeeperText)
{
Cast<ATempCharacter>(PlayerActor)->ExitMerchant();
}
FOutputDeviceNull AR; FOutputDeviceNull AR;
const FString Command = FString::Printf(TEXT("CloseInventory")); const FString Command = FString::Printf(TEXT("CloseInventory"));
PlayerActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true); PlayerActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true);
@ -80,6 +70,7 @@ void ATurnBaseCombatV2::StartCombat(AActor* Enemy, const bool bWasShot)
PlayerController->SetInputMode(FInputModeGameAndUI()); PlayerController->SetInputMode(FInputModeGameAndUI());
PlayerController->bShowMouseCursor = true; PlayerController->bShowMouseCursor = true;
if (EnemyBlackboard->GetValueAsBool("IsInCombat")) return;
EnemyBlackboard->SetValueAsBool("IsInCombat", true); EnemyBlackboard->SetValueAsBool("IsInCombat", true);
const FProperty* HealthProperty = Enemy->GetClass()->FindPropertyByName(FName("Health")); const FProperty* HealthProperty = Enemy->GetClass()->FindPropertyByName(FName("Health"));
int32* EnemyHealthPtr = HealthProperty->ContainerPtrToValuePtr<int32>(Enemy); int32* EnemyHealthPtr = HealthProperty->ContainerPtrToValuePtr<int32>(Enemy);
@ -157,7 +148,6 @@ void ATurnBaseCombatV2::CombatCheck(const bool bWasShot)
void ATurnBaseCombatV2::EndCombat() void ATurnBaseCombatV2::EndCombat()
{ {
GetWorldTimerManager().ClearTimer(EnemyTurnTimerHandle);
BookHUD->SetVisibility(ESlateVisibility::Hidden); BookHUD->SetVisibility(ESlateVisibility::Hidden);
HUD->RemoveFromParent(); HUD->RemoveFromParent();
BookStaticMeshComponent->SetVisibility(false); BookStaticMeshComponent->SetVisibility(false);
@ -343,7 +333,7 @@ void ATurnBaseCombatV2::ExecuteCast(FString Combo)
{ {
FOutputDeviceNull AR; FOutputDeviceNull AR;
UStatusSystem* StatusSystem = Cast<UStatusSystem>(PlayerActor->GetComponentByClass(UStatusSystem::StaticClass())); UStatusSystem* StatusSystem = Cast<UStatusSystem>(PlayerActor->GetComponentByClass(UStatusSystem::StaticClass()));
if (Combo == "PA" || Combo == "AP") if (Combo == "PA")
{ {
UStatusEffect* TempThornsStatusEffect = NewObject<UStatusEffect>(PlayerActor, ThornsStatusEffect); UStatusEffect* TempThornsStatusEffect = NewObject<UStatusEffect>(PlayerActor, ThornsStatusEffect);
StatusSystem->AddStatusEffect(TempThornsStatusEffect, 1, false); StatusSystem->AddStatusEffect(TempThornsStatusEffect, 1, false);
@ -354,7 +344,7 @@ void ATurnBaseCombatV2::ExecuteCast(FString Combo)
const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation")); const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation"));
HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true);
} }
else if (Combo == "PI" || Combo == "IP") else if (Combo == "PI")
{ {
UStatusEffect* TempDOTStatusEffect = NewObject<UStatusEffect>(PlayerActor, DOTStatusEffect); UStatusEffect* TempDOTStatusEffect = NewObject<UStatusEffect>(PlayerActor, DOTStatusEffect);
StatusSystem->AddStatusEffect(TempDOTStatusEffect, 1, false); StatusSystem->AddStatusEffect(TempDOTStatusEffect, 1, false);
@ -365,7 +355,7 @@ void ATurnBaseCombatV2::ExecuteCast(FString Combo)
const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation")); const FString Command3 = FString::Printf(TEXT("PlayStatusAnimation"));
HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true); HUD->CallFunctionByNameWithArguments(*Command3, AR, nullptr, true);
} }
else if (Combo == "AI" || Combo == "IA") else if (Combo == "AI")
{ {
UStatusEffect* TempDamageDownStatusEffect = NewObject<UStatusEffect>(PlayerActor, DamageDownStatusEffect); UStatusEffect* TempDamageDownStatusEffect = NewObject<UStatusEffect>(PlayerActor, DamageDownStatusEffect);
StatusSystem->AddStatusEffect(TempDamageDownStatusEffect, 1, true); StatusSystem->AddStatusEffect(TempDamageDownStatusEffect, 1, true);
@ -413,7 +403,7 @@ void ATurnBaseCombatV2::ExecuteCast(FString Combo)
{ {
case true: case true:
// Player Turn // Player Turn
DamageEnemy(ValidCombos.Contains(Combo) ? *ValidCombos.Find(Combo) : *ValidCombos.Find(Combo.Reverse())); DamageEnemy(*ValidCombos.Find(Combo));
OnPlayerTurn.Broadcast(PlayerActor, EnemyActor); OnPlayerTurn.Broadcast(PlayerActor, EnemyActor);
break; break;
case false: case false:
@ -425,9 +415,6 @@ void ATurnBaseCombatV2::ExecuteCast(FString Combo)
if (!bPlayerHasExtraTurn) if (!bPlayerHasExtraTurn)
{ {
FProperty* IsBossProperty = FindFieldChecked<FProperty>(EnemyActor->GetClass(), "IsBoss");
const FBoolProperty* IsBossBoolProperty = CastFieldChecked<FBoolProperty>(IsBossProperty);
if (IsBossBoolProperty->GetPropertyValue_InContainer(EnemyActor) && EnemyHealth <= nullptr) return;
SwitchTurn(); SwitchTurn();
} }
else else
@ -472,11 +459,6 @@ void ATurnBaseCombatV2::DamagePlayer(int Damage, const FString& DamageType)
{ {
const FString Command = FString::Printf(TEXT("TriggerDeathAnimation")); const FString Command = FString::Printf(TEXT("TriggerDeathAnimation"));
EnemyActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true); EnemyActor->CallFunctionByNameWithArguments(*Command, AR, nullptr, true);
for (UStatusEffect* StatusEffect : StatusEffects)
{
StatusEffect->OnExpiry(PlayerActor);
}
StatusEffects.Empty();
return; return;
} }
EndCombat(); EndCombat();
@ -530,11 +512,6 @@ void ATurnBaseCombatV2::DamageEnemy(int Damage, const FString& DamageType)
{ {
const FString Command2 = FString::Printf(TEXT("TriggerDeathAnimation")); const FString Command2 = FString::Printf(TEXT("TriggerDeathAnimation"));
EnemyActor->CallFunctionByNameWithArguments(*Command2, AR, nullptr, true); EnemyActor->CallFunctionByNameWithArguments(*Command2, AR, nullptr, true);
for (UStatusEffect* StatusEffect : StatusEffects)
{
StatusEffect->OnExpiry(PlayerActor);
}
StatusEffects.Empty();
return; return;
} }
EndCombat(); EndCombat();
@ -565,14 +542,12 @@ float ATurnBaseCombatV2::CalculateEscapePercentage() const
bool ATurnBaseCombatV2::IsValidCombo(const FString& Combo) const bool ATurnBaseCombatV2::IsValidCombo(const FString& Combo) const
{ {
if (ValidCombos.Contains(Combo) || ValidCombos.Contains(Combo.Reverse())) return true; return ValidCombos.Contains(Combo);
return false;
} }
bool ATurnBaseCombatV2::IsSpecialCombo(const FString& Combo) const bool ATurnBaseCombatV2::IsSpecialCombo(const FString& Combo) const
{ {
if (SpecialCombos.Contains(Combo) || SpecialCombos.Contains(Combo.Reverse())) return true; return SpecialCombos.Contains(Combo);
return false;
} }
void ATurnBaseCombatV2::SwitchTurn() void ATurnBaseCombatV2::SwitchTurn()
@ -582,7 +557,8 @@ void ATurnBaseCombatV2::SwitchTurn()
TurnIndicatorTextBlock->SetText(FText::FromString("Enemy Turn")); TurnIndicatorTextBlock->SetText(FText::FromString("Enemy Turn"));
DisableButtons(); DisableButtons();
GetWorldTimerManager().SetTimer(EnemyTurnTimerHandle, this, &ATurnBaseCombatV2::EnemyTurn, 2.0f, false); FTimerHandle UnusedHandle;
GetWorldTimerManager().SetTimer(UnusedHandle, this, &ATurnBaseCombatV2::EnemyTurn, 2.0f, false);
//activeActor = bIsPlayerTurn ? enemyActor : playerActor; //activeActor = bIsPlayerTurn ? enemyActor : playerActor;
} }
@ -823,7 +799,6 @@ void ATurnBaseCombatV2::EnableButtons() const
void ATurnBaseCombatV2::EnemyTurn() void ATurnBaseCombatV2::EnemyTurn()
{ {
if (!IsValid(EnemyActor)) return; if (!IsValid(EnemyActor)) return;
if (EnemyHealth <= nullptr) return;
int ChanceToMiss; int ChanceToMiss;
FProperty* IsBossProperty = FindFieldChecked<FProperty>(EnemyActor->GetClass(), "IsBoss"); FProperty* IsBossProperty = FindFieldChecked<FProperty>(EnemyActor->GetClass(), "IsBoss");
const FBoolProperty* IsBossBoolProperty = CastFieldChecked<FBoolProperty>(IsBossProperty); const FBoolProperty* IsBossBoolProperty = CastFieldChecked<FBoolProperty>(IsBossProperty);

View File

@ -146,8 +146,6 @@ protected:
private: private:
bool IsValidCombo(const FString& Combo) const; bool IsValidCombo(const FString& Combo) const;
bool IsSpecialCombo(const FString& Combo) const; bool IsSpecialCombo(const FString& Combo) const;
UPROPERTY()
FTimerHandle EnemyTurnTimerHandle;
UPROPERTY() UPROPERTY()
APostProcessVolume* PostProcessVolume; APostProcessVolume* PostProcessVolume;