test/Source/MyProject/Variant_Combat/AI/CombatEnemy.h

233 lines
7.6 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Character.h"
#include "CombatAttacker.h"
#include "CombatDamageable.h"
#include "Animation/AnimMontage.h"
#include "Engine/TimerHandle.h"
#include "CombatEnemy.generated.h"
class UWidgetComponent;
class UCombatLifeBar;
class UAnimMontage;
/** Completed attack animation delegate for StateTree */
DECLARE_DELEGATE(FOnEnemyAttackCompleted);
/** Landed delegate for StateTree */
DECLARE_DELEGATE(FOnEnemyLanded);
/** Enemy died delegate */
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FOnEnemyDied);
/**
* An AI-controlled character with combat capabilities.
* Its bundled AI Controller runs logic through StateTree
*/
UCLASS(abstract)
class ACombatEnemy : public ACharacter, public ICombatAttacker, public ICombatDamageable
{
GENERATED_BODY()
/** Life bar widget component */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
UWidgetComponent* LifeBar;
public:
/** Constructor */
ACombatEnemy();
protected:
/** Max amount of HP the character will have on respawn */
UPROPERTY(EditAnywhere, Category="Damage")
float MaxHP = 3.0f;
public:
/** Current amount of HP the character has */
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Damage", meta = (ClampMin = 0, ClampMax = 100))
float CurrentHP = 0.0f;
protected:
/** Name of the pelvis bone, for damage ragdoll physics */
UPROPERTY(EditAnywhere, Category="Damage")
FName PelvisBoneName;
/** Pointer to the life bar widget */
UPROPERTY(EditAnywhere, Category="Damage")
UCombatLifeBar* LifeBarWidget;
/** If true, the character is currently playing an attack animation */
bool bIsAttacking = false;
/** Distance ahead of the character that melee attack sphere collision traces will extend */
UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 500, Units = "cm"))
float MeleeTraceDistance = 75.0f;
/** Radius of the sphere trace for melee attacks */
UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 500, Units = "cm"))
float MeleeTraceRadius = 50.0f;
/** Amount of damage a melee attack will deal */
UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 100))
float MeleeDamage = 1.0f;
/** Amount of knockback impulse a melee attack will apply */
UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm/s"))
float MeleeKnockbackImpulse = 150.0f;
/** Amount of upwards impulse a melee attack will apply */
UPROPERTY(EditAnywhere, Category="Melee Attack|Damage", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm/s"))
float MeleeLaunchImpulse = 350.0f;
/** AnimMontage that will play for combo attacks */
UPROPERTY(EditAnywhere, Category="Melee Attack|Combo")
UAnimMontage* ComboAttackMontage;
/** Names of the AnimMontage sections that correspond to each stage of the combo attack */
UPROPERTY(EditAnywhere, Category="Melee Attack|Combo")
TArray<FName> ComboSectionNames;
/** Target number of attacks in the combo attack string we're playing */
int32 TargetComboCount = 0;
/** Index of the current stage of the melee attack combo */
int32 CurrentComboAttack = 0;
/** AnimMontage that will play for charged attacks */
UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
UAnimMontage* ChargedAttackMontage;
/** Name of the AnimMontage section that corresponds to the charge loop */
UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
FName ChargeLoopSection;
/** Name of the AnimMontage section that corresponds to the attack */
UPROPERTY(EditAnywhere, Category="Melee Attack|Charged")
FName ChargeAttackSection;
/** Minimum number of charge animation loops that will be played by the AI */
UPROPERTY(EditAnywhere, Category="Melee Attack|Charged", meta = (ClampMin = 1, ClampMax = 20))
int32 MinChargeLoops = 2;
/** Maximum number of charge animation loops that will be played by the AI */
UPROPERTY(EditAnywhere, Category="Melee Attack|Charged", meta = (ClampMin = 1, ClampMax = 20))
int32 MaxChargeLoops = 5;
/** Target number of charge animation loops to play in this charged attack */
int32 TargetChargeLoops = 0;
/** Number of charge animation loop currently playing */
int32 CurrentChargeLoop = 0;
/** Time to wait before removing this character from the level after it dies */
UPROPERTY(EditAnywhere, Category="Death")
float DeathRemovalTime = 5.0f;
/** Enemy death timer */
FTimerHandle DeathTimer;
/** Attack montage ended delegate */
FOnMontageEnded OnAttackMontageEnded;
/** Last recorded location we're being attacked from */
FVector LastDangerLocation = FVector::ZeroVector;
/** Last recorded game time we were attacked */
float LastDangerTime = -1000.0f;
public:
/** Attack completed internal delegate to notify StateTree tasks */
FOnEnemyAttackCompleted OnAttackCompleted;
/** Landed internal delegate to notify StateTree tasks. We use this instead of the built-in Landed delegate so we can bind to a Lambda in StateTree tasks */
FOnEnemyLanded OnEnemyLanded;
/** Enemy died delegate. Allows external subscribers to respond to enemy death */
UPROPERTY(BlueprintAssignable, Category="Events")
FOnEnemyDied OnEnemyDied;
public:
/** Performs an AI-initiated combo attack. Number of hits will be decided by this character */
void DoAIComboAttack();
/** Performs an AI-initiated charged attack. Charge time will be decided by this character */
void DoAIChargedAttack();
/** Called from a delegate when the attack montage ends */
void AttackMontageEnded(UAnimMontage* Montage, bool bInterrupted);
/** Returns the last recorded location we were attacked from */
const FVector& GetLastDangerLocation() const;
/** Returns the last game time we were attacked */
float GetLastDangerTime() const;
public:
// ~begin ICombatAttacker interface
/** Performs an attack's collision check */
virtual void DoAttackTrace(FName DamageSourceBone) override;
/** Performs a combo attack's check to continue the string */
UFUNCTION(BlueprintCallable, Category="Attacker")
virtual void CheckCombo() override;
/** Performs a charged attack's check to loop the charge animation */
UFUNCTION(BlueprintCallable, Category="Attacker")
virtual void CheckChargedAttack() override;
// ~end ICombatAttacker interface
// ~begin ICombatDamageable interface
/** Handles damage and knockback events */
virtual void ApplyDamage(float Damage, AActor* DamageCauser, const FVector& DamageLocation, const FVector& DamageImpulse) override;
/** Handles death events */
virtual void HandleDeath() override;
/** Handles healing events */
virtual void ApplyHealing(float Healing, AActor* Healer) override;
/** Allows the enemy to react to incoming attacks */
virtual void NotifyDanger(const FVector& DangerLocation, AActor* DangerSource) override;
// ~end ICombatDamageable interface
protected:
/** Removes this character from the level after it dies */
void RemoveFromLevel();
public:
/** Overrides the default TakeDamage functionality */
virtual float TakeDamage(float Damage, struct FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override;
/** Overrides landing to reset damage ragdoll physics */
virtual void Landed(const FHitResult& Hit) override;
protected:
/** Blueprint handler to play damage received effects */
UFUNCTION(BlueprintImplementableEvent, Category="Combat")
void ReceivedDamage(float Damage, const FVector& ImpactPoint, const FVector& DamageDirection);
protected:
/** Gameplay initialization */
virtual void BeginPlay() override;
/** EndPlay cleanup */
virtual void EndPlay(EEndPlayReason::Type EndPlayReason) override;
};