diff --git a/Content/Blueprints/Character/Ball/BP_Ball.uasset b/Content/Blueprints/Character/Ball/BP_Ball.uasset new file mode 100644 index 000000000..28e5bea8e Binary files /dev/null and b/Content/Blueprints/Character/Ball/BP_Ball.uasset differ diff --git a/Content/Maps/warmup.umap b/Content/Maps/warmup.umap index b50d4b175..283707d01 100644 Binary files a/Content/Maps/warmup.umap and b/Content/Maps/warmup.umap differ diff --git a/Source/Cloud9/Character/Cloud9Character.cpp b/Source/Cloud9/Character/Cloud9Character.cpp index 6f0958069..8bbb6c8f8 100644 --- a/Source/Cloud9/Character/Cloud9Character.cpp +++ b/Source/Cloud9/Character/Cloud9Character.cpp @@ -82,9 +82,6 @@ ACloud9Character::ACloud9Character(const FObjectInitializer& ObjectInitializer) Score = 1; // TODO: Set score to 0 after implementation - OnTakePointDamage.AddDynamic(HealthComponent, &UCloud9CharacterHealthComponent::OnTakePointDamage); - OnTakeRadialDamage.AddDynamic(HealthComponent, &UCloud9CharacterHealthComponent::OnTakeRadialDamage); - // Activate ticking in order to update the cursor every frame. PrimaryActorTick.bCanEverTick = true; PrimaryActorTick.bStartWithTickEnabled = true; diff --git a/Source/Cloud9/Character/Components/Cloud9CharacterHealthComponent.cpp b/Source/Cloud9/Character/Components/Cloud9CharacterHealthComponent.cpp index 8e79aca82..522d3344f 100644 --- a/Source/Cloud9/Character/Components/Cloud9CharacterHealthComponent.cpp +++ b/Source/Cloud9/Character/Components/Cloud9CharacterHealthComponent.cpp @@ -46,7 +46,7 @@ bool UCloud9CharacterHealthComponent::ChangeHealth(float NewHealth) return false; } - Owner->MarkPendingKill(); + Owner->Destroy(); } return true; @@ -71,6 +71,24 @@ bool UCloud9CharacterHealthComponent::ChangeArmor(float NewArmor) return false; } +void UCloud9CharacterHealthComponent::OnRegister() +{ + Super::OnRegister(); + + let MyOwner = GetOwner(); + + if (not IsValid(MyOwner)) + { + log(Error, "HealthComponent isn't valid"); + return; + } + + log(Display, "Register HealthComponent for '%s'", *MyOwner->GetName()); + // Register twice or delegate add twice (?) + MyOwner->OnTakePointDamage.AddUniqueDynamic(this, &UCloud9CharacterHealthComponent::OnTakePointDamage); + MyOwner->OnTakeRadialDamage.AddUniqueDynamic(this, &UCloud9CharacterHealthComponent::OnTakeRadialDamage); +} + bool UCloud9CharacterHealthComponent::ChangeHasHelmet(bool NewState) { if (bHasHelmet != NewState) diff --git a/Source/Cloud9/Character/Components/Cloud9CharacterHealthComponent.h b/Source/Cloud9/Character/Components/Cloud9CharacterHealthComponent.h index b3490ee86..207f58793 100644 --- a/Source/Cloud9/Character/Components/Cloud9CharacterHealthComponent.h +++ b/Source/Cloud9/Character/Components/Cloud9CharacterHealthComponent.h @@ -39,7 +39,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnHelmetChange, bool, State); class ACloud9Character; -UCLASS(Blueprintable) +UCLASS(Blueprintable, meta=(BlueprintSpawnableComponent)) class CLOUD9_API UCloud9CharacterHealthComponent : public UActorComponent , public ICloud9CharacterComponent @@ -88,6 +88,8 @@ class CLOUD9_API UCloud9CharacterHealthComponent bool ChangeHealth(float NewHealth); bool ChangeArmor(float NewArmor); + virtual void OnRegister() override; + protected: UPROPERTY(BlueprintAssignable, meta=(AllowPrivateAccess), Category=Events) FOnHealthChange OnHealthChange; @@ -99,14 +101,14 @@ class CLOUD9_API UCloud9CharacterHealthComponent FOnHelmetChange OnHelmetChange; /** Current percentage health of character */ - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=State, meta=(AllowPrivateAccess)) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=State, meta=(AllowPrivateAccess)) float Health; /** Current percentage armor of character */ - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=State, meta=(AllowPrivateAccess)) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=State, meta=(AllowPrivateAccess)) float Armor; /** Current percentage armor of character */ - UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category=State, meta=(AllowPrivateAccess)) + UPROPERTY(EditAnywhere, BlueprintReadOnly, Category=State, meta=(AllowPrivateAccess)) bool bHasHelmet; }; diff --git a/Source/Cloud9/Environment/Cloud9ShootingRange.cpp b/Source/Cloud9/Environment/Cloud9ShootingRange.cpp new file mode 100644 index 000000000..b68da20d8 --- /dev/null +++ b/Source/Cloud9/Environment/Cloud9ShootingRange.cpp @@ -0,0 +1,69 @@ +// Copyright (c) 2024 Alexei Gladkikh + +#include "Cloud9ShootingRange.h" + +#include "Cloud9/Tools/Extensions/FVector.h" +#include "Cloud9/Tools/Extensions/TContainer.h" +#include "Components/BoxComponent.h" + +FName ACloud9ShootingRange::ZoneComponentName = "ZoneComponentName"; + +ACloud9ShootingRange::ACloud9ShootingRange() +{ + PrimaryActorTick.bCanEverTick = true; + ZoneComponent = CreateDefaultSubobject(ZoneComponentName); + RootComponent = ZoneComponent; + ZoneSize = {100.0f, 100.0f, 100.0f}; + Count = 1; + GridSize = FVector::OneVector; +} + +void ACloud9ShootingRange::OnConstruction(const FTransform& Transform) +{ + Super::OnConstruction(Transform); + ZoneComponent->SetBoxExtent(ZoneSize); +} + +void ACloud9ShootingRange::BeginPlay() +{ + Super::BeginPlay(); +} + +void ACloud9ShootingRange::Tick(float DeltaTime) +{ + Super::Tick(DeltaTime); + SpawnShootingActors(); +} + +bool ACloud9ShootingRange::SpawnShootingActors() +{ + int Retries = 0; + FVector Origin; + FVector BoxExtent; + GetActorBounds(false, Origin, BoxExtent); + while (Actors.Num() != Count) + { + // GridSize items can be zero (GridSnap should handle it) + let Location = EFVector::Random(BoxExtent - Origin, BoxExtent + Origin, GridSize); + let Actor = GetWorld()->SpawnActor(Class.Get(), &Location); + if (Actors | ETContainer::AnyByPredicate{[Actor](let It) { return It->IsOverlappingActor(Actor); }}) + { + if (constexpr int MaxRetries = 10; Retries++ == MaxRetries) + { + log( + Error, + "[Range=%s] parameters seems to be invalid can't spawn specified count of Actors = %d", + *GetName(), Count + ); + SetActorTickEnabled(false); + return false; + } + + continue; + } + + Actors.Add(Actor); + } + + return true; +} diff --git a/Source/Cloud9/Environment/Cloud9ShootingRange.h b/Source/Cloud9/Environment/Cloud9ShootingRange.h new file mode 100644 index 000000000..4dbfa47c9 --- /dev/null +++ b/Source/Cloud9/Environment/Cloud9ShootingRange.h @@ -0,0 +1,48 @@ +// Copyright (c) 2024 Alexei Gladkikh + +#pragma once + +#include "CoreMinimal.h" +#include "GameFramework/Actor.h" +#include "Cloud9ShootingRange.generated.h" + +class UBoxComponent; + +UCLASS() +class CLOUD9_API ACloud9ShootingRange : public AActor +{ + GENERATED_BODY() + +public: + static FName ZoneComponentName; + + ACloud9ShootingRange(); + +protected: + virtual void OnConstruction(const FTransform& Transform) override; + + virtual void BeginPlay() override; + + virtual void Tick(float DeltaTime) override; + + UPROPERTY(BlueprintReadOnly, Category=Implementation) + UBoxComponent* ZoneComponent; + + UPROPERTY(BlueprintReadOnly, Category=Implementation) + TArray Actors; + + UPROPERTY(EditAnywhere, Category=Config) + int Count; + + UPROPERTY(EditAnywhere, Category=Config) + TSoftClassPtr Class; + + UPROPERTY(EditAnywhere, Category=Config) + FVector ZoneSize; + + UPROPERTY(EditAnywhere, Category=Config) + FVector GridSize; + +private: + bool SpawnShootingActors(); +}; diff --git a/Source/Cloud9/Tools/Extensions/FVector.h b/Source/Cloud9/Tools/Extensions/FVector.h index f9000760a..375d7c58e 100644 --- a/Source/Cloud9/Tools/Extensions/FVector.h +++ b/Source/Cloud9/Tools/Extensions/FVector.h @@ -24,6 +24,7 @@ #pragma once #include "Cloud9/Tools/Macro/Operator.h" +#include "Cloud9/Tools/Macro/Common.h" #include "Cloud9/Tools/Concepts.h" namespace EFVector @@ -40,4 +41,23 @@ namespace EFVector OPERATOR_BODY(Normalize) }; + + inline FVector Random(FVector Min, FVector Max) + { + return { + FMath::RandRange(Min.X, Max.X), + FMath::RandRange(Min.Y, Max.Y), + FMath::RandRange(Min.Z, Max.Z), + }; + } + + inline FVector Random(FVector Min, FVector Max, FVector Grid) + { + let Vector = Random(Min, Max); + return { + FMath::GridSnap(Vector.X, Grid.X), + FMath::GridSnap(Vector.Y, Grid.Y), + FMath::GridSnap(Vector.Z, Grid.Z), + }; + } } diff --git a/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp b/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp index 7618e52f4..dc9428fce 100644 --- a/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp +++ b/Source/Cloud9/Weapon/Classes/Cloud9WeaponGrenade.cpp @@ -33,6 +33,7 @@ #include "Cloud9/Contollers/Cloud9PlayerController.h" #include "Cloud9/Weapon/Sounds/Cloud9SoundPlayer.h" #include "Cloud9/Weapon/Tables/WeaponTableGrenade.h" +#include "Kismet/GameplayStatics.h" const FName ACloud9WeaponGrenade::ExplosionComponentName = TEXT("ExplosionComponent"); const FName ACloud9WeaponGrenade::DetonationEffectComponentName = TEXT("DetonationEffectComponent"); @@ -253,8 +254,6 @@ bool ACloud9WeaponGrenade::OnGrenadeActionLoop() UCloud9SoundPlayer::PlaySingleSound(LoopSound, GetActorLocation(), Settings->Volume); } - log(Display, "OnGrenadeActionLoop()") - if (GetWeaponInfo()->bIsDestroyedOnDetonation) { GetWeaponMesh()->SetHiddenInGame(true); @@ -266,11 +265,27 @@ bool ACloud9WeaponGrenade::OnGrenadeActionLoop() DetonationEffect->Activate(); } + let Location = GetActorLocation(); + + let IgnoredActors = TArray{}; + + // TODO: Handle other types of nades if required + UGameplayStatics::ApplyRadialDamage( + GetWorld(), + GetWeaponInfo()->Damage, + GetActorLocation(), + Explosion->Radius, + UDamageType::StaticClass(), + IgnoredActors, + this, + nullptr, + false, + ECC_Visibility); Explosion->FireImpulse(); if (let ExplodeSounds = GetWeaponInfo()->Sounds.ExplodeSounds; ExplodeSounds.Num() > 0) { - UCloud9SoundPlayer::PlayRandomSound(ExplodeSounds, GetActorLocation(), Settings->Volume); + UCloud9SoundPlayer::PlayRandomSound(ExplodeSounds, Location, Settings->Volume); } if (Settings->bIsDrawExplosionSpheres)