diff --git a/Content/_Game/DataTable/WeaponDataTable.uasset b/Content/_Game/DataTable/WeaponDataTable.uasset index c6e2f480..84d5a1f9 100644 --- a/Content/_Game/DataTable/WeaponDataTable.uasset +++ b/Content/_Game/DataTable/WeaponDataTable.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:04f1483abd1bb48b0f2b97faa75fc0e3696a8e504c5dadef280089cab62f399a -size 11041 +oid sha256:de32ddb39ce3eb72b7b89e82dfbf945d55d7d942a5e8cda7c26fcc2ea5d55614 +size 11253 diff --git a/Content/_Game/Enemies/EnemyBP.uasset b/Content/_Game/Enemies/EnemyBP.uasset index f188c9f0..6964dac0 100644 --- a/Content/_Game/Enemies/EnemyBP.uasset +++ b/Content/_Game/Enemies/EnemyBP.uasset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fa4907ef2bd55e2cbb48635c541670a25db03db5022a68431a69296e083e773c -size 33675 +oid sha256:c69bcaa51d0fedf6eb2ef4d3b36c738040cb165bffdd84358b5b2a0a4a76137f +size 68665 diff --git a/Content/_Game/HUD/EnemyHealthBarBP.uasset b/Content/_Game/HUD/EnemyHealthBarBP.uasset new file mode 100644 index 00000000..b7702508 --- /dev/null +++ b/Content/_Game/HUD/EnemyHealthBarBP.uasset @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:98fff25891e5908763df890c87a8abd83e9b52ba55cc24d3cebd4cead8ca81dd +size 46748 diff --git a/Content/_Game/Maps/DefaultMap.umap b/Content/_Game/Maps/DefaultMap.umap index c2bd1d8f..1cc42f88 100644 --- a/Content/_Game/Maps/DefaultMap.umap +++ b/Content/_Game/Maps/DefaultMap.umap @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c1e0103f628f1bbf30af806645009416132181302b04cbf92c932650e9471b1c -size 237612 +oid sha256:1bf9ecce6c147370e9b830b9718b3210b0066614b07676814171e53e88b70267 +size 238616 diff --git a/Source/Shooter/Enemy.cpp b/Source/Shooter/Enemy.cpp index bff121b6..a14773eb 100644 --- a/Source/Shooter/Enemy.cpp +++ b/Source/Shooter/Enemy.cpp @@ -7,7 +7,10 @@ #include "Particles/ParticleSystemComponent.h" // Sets default values -AEnemy::AEnemy() +AEnemy::AEnemy() : + Health(100.f), + MaxHealth(100.f), + HealthBarDisplayTime(4.f) { // Set this character to call Tick() every frame. You can turn this off to improve performance if you don't need it. PrimaryActorTick.bCanEverTick = true; @@ -22,6 +25,12 @@ void AEnemy::BeginPlay() GetMesh()->SetCollisionResponseToChannel(ECollisionChannel::ECC_Visibility, ECollisionResponse::ECR_Block); } +void AEnemy::ShowHealthBar_Implementation() +{ + GetWorldTimerManager().ClearTimer(HealthBarTimer); + GetWorldTimerManager().SetTimer(HealthBarTimer, this, &AEnemy::HideHealthBar, HealthBarDisplayTime); +} + // Called every frame void AEnemy::Tick(float DeltaTime) { @@ -47,5 +56,23 @@ void AEnemy::BulletHit_Implementation(FHitResult HitResult) { UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactParticles, HitResult.Location, FRotator(0.f), true); } + + ShowHealthBar(); +} + +float AEnemy::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, + AActor* DamageCauser) +{ + float DamageInflicted = DamageAmount; + if (Health - DamageAmount <= 0.f) + { + DamageInflicted = Health; + Health = 0.f; + } + else + { + Health -= DamageAmount; + } + return DamageInflicted; } diff --git a/Source/Shooter/Enemy.h b/Source/Shooter/Enemy.h index cac25f21..dc3f17cf 100644 --- a/Source/Shooter/Enemy.h +++ b/Source/Shooter/Enemy.h @@ -23,6 +23,14 @@ protected: // Called when the game starts or when spawned virtual void BeginPlay() override; + UFUNCTION(BlueprintNativeEvent) + void ShowHealthBar(); + void ShowHealthBar_Implementation(); + + UFUNCTION(BlueprintImplementableEvent) + void HideHealthBar(); + +private: /** Particles to spawn when hit by bullets */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) UParticleSystem* ImpactParticles; @@ -30,6 +38,23 @@ protected: /** Sound to play when hit by bullets */ UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Combat, meta = (AllowPrivateAccess = true)) USoundCue* ImpactSound; + + /** Current health */ + UPROPERTY(VisibleAnywhere, 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; public: // Called every frame virtual void Tick(float DeltaTime) override; @@ -38,4 +63,8 @@ public: virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override; virtual void BulletHit_Implementation(FHitResult HitResult) override; + + virtual float TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser) override; + + FORCEINLINE FString GetHeadBone() const { return HeadBone; } }; diff --git a/Source/Shooter/ShooterCharacter.cpp b/Source/Shooter/ShooterCharacter.cpp index 6b4c1961..7115761d 100644 --- a/Source/Shooter/ShooterCharacter.cpp +++ b/Source/Shooter/ShooterCharacter.cpp @@ -6,8 +6,6 @@ #include "Item.h" #include "Camera/CameraComponent.h" -#include "Components/SphereComponent.h" -#include "Components/BoxComponent.h" #include "Components/CapsuleComponent.h" #include "Components/WidgetComponent.h" #include "Engine/SkeletalMeshSocket.h" @@ -18,8 +16,8 @@ #include "Sound/SoundCue.h" #include "Ammo.h" #include "BulletHitInterface.h" +#include "Enemy.h" #include "PhysicalMaterials/PhysicalMaterial.h" -#include "Shooter.h" // Sets default values AShooterCharacter::AShooterCharacter() : @@ -662,24 +660,48 @@ void AShooterCharacter::SendBullet() // Spawn impact particles after updating BeamEndPoint // Does hit actor implement BulletHitInterface? - IBulletHitInterface* BulletHitInterface = nullptr; - if (IsValid(BeamHitResult.GetActor()) && - (BulletHitInterface = Cast(BeamHitResult.GetActor())) != nullptr) + bool spawnGenericImpactParticles = false; + if (IsValid(BeamHitResult.GetActor())) { - BulletHitInterface->BulletHit_Implementation(BeamHitResult); - UE_LOG(LogTemp, Warning, TEXT("actor particle")); - GEngine->AddOnScreenDebugMessage(-1, 5.f, FColor::Red, TEXT("actor particle")); + if (IBulletHitInterface* BulletHitInterface = Cast(BeamHitResult.GetActor())) + { + BulletHitInterface->BulletHit_Implementation(BeamHitResult); + } + else + { + spawnGenericImpactParticles = true; + } + + if (AEnemy* HitEnemy = Cast(BeamHitResult.GetActor())) + { + float damageToApply; + if (BeamHitResult.BoneName.ToString() == HitEnemy->GetHeadBone()) + { + damageToApply = EquippedWeapon->GetHeadshotDamage(); + } + else + { + damageToApply = EquippedWeapon->GetDamage(); + } + + UGameplayStatics::ApplyDamage(HitEnemy, + damageToApply, + GetController(), this, + UDamageType::StaticClass()); + UE_LOG(LogTemp, Warning, TEXT("Hit Component: %s"), *BeamHitResult.BoneName.ToString()); + } } else { - if (ImpactParticles) - { - UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactParticles, - BeamHitResult.Location); - UE_LOG(LogTemp, Warning, TEXT("normal particle")); - } + spawnGenericImpactParticles = true; } - + + if (ImpactParticles && spawnGenericImpactParticles) + { + UGameplayStatics::SpawnEmitterAtLocation(GetWorld(), ImpactParticles, + BeamHitResult.Location); + } + // Spawn smoke trail particles if (BeamParticles) { diff --git a/Source/Shooter/Weapon.cpp b/Source/Shooter/Weapon.cpp index 17a22a2a..dd08ff78 100644 --- a/Source/Shooter/Weapon.cpp +++ b/Source/Shooter/Weapon.cpp @@ -156,6 +156,8 @@ void AWeapon::OnConstruction(const FTransform& Transform) } bAutomatic = WeaponDataRow->bAutomatic; + Damage = WeaponDataRow->Damage; + HeadshotDamage = WeaponDataRow->HeadshotDamage; } if (GetMaterialInstance()) diff --git a/Source/Shooter/Weapon.h b/Source/Shooter/Weapon.h index 7773798a..50ae176a 100644 --- a/Source/Shooter/Weapon.h +++ b/Source/Shooter/Weapon.h @@ -90,6 +90,12 @@ struct FWeaponDataTable : public FTableRowBase UPROPERTY(EditAnywhere, BlueprintReadWrite) bool bAutomatic; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float Damage; + + UPROPERTY(EditAnywhere, BlueprintReadWrite) + float HeadshotDamage; }; /** @@ -221,6 +227,12 @@ private: UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Properties", meta = (AllowPrivateAccess = "true")) bool bAutomatic; + /** Amount of damage caused by bullet */ + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Properties", meta = (AllowPrivateAccess = "true")) + float Damage; + + UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Weapon Properties", meta = (AllowPrivateAccess = "true")) + float HeadshotDamage; public: /** Adds an impulse to the weapon */ void ThrowWeapon(); @@ -239,6 +251,8 @@ public: FORCEINLINE UParticleSystem* GetMuzzleFlash() const { return MuzzleFlash; } FORCEINLINE USoundCue* GetFireSound() const { return FireSound; } FORCEINLINE bool GetAutomatic() const { return bAutomatic; } + FORCEINLINE float GetDamage() const { return Damage; } + FORCEINLINE float GetHeadshotDamage() const { return HeadshotDamage; } void StartSlideTimer();