diff --git a/Source/FLESH/Private/Gore/BloodPool.cpp b/Source/FLESH/Private/Gore/BloodPool.cpp new file mode 100644 index 0000000..d89b80a --- /dev/null +++ b/Source/FLESH/Private/Gore/BloodPool.cpp @@ -0,0 +1,155 @@ +// @ 2025, Copyright Virtuos Games. All rights reserved. + +#include "Gore/BloodPool.h" + +#include "Components/BoxComponent.h" +#include "Components/DecalComponent.h" +#include "UObject/ConstructorHelpers.h" +#include "Materials/MaterialInterface.h" +#include "NiagaraComponent.h" +#include "NiagaraFunctionLibrary.h" + +// Sets default values +ABloodPool::ABloodPool() +{ + PrimaryActorTick.bCanEverTick = true; + PrimaryActorTick.bStartWithTickEnabled = false; + + // Create root component + USceneComponent* Root = CreateDefaultSubobject("Root"); + SetRootComponent(Root); + + // Create decal component + Decal = CreateDefaultSubobject("Decal"); + Decal->SetupAttachment(Root); + Decal->DecalSize = FVector(100); + Decal->SetRelativeRotation(FRotator(-90,0,0)); + Decal->SetFadeIn(-0.2f, 0.3f); + + // Try to load default decal material if not set + if(!DecalMaterial) + { + static ConstructorHelpers::FObjectFinder DefaultDecalMaterial(TEXT("/Game/FLESH/Gore/Textures/M_Decal_BloodPool")); + if(DefaultDecalMaterial.Succeeded()) + { + DecalMaterial = DefaultDecalMaterial.Object; + } + } + + // Try to load default Niagara system if not set + static ConstructorHelpers::FObjectFinder DefaultNiagaraSystem(TEXT("/Game/FLESH/Gore/Niagara/NS_DIS_BloodBurst")); + if(DefaultNiagaraSystem.Succeeded()) + { + BloodBurstSystem = DefaultNiagaraSystem.Object; + } + + // Create Niagara component for blood burst effect + BloodBurstEffect = CreateDefaultSubobject("BloodBurstEffect"); + BloodBurstEffect->SetupAttachment(Root); + BloodBurstEffect->bAutoActivate = false; + + // Create collision component + Collision = CreateDefaultSubobject("Collision"); + Collision->SetupAttachment(Root); + Collision->SetCollisionEnabled(ECollisionEnabled::QueryOnly); + Collision->SetCollisionResponseToAllChannels(ECollisionResponse::ECR_Ignore); + Collision->InitBoxExtent(FVector(100)); + + // Set up collision response for specific channel + Collision->SetCollisionResponseToChannel(ECC_GameTraceChannel4, ECR_Overlap); +} + +void ABloodPool::BeginPlay() +{ + Super::BeginPlay(); + + // Load default decal material if not set + if(!DecalMaterial) + { + DecalMaterial = LoadObject(nullptr, TEXT("/Game/FLESH/Gore/Textures/M_Decal_BloodPool")); + } + + // Load default Niagara system if not set + if(!BloodBurstSystem) + { + BloodBurstSystem = LoadObject(nullptr, TEXT("/Game/FLESH/Gore/Niagara/NS_DIS_BloodBurst")); + } + + // Set up decal properties + Decal->SetFadeIn(StartDelay, InterpTime); + Decal->SetRelativeScale3D(RemapSizeForDecal(DecalSize)); + Decal->SetDecalMaterial(DecalMaterial); + Decal->SetRelativeRotation(DecalRotation); + Decal->AddRelativeRotation(FRotator(-90, FMath::FRandRange(-10.f, 10.f), 0)); + Decal->SetFadeOut(MaxLifetime / 5, MaxLifetime, false); + + // Set up collision properties + Collision->SetRelativeRotation(DecalRotation); + Collision->SetRelativeScale3D(DecalSize); + + // Activate blood burst effect + if(BloodBurstSystem && BloodBurstEffect) + { + BloodBurstEffect->SetAsset(BloodBurstSystem); + BloodBurstEffect->Activate(true); + } +} + +FVector ABloodPool::RemapSizeForDecal(const FVector In) const +{ + FVector Out; + Out.X = In.Z; + Out.Y = In.Y; + Out.Z = In.X; + + return Out; +} + +ABloodPool* ABloodPool::CreateBloodPool(UWorld* World, const FVector& Location, float Scale, TSubclassOf BloodPoolTemplate) +{ + if (!World) + { + return nullptr; + } + + // Use default blood pool template (if not provided) + TSubclassOf PoolTemplate = BloodPoolTemplate; + if (!PoolTemplate) + { + // Use ABloodPool class as default + PoolTemplate = ABloodPool::StaticClass(); + } + + if (PoolTemplate) + { + // Trace downward to find the ground + FHitResult HitResult; + FCollisionQueryParams QueryParams; + QueryParams.bTraceComplex = false; + + if (World->LineTraceSingleByChannel( + HitResult, + Location, + Location - FVector(0, 0, 500), + ECC_Visibility, + QueryParams + )) + { + FVector PoolLocation = HitResult.Location + FVector(0, 0, 1); // Slightly above the ground + + // Spawn blood pool + FActorSpawnParameters SpawnParams; + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn; + ABloodPool* BloodPool = World->SpawnActor(PoolTemplate, PoolLocation, FRotator::ZeroRotator, SpawnParams); + + // Apply scale + if (BloodPool) + { + BloodPool->SetActorScale3D(FVector(Scale)); + return BloodPool; + } + } + } + + return nullptr; +} \ No newline at end of file