diff --git a/Source/FLESH/Private/BloodPool.cpp b/Source/FLESH/Private/BloodPool.cpp index e623809..cbb9ff4 100644 --- a/Source/FLESH/Private/BloodPool.cpp +++ b/Source/FLESH/Private/BloodPool.cpp @@ -1,114 +1,166 @@ -#include "BloodPool.h" -#include "Components/BoxComponent.h" +#include "BloodSystem.h" +#include "NiagaraComponent.h" +#include "NiagaraFunctionLibrary.h" #include "Components/DecalComponent.h" -#include "Materials/MaterialInterface.h" -#include "Materials/MaterialInstanceDynamic.h" -#include "TimerManager.h" +#include "Kismet/GameplayStatics.h" +#include "Engine/World.h" -// Sets default values -ABloodPool::ABloodPool() +// Sets default values for this component's properties +UBloodSystem::UBloodSystem() { - // Set this actor to call Tick() every frame - PrimaryActorTick.bCanEverTick = false; - - // Create and set up the collision component - Collision = CreateDefaultSubobject(TEXT("Collision")); - RootComponent = Collision; - Collision->SetCollisionProfileName(TEXT("NoCollision")); - Collision->SetBoxExtent(FVector(1.0f, 100.0f, 100.0f)); - - // Create and set up the decal component - Decal = CreateDefaultSubobject(TEXT("Decal")); - Decal->SetupAttachment(RootComponent); - Decal->SetRelativeRotation(FRotator(90.0f, 0.0f, 0.0f)); - Decal->DecalSize = FVector(10.0f, 100.0f, 100.0f); + // Set this component to be initialized when the game starts, and to be ticked every frame + PrimaryComponentTick.bCanEverTick = true; } -// Called when the game starts or when spawned -void ABloodPool::BeginPlay() +// Called when the game starts +void UBloodSystem::BeginPlay() { Super::BeginPlay(); - - // Set the initial decal material - if (DecalMaterial) - { - UMaterialInstanceDynamic* DynamicMaterial = UMaterialInstanceDynamic::Create(DecalMaterial, this); - if (DynamicMaterial) - { - // Set the color parameter if it exists - DynamicMaterial->SetVectorParameterValue(FName("Color"), PoolColor); - Decal->SetMaterial(0, DynamicMaterial); - } - } - - // Update the pool size - UpdatePoolSize(); } -void ABloodPool::SetPoolSize(float NewSize) +// Called every frame +void UBloodSystem::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) { - PoolSize = FMath::Max(0.1f, NewSize); - UpdatePoolSize(); -} + Super::TickComponent(DeltaTime, TickType, ThisTickFunction); -void ABloodPool::SetPoolColor(const FLinearColor& NewColor) -{ - PoolColor = NewColor; - - // Update the material color - if (Decal && Decal->GetMaterial(0)) + // Clean up finished blood effects + for (int32 i = ActiveBloodEffects.Num() - 1; i >= 0; --i) { - UMaterialInstanceDynamic* DynamicMaterial = Cast(Decal->GetMaterial(0)); - if (DynamicMaterial) + if (!ActiveBloodEffects[i] || !ActiveBloodEffects[i]->IsActive()) { - DynamicMaterial->SetVectorParameterValue(FName("Color"), PoolColor); + ActiveBloodEffects.RemoveAt(i); } } } -void ABloodPool::StartExpansion(float InExpansionRate, float MaxSize) +void UBloodSystem::SpawnBloodEffect(const FVector& Location, const FVector& Direction, float Intensity) { - ExpansionRate = InExpansionRate; - MaxPoolSize = MaxSize; - bIsExpanding = true; - - // Start the expansion timer - GetWorldTimerManager().SetTimer(ExpansionTimerHandle, this, &ABloodPool::ExpandPool, 0.1f, true); -} - -void ABloodPool::UpdatePoolSize() -{ - // Update the decal size - if (Decal) - { - Decal->DecalSize = FVector(10.0f, 100.0f * PoolSize, 100.0f * PoolSize); - } - - // Update the collision box size - if (Collision) - { - Collision->SetBoxExtent(FVector(1.0f, 100.0f * PoolSize, 100.0f * PoolSize)); - } -} - -void ABloodPool::ExpandPool() -{ - if (!bIsExpanding) + // Ensure we have a valid blood spray system + if (!BloodSpraySystem) { return; } - - // Increase the pool size - PoolSize += ExpansionRate * 0.1f; - - // Check if we've reached the maximum size - if (PoolSize >= MaxPoolSize) + + // Manage the number of active blood effects + if (ActiveBloodEffects.Num() >= MaxBloodEffects) { - PoolSize = MaxPoolSize; - bIsExpanding = false; - GetWorldTimerManager().ClearTimer(ExpansionTimerHandle); + // Remove the oldest blood effect + if (ActiveBloodEffects[0]) + { + ActiveBloodEffects[0]->DestroyComponent(); + } + ActiveBloodEffects.RemoveAt(0); + } + + // Create a rotation from the direction + FRotator Rotation = Direction.Rotation(); + + // Spawn the blood effect + UNiagaraComponent* BloodEffect = UNiagaraFunctionLibrary::SpawnSystemAtLocation( + GetWorld(), + BloodSpraySystem, + Location, + Rotation, + FVector(Intensity), + true, + true, + ENCPoolMethod::AutoRelease + ); + + if (BloodEffect) + { + // Set the intensity parameter if it exists + BloodEffect->SetFloatParameter(FName("Intensity"), Intensity); + + // Add to active blood effects + ActiveBloodEffects.Add(BloodEffect); } - - // Update the pool size - UpdatePoolSize(); +} + +AActor* UBloodSystem::CreateBloodPool(const FVector& Location, float Size) +{ + // Ensure we have a valid blood pool class + if (!BloodPoolClass) + { + return nullptr; + } + + // Manage the number of active blood pools + if (ActiveBloodPools.Num() >= MaxBloodPools) + { + // Remove the oldest blood pool + if (ActiveBloodPools[0]) + { + ActiveBloodPools[0]->Destroy(); + } + ActiveBloodPools.RemoveAt(0); + } + + // Spawn the blood pool + FActorSpawnParameters SpawnParams; + SpawnParams.Owner = GetOwner(); + SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn; + + // Trace downward to find the floor + FHitResult HitResult; + FVector TraceStart = Location; + FVector TraceEnd = Location - FVector(0, 0, 200.0f); + FCollisionQueryParams QueryParams; + QueryParams.AddIgnoredActor(GetOwner()); + + if (GetWorld()->LineTraceSingleByChannel(HitResult, TraceStart, TraceEnd, ECC_Visibility, QueryParams)) + { + // Spawn at the hit location + AActor* BloodPool = GetWorld()->SpawnActor(BloodPoolClass, HitResult.Location, FRotator::ZeroRotator, SpawnParams); + + if (BloodPool) + { + // Set the size of the blood pool + BloodPool->SetActorScale3D(FVector(Size)); + + // Add to active blood pools + ActiveBloodPools.Add(BloodPool); + + return BloodPool; + } + } + + return nullptr; +} + +UDecalComponent* UBloodSystem::ApplyBloodDecal(UPrimitiveComponent* Component, const FVector& Location, const FVector& Direction, float Size) +{ + // Ensure we have a valid blood decal material + if (!BloodDecalMaterial || !Component) + { + return nullptr; + } + + // Manage the number of active blood decals + if (ActiveBloodDecals.Num() >= MaxBloodDecals) + { + // Remove the oldest blood decal + if (ActiveBloodDecals[0]) + { + ActiveBloodDecals[0]->DestroyComponent(); + } + ActiveBloodDecals.RemoveAt(0); + } + + // Create a rotation from the direction (opposite to the direction for the decal to face outward) + FRotator Rotation = (-Direction).Rotation(); + + // Create the decal component + UDecalComponent* DecalComponent = NewObject(Component); + DecalComponent->SetupAttachment(Component); + DecalComponent->SetRelativeLocation(Component->GetComponentTransform().InverseTransformPosition(Location)); + DecalComponent->SetRelativeRotation(Component->GetComponentTransform().InverseTransformRotation(Rotation.Quaternion())); + DecalComponent->SetDecalMaterial(BloodDecalMaterial); + DecalComponent->DecalSize = FVector(10.0f, Size * 100.0f, Size * 100.0f); + DecalComponent->RegisterComponent(); + + // Add to active blood decals + ActiveBloodDecals.Add(DecalComponent); + + return DecalComponent; }