From 3a1a2a106da2595327cf614eebffef6a79b2bd1d Mon Sep 17 00:00:00 2001 From: PHILIP White Date: Thu, 23 Mar 2023 02:55:32 +0000 Subject: [PATCH] Added Healing Action & Resource Uncertainty --- .../GOAP/Actions/COMBO_AAA.cpp | 2 +- .../GOAP/Actions/COMBO_IIA.cpp | 21 +++++++++++ .../GOAP/Actions/COMBO_IIA.h | 20 +++++++++++ .../GOAP/Actions/COMBO_III.cpp | 2 +- .../COMP250_1_2101327_AI/GOAP/GOAPAction.cpp | 20 +++++++++-- .../COMP250_1_2101327_AI/GOAP/GOAPAction.h | 2 +- .../TurnBasedCombatV2/TurnBaseCombatV2.cpp | 36 ++++++++++++++----- 7 files changed, 89 insertions(+), 14 deletions(-) create mode 100644 COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_IIA.cpp create mode 100644 COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_IIA.h diff --git a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_AAA.cpp b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_AAA.cpp index 7a7effb..1a7cb78 100644 --- a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_AAA.cpp +++ b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_AAA.cpp @@ -5,7 +5,7 @@ void UCOMBO_AAA::Init() { - ActionCost = 1.0f; + ActionCost = 1.5f; ActionName = "AAA"; PreConditions.Add("AzosResource", 3); Effects.Add("PlayerHealth", 20); diff --git a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_IIA.cpp b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_IIA.cpp new file mode 100644 index 0000000..a0bf03d --- /dev/null +++ b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_IIA.cpp @@ -0,0 +1,21 @@ +// Fill out your copyright notice in the Description page of Project Settings. + + +#include "COMBO_IIA.h" + +void UCOMBO_IIA::Init() +{ + ActionCost = 0.5f; + ActionName = "Heal"; + PreConditions.Add("AzosResource", 1); + PreConditions.Add("IroquoidResource", 2); + PreConditions.Add("EnemyHealth", 50); + Effects.Add("EnemyHealth", -10); + Effects.Add("AzosResource", 1); + Effects.Add("IroquoidResource", 2); +} + +TMap UCOMBO_IIA::Perform(UWorldState* WorldState) +{ + return Effects; +} diff --git a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_IIA.h b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_IIA.h new file mode 100644 index 0000000..bd33873 --- /dev/null +++ b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_IIA.h @@ -0,0 +1,20 @@ +// Fill out your copyright notice in the Description page of Project Settings. + +#pragma once + +#include "CoreMinimal.h" +#include "../GOAPAction.h" +#include "COMBO_IIA.generated.h" + +/** + * + */ +UCLASS() +class COMP250_1_2101327_AI_API UCOMBO_IIA : public UGOAPAction +{ + GENERATED_BODY() + +public: + virtual void Init() override; + virtual TMap Perform(UWorldState* WorldState) override; +}; diff --git a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_III.cpp b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_III.cpp index 586cfc1..7f9bfca 100644 --- a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_III.cpp +++ b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/Actions/COMBO_III.cpp @@ -5,7 +5,7 @@ void UCOMBO_III::Init() { - ActionCost = 1.0f; + ActionCost = 1.5f; ActionName = "III"; PreConditions.Add("IroquoidResource", 3); Effects.Add("PlayerHealth", 20); diff --git a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/GOAPAction.cpp b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/GOAPAction.cpp index 65128ac..30687fd 100644 --- a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/GOAPAction.cpp +++ b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/GOAPAction.cpp @@ -12,7 +12,11 @@ bool UGOAPAction::CheckPreConditions(UWorldState* WorldState) { for (TPair PreCondition : PreConditions) { - if (WorldState->State.Contains(PreCondition.Key)) + if (PreCondition.Key == "PlayerHealth" || PreCondition.Key == "EnemyHealth") + { + if (WorldState->State[PreCondition.Key] >= PreCondition.Value) return false; + } + else if (WorldState->State.Contains(PreCondition.Key)) { if (WorldState->State[PreCondition.Key] < PreCondition.Value) return false; } @@ -41,9 +45,19 @@ UWorldState* UGOAPAction::ApplyEffects(UWorldState* WorldState) /** * @brief Performs the action - * @return A map of the effects of the action + * @return A map of the effects of the action or + * an empty map if the enemy health is less than or equal to 50 to force a re-plan or + * if the world state falls short of the preconditions to force a re-plan */ -TMap UGOAPAction::Perform() +TMap UGOAPAction::Perform(UWorldState* WorldState) { + if (WorldState->State["EnemyHealth"] <= 50) + { + return TMap(); + } + if (!CheckPreConditions(WorldState)) + { + return TMap(); + } return Effects; } diff --git a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/GOAPAction.h b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/GOAPAction.h index af421bc..a4b8334 100644 --- a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/GOAPAction.h +++ b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/GOAP/GOAPAction.h @@ -24,7 +24,7 @@ public: UFUNCTION() UWorldState* ApplyEffects(UWorldState* WorldState); UFUNCTION() - virtual TMap Perform(); + virtual TMap Perform(UWorldState* WorldState); UPROPERTY() TMap PreConditions; diff --git a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/TurnBasedCombatV2/TurnBaseCombatV2.cpp b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/TurnBasedCombatV2/TurnBaseCombatV2.cpp index aa27535..6e07894 100644 --- a/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/TurnBasedCombatV2/TurnBaseCombatV2.cpp +++ b/COMP250_1_2101327_AI/Source/COMP250_1_2101327_AI/TurnBasedCombatV2/TurnBaseCombatV2.cpp @@ -267,14 +267,14 @@ void ATurnBaseCombatV2::RevertActionPoints() void ATurnBaseCombatV2::DamagePlayer(int Damage, FString DamageType) { - *PlayerHealth -= FMath::Clamp(Damage, 0, 100); + *PlayerHealth -= FMath::Clamp(Damage, -10, 100); UpdateProgressBars(); AddBattleLogMessage("Player was damaged for " + FString::FromInt(Damage) + " HP by " + DamageType + "."); } void ATurnBaseCombatV2::DamageEnemy(int Damage, FString DamageType) { - *EnemyHealth -= FMath::Clamp(Damage, 0, 100); + *EnemyHealth -= FMath::Clamp(Damage, -10, 100); UpdateProgressBars(); AddBattleLogMessage("Enemy was damaged for " + FString::FromInt(Damage) + " HP by " + DamageType + "."); } @@ -471,26 +471,44 @@ void ATurnBaseCombatV2::EnemyTurn() { if (EnemyActionPlan.Num() > 0) { - TMap ActionEffects = EnemyActionPlan[0]->Perform(); - DamagePlayer(ActionEffects["PlayerHealth"], EnemyActionPlan[0]->ActionName); + TMap ActionEffects = EnemyActionPlan[0]->Perform(GetWorldState()); + if (ActionEffects.IsEmpty()) + { + EnemyActionPlan = EnemyGOAPAgent->Plan(GetWorldState(), EnemyGOAPAgent->Goals); + ClearActionPlanWidget(); + UpdateActionPlanWidget(); + OnEnemyTurn.Broadcast(EnemyActor, PlayerActor); + TurnIndicatorTextBlock->SetText(FText::FromString("Player Turn")); + ToggleButtons(); + return; + } + + if (ActionEffects.Contains("PlayerHealth")) + { + DamagePlayer(ActionEffects["PlayerHealth"], EnemyActionPlan[0]->ActionName); + } + else + { + DamageEnemy(ActionEffects["EnemyHealth"], EnemyActionPlan[0]->ActionName); + } for (const TTuple Effect : ActionEffects) { if (Effect.Key == "ProbertiumResource") { - EnemyProbertiumResource -= Effect.Value; + EnemyProbertiumResource -= FMath::RandRange(Effect.Value - 1, Effect.Value + 1); } else if (Effect.Key == "EisResource") { - EnemyEisResource -= Effect.Value; + EnemyEisResource -= FMath::RandRange(Effect.Value - 1, Effect.Value + 1); } else if (Effect.Key == "AzosResource") { - EnemyAzosResource -= Effect.Value; + EnemyAzosResource -= FMath::RandRange(Effect.Value - 1, Effect.Value + 1); } else if (Effect.Key == "IroquoidResource") { - EnemyIroquoidResource -= Effect.Value; + EnemyIroquoidResource -= FMath::RandRange(Effect.Value - 1, Effect.Value + 1); } } @@ -500,6 +518,8 @@ void ATurnBaseCombatV2::EnemyTurn() else { EnemyActionPlan = EnemyGOAPAgent->Plan(GetWorldState(), EnemyGOAPAgent->Goals); + ClearActionPlanWidget(); + UpdateActionPlanWidget(); } OnEnemyTurn.Broadcast(EnemyActor, PlayerActor); TurnIndicatorTextBlock->SetText(FText::FromString("Player Turn"));