// Fill out your copyright notice in the Description page of Project Settings. #pragma once #include "CoreMinimal.h" #include "BulletHitInterface.h" #include "GameFramework/Character.h" #include "Enemy.generated.h" class UParticleSystem; class USoundCue; class UAnimMontage; class UBehaviorTree; class AEnemyController; class USphereComponent; class UBoxComponent; class AShooterCharacter; DECLARE_DYNAMIC_MULTICAST_DELEGATE(FEnemyDeadDelegate); UCLASS() class SHOOTER_API AEnemy : public ACharacter, public IBulletHitInterface { GENERATED_BODY() public: // Sets default values for this character's properties AEnemy(); protected: // Called when the game starts or when spawned virtual void BeginPlay() override; UFUNCTION(BlueprintNativeEvent) void ShowHealthBar(); void ShowHealthBar_Implementation(); UFUNCTION(BlueprintImplementableEvent) void HideHealthBar(); void Die(); void PlayHitMontage(FName Section, float PlayRate = 1.0f); void ResetHitReactTimer(); UFUNCTION(BlueprintCallable) void StoreHitNumber(UUserWidget* HitNumber, FVector Location); UFUNCTION() void DestroyHitNumber(UUserWidget* HitNumber); void UpdateHitNumbers(); UFUNCTION(BlueprintCallable) float GetHealth() const { return Health; } UFUNCTION(BlueprintCallable) float GetMaxHealth() const { return MaxHealth; } /** Called when something overlaps with the agro sphere */ UFUNCTION() void AgroSphereOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); UFUNCTION(BlueprintCallable) void SetStunned(bool Stunned); UFUNCTION() void CombatRangeOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); UFUNCTION() void CombatRangeEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex); UFUNCTION(BlueprintCallable) void PlayAttackMontage(FName Section, float PlayRate = 1.0f); UFUNCTION(BlueprintPure) FName GetAttackSectionName() const; UFUNCTION() void OnLeftWeaponOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); UFUNCTION() void OnRightWeaponOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult); // Activate/Deactivate collision for weapon boxes UFUNCTION(BlueprintCallable) void ActivateLeftWeapon(); UFUNCTION(BlueprintCallable) void DeactivateLeftWeapon(); UFUNCTION(BlueprintCallable) void ActivateRightWeapon(); UFUNCTION(BlueprintCallable) void DeactivateRightWeapon(); void DoDamage(AShooterCharacter* Victim); void SpawnBlood(AShooterCharacter* Victim, FName WeaponSocket); // Attemp to stun character void StunCharacter(AShooterCharacter* Victim); void ResetCanAttack(); UFUNCTION(BlueprintCallable) void FinishDeath(); void DestroyEnemy(); private: /** Particles to spawn when hit by bullets */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) UParticleSystem* ImpactParticles; /** Sound to play when hit by bullets */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) USoundCue* ImpactSound; /** Current health */ UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Combat, meta = (AllowPrivateAccess = true)) float Health; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) float MaxHealth; /** Name of the head bone */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) FString HeadBone; /** Time to display health bar once shot */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) float HealthBarDisplayTime; FTimerHandle HealthBarTimer; /** Montage containing Hit and Death animations */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) UAnimMontage* HitMontage; FTimerHandle HitReactTimer; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) float HitReactTimeMin; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) float HitReactTimeMax; bool bCanHitReact; /** Map to store HitNumber widgets and their hit locations */ UPROPERTY(VisibleAnywhere, Category = Combat, meta = (AllowPrivateAccess = true)) TMap HitNumbers; /** Time before a hit number is removed from the screen */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) float HitNumberDestroyTime; /** Behavior tree for the AI Character */ UPROPERTY(EditAnywhere, Category = "Behavior Tree", meta = (AllowPrivateAccess = true)) UBehaviorTree* BehaviorTree; /** Point for the enemy to move to */ UPROPERTY(EditAnywhere, Category = "Behavior Tree", meta = (AllowPrivateAccess = true, MakeEditWidget = true)) FVector PatrolPoint; /** 2nd Point for the enemy to move to */ UPROPERTY(EditAnywhere, Category = "Behavior Tree", meta = (AllowPrivateAccess = true, MakeEditWidget = true)) FVector PatrolPoint2; AEnemyController* EnemyController; /** Overlap sphere for when the enemy becomes hostile */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) USphereComponent* AgroSphere; /** True when playing the get hit animation */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) bool bStunned; /** Chance of being stunned. 0: no stun chance, 1: 100% stun chance */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) float StunChance; /** True when in attack rangel; time to attack */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) bool bInAttackRange; /** Overlap sphere for attack range */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) USphereComponent* CombatRangeSphere; /** Montage containing different attacks */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) UAnimMontage* AttackMontage; /** The 4 attack montage section names */ FName AttackLFast; FName AttackRFast; FName AttackL; FName AttackR; /** Collision volume for the left weapon */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) UBoxComponent* LeftWeaponCollision; /** Collision volume for the right weapon */ UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) UBoxComponent* RightWeaponCollision; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) float BaseDamage; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) FName LeftWeaponSocket; UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) FName RightWeaponSocket; /** True when Enemy can attack */ UPROPERTY(VisibleAnywhere, Category = Combat, meta = (AllowPrivateAccess = true)) bool bCanAttack; FTimerHandle AttackWaitTimer; /** Min wait time between attacks */ UPROPERTY(EditAnywhere, Category = Combat, meta = (AllowPrivateAccess = true)) float AttackWaitTime; /** Death anim montage */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) UAnimMontage* DeathMontage; bool bDying; FTimerHandle DeathTimer; /** Time after death until Destroy */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) float DeathTime; /** Delegate for sending enemy death event */ UPROPERTY(BlueprintAssignable, Category = Delegates, meta = (AllowPrivateAccess = true)) FEnemyDeadDelegate EnemyDeadDelegate; /** Sound to play when Dead */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) USoundCue* DeadSound; public: // Called every frame virtual void Tick(float DeltaTime) override; // Called to bind functionality to input virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; virtual void BulletHit_Implementation(FHitResult HitResult, AActor* Shooter, AController* ShooterController) override; virtual float TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override; FORCEINLINE FString GetHeadBone() const { return HeadBone; } UFUNCTION(BlueprintImplementableEvent) void ShowHitNumber(int32 Damage, FVector HitLocation, bool bHeadshot); FORCEINLINE UBehaviorTree* GetBehaviorTree() const { return BehaviorTree; } };