335 lines
11 KiB
C++
335 lines
11 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/AnimInstance.h"
|
|
#include "CombatCharacter.generated.h"
|
|
|
|
class USpringArmComponent;
|
|
class UCameraComponent;
|
|
class UInputAction;
|
|
struct FInputActionValue;
|
|
class UCombatLifeBar;
|
|
class UWidgetComponent;
|
|
|
|
DECLARE_LOG_CATEGORY_EXTERN(LogCombatCharacter, Log, All);
|
|
|
|
/**
|
|
* An enhanced Third Person Character with melee combat capabilities:
|
|
* - Combo attack string
|
|
* - Press and hold charged attack
|
|
* - Damage dealing and reaction
|
|
* - Death
|
|
* - Respawning
|
|
*/
|
|
UCLASS(abstract)
|
|
class ACombatCharacter : public ACharacter, public ICombatAttacker, public ICombatDamageable
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
/** Camera boom positioning the camera behind the character */
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
|
|
USpringArmComponent* CameraBoom;
|
|
|
|
/** Follow camera */
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
|
|
UCameraComponent* FollowCamera;
|
|
|
|
/** Life bar widget component */
|
|
UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category="Components", meta = (AllowPrivateAccess = "true"))
|
|
UWidgetComponent* LifeBar;
|
|
|
|
protected:
|
|
|
|
/** Jump Input Action */
|
|
UPROPERTY(EditAnywhere, Category ="Input")
|
|
UInputAction* JumpAction;
|
|
|
|
/** Move Input Action */
|
|
UPROPERTY(EditAnywhere, Category ="Input")
|
|
UInputAction* MoveAction;
|
|
|
|
/** Look Input Action */
|
|
UPROPERTY(EditAnywhere, Category ="Input")
|
|
UInputAction* LookAction;
|
|
|
|
/** Mouse Look Input Action */
|
|
UPROPERTY(EditAnywhere, Category="Input")
|
|
UInputAction* MouseLookAction;
|
|
|
|
/** Combo Attack Input Action */
|
|
UPROPERTY(EditAnywhere, Category ="Input")
|
|
UInputAction* ComboAttackAction;
|
|
|
|
/** Charged Attack Input Action */
|
|
UPROPERTY(EditAnywhere, Category ="Input")
|
|
UInputAction* ChargedAttackAction;
|
|
|
|
/** Toggle Camera Side Input Action */
|
|
UPROPERTY(EditAnywhere, Category ="Input")
|
|
UInputAction* ToggleCameraAction;
|
|
|
|
/** Max amount of HP the character will have on respawn */
|
|
UPROPERTY(EditAnywhere, Category="Damage", meta = (ClampMin = 0, ClampMax = 100))
|
|
float MaxHP = 5.0f;
|
|
|
|
/** Current amount of HP the character has */
|
|
UPROPERTY(VisibleAnywhere, Category="Damage")
|
|
float CurrentHP = 0.0f;
|
|
|
|
/** Life bar widget fill color */
|
|
UPROPERTY(EditAnywhere, Category="Damage")
|
|
FLinearColor LifeBarColor;
|
|
|
|
/** 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")
|
|
TObjectPtr<UCombatLifeBar> LifeBarWidget;
|
|
|
|
/** Max amount of time that may elapse for a non-combo attack input to not be considered stale */
|
|
UPROPERTY(EditAnywhere, Category="Melee Attack", meta = (ClampMin = 0, ClampMax = 5, Units = "s"))
|
|
float AttackInputCacheTimeTolerance = 1.0f;
|
|
|
|
/** Time at which an attack button was last pressed */
|
|
float CachedAttackInputTime = 0.0f;
|
|
|
|
/** 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 = 200, Units = "cm"))
|
|
float MeleeTraceRadius = 75.0f;
|
|
|
|
/** Distance ahead of the character that enemies will be notified of incoming attacks */
|
|
UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 500, Units="cm"))
|
|
float DangerTraceDistance = 300.0f;
|
|
|
|
/** Radius of the sphere trace to notify enemies of incoming attacks */
|
|
UPROPERTY(EditAnywhere, Category="Melee Attack|Trace", meta = (ClampMin = 0, ClampMax = 200, Units = "cm"))
|
|
float DangerTraceRadius = 100.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 = 250.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 = 300.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;
|
|
|
|
/** Max amount of time that may elapse for a combo attack input to not be considered stale */
|
|
UPROPERTY(EditAnywhere, Category="Melee Attack|Combo", meta = (ClampMin = 0, ClampMax = 5, Units = "s"))
|
|
float ComboInputCacheTimeTolerance = 0.45f;
|
|
|
|
/** Index of the current stage of the melee attack combo */
|
|
int32 ComboCount = 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;
|
|
|
|
/** Flag that determines if the player is currently holding the charged attack input */
|
|
bool bIsChargingAttack = false;
|
|
|
|
/** If true, the charged attack hold check has been tested at least once */
|
|
bool bHasLoopedChargedAttack = false;
|
|
|
|
/** Camera boom length while the character is dead */
|
|
UPROPERTY(EditAnywhere, Category="Camera", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm"))
|
|
float DeathCameraDistance = 400.0f;
|
|
|
|
/** Camera boom length when the character respawns */
|
|
UPROPERTY(EditAnywhere, Category="Camera", meta = (ClampMin = 0, ClampMax = 1000, Units = "cm"))
|
|
float DefaultCameraDistance = 100.0f;
|
|
|
|
/** Time to wait before respawning the character */
|
|
UPROPERTY(EditAnywhere, Category="Respawn", meta = (ClampMin = 0, ClampMax = 10, Units = "s"))
|
|
float RespawnTime = 3.0f;
|
|
|
|
/** Attack montage ended delegate */
|
|
FOnMontageEnded OnAttackMontageEnded;
|
|
|
|
/** Character respawn timer */
|
|
FTimerHandle RespawnTimer;
|
|
|
|
/** Copy of the mesh's transform so we can reset it after ragdoll animations */
|
|
FTransform MeshStartingTransform;
|
|
|
|
public:
|
|
|
|
/** Constructor */
|
|
ACombatCharacter();
|
|
|
|
protected:
|
|
|
|
/** Called for movement input */
|
|
void Move(const FInputActionValue& Value);
|
|
|
|
/** Called for looking input */
|
|
void Look(const FInputActionValue& Value);
|
|
|
|
/** Called for combo attack input */
|
|
void ComboAttackPressed();
|
|
|
|
/** Called for combo attack input pressed */
|
|
void ChargedAttackPressed();
|
|
|
|
/** Called for combo attack input released */
|
|
void ChargedAttackReleased();
|
|
|
|
/** Called for toggle camera side input */
|
|
void ToggleCamera();
|
|
|
|
/** BP hook to animate the camera side switch */
|
|
UFUNCTION(BlueprintImplementableEvent, Category="Combat")
|
|
void BP_ToggleCamera();
|
|
|
|
public:
|
|
|
|
/** Handles move inputs from either controls or UI interfaces */
|
|
UFUNCTION(BlueprintCallable, Category="Input")
|
|
virtual void DoMove(float Right, float Forward);
|
|
|
|
/** Handles look inputs from either controls or UI interfaces */
|
|
UFUNCTION(BlueprintCallable, Category="Input")
|
|
virtual void DoLook(float Yaw, float Pitch);
|
|
|
|
/** Handles combo attack pressed from either controls or UI interfaces */
|
|
UFUNCTION(BlueprintCallable, Category="Input")
|
|
virtual void DoComboAttackStart();
|
|
|
|
/** Handles combo attack released from either controls or UI interfaces */
|
|
UFUNCTION(BlueprintCallable, Category="Input")
|
|
virtual void DoComboAttackEnd();
|
|
|
|
/** Handles charged attack pressed from either controls or UI interfaces */
|
|
UFUNCTION(BlueprintCallable, Category="Input")
|
|
virtual void DoChargedAttackStart();
|
|
|
|
/** Handles charged attack released from either controls or UI interfaces */
|
|
UFUNCTION(BlueprintCallable, Category="Input")
|
|
virtual void DoChargedAttackEnd();
|
|
|
|
protected:
|
|
|
|
/** Resets the character's current HP to maximum */
|
|
void ResetHP();
|
|
|
|
/** Performs a combo attack */
|
|
void ComboAttack();
|
|
|
|
/** Performs a charged attack */
|
|
void ChargedAttack();
|
|
|
|
/** Called from a delegate when the attack montage ends */
|
|
void AttackMontageEnded(UAnimMontage* Montage, bool bInterrupted);
|
|
|
|
|
|
public:
|
|
|
|
// ~begin CombatAttacker interface
|
|
|
|
/** Performs the collision check for an attack */
|
|
virtual void DoAttackTrace(FName DamageSourceBone) override;
|
|
|
|
/** Performs the combo string check */
|
|
virtual void CheckCombo() override;
|
|
|
|
/** Performs the charged attack hold check */
|
|
virtual void CheckChargedAttack() override;
|
|
|
|
// ~end CombatAttacker interface
|
|
|
|
// ~begin CombatDamageable interface
|
|
|
|
/** Notifies nearby enemies that an attack is coming so they can react */
|
|
void NotifyEnemiesOfIncomingAttack();
|
|
|
|
/** 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 reaction to incoming attacks */
|
|
virtual void NotifyDanger(const FVector& DangerLocation, AActor* DangerSource) override;
|
|
|
|
// ~end CombatDamageable interface
|
|
|
|
/** Called from the respawn timer to destroy and re-create the character */
|
|
void RespawnCharacter();
|
|
|
|
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 dealt effects */
|
|
UFUNCTION(BlueprintImplementableEvent, Category="Combat")
|
|
void DealtDamage(float Damage, const FVector& ImpactPoint);
|
|
|
|
/** Blueprint handler to play damage received effects */
|
|
UFUNCTION(BlueprintImplementableEvent, Category="Combat")
|
|
void ReceivedDamage(float Damage, const FVector& ImpactPoint, const FVector& DamageDirection);
|
|
|
|
protected:
|
|
|
|
/** Initialization */
|
|
virtual void BeginPlay() override;
|
|
|
|
/** Cleanup */
|
|
virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override;
|
|
|
|
/** Handles input bindings */
|
|
virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;
|
|
|
|
/** Handles possessed initialization */
|
|
virtual void NotifyControllerChanged() override;
|
|
|
|
public:
|
|
|
|
/** Returns CameraBoom subobject **/
|
|
FORCEINLINE class USpringArmComponent* GetCameraBoom() const { return CameraBoom; }
|
|
|
|
/** Returns FollowCamera subobject **/
|
|
FORCEINLINE class UCameraComponent* GetFollowCamera() const { return FollowCamera; }
|
|
};
|