Files
FLESH/Source/FLESHEditor/Private/DismembermentExecutor.cpp

622 lines
22 KiB
C++

#include "DismembermentGraph/DismembermentExecutor.h"
#include "Engine/SkeletalMesh.h"
#include "Components/SkeletalMeshComponent.h"
#include "NiagaraSystem.h"
#include "NiagaraComponent.h"
#include "NiagaraFunctionLibrary.h"
#include "Components/DecalComponent.h"
#include "Kismet/GameplayStatics.h"
#include "PhysicalMaterials/PhysicalMaterial.h"
UDismembermentExecutor::UDismembermentExecutor()
{
// Initialize default values
Compiler = nullptr;
TargetActor = nullptr;
TargetSkeletalMesh = nullptr;
// Create boolean cut tool
CutTool = CreateDefaultSubobject<UBooleanCutTool>(TEXT("CutTool"));
}
void UDismembermentExecutor::Initialize(UDismembermentCompiler* InCompiler)
{
// Set compiler reference
Compiler = InCompiler;
}
bool UDismembermentExecutor::Execute(AActor* InTargetActor)
{
// Set target actor
TargetActor = InTargetActor;
// Find skeletal mesh component
if (!FindTargetSkeletalMesh())
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute dismemberment graph, skeletal mesh component not found"));
return false;
}
// Check if compiler is valid
if (!Compiler)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute dismemberment graph, invalid compiler"));
return false;
}
// Get execution order from compiler
TArray<int32> ExecutionOrder;
if (!Compiler->GetExecutionOrder(ExecutionOrder))
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute dismemberment graph, invalid execution order"));
return false;
}
// Clear selected bones
SelectedBones.Empty();
// Execute nodes in order
bool bSuccess = true;
for (int32 NodeIndex : ExecutionOrder)
{
// Execute node
bool bNodeSuccess = ExecuteNode(NodeIndex);
// Log node execution result
FDismembermentNodeData NodeData;
if (Compiler->GetNodeData(NodeIndex, NodeData))
{
FString NodeName = NodeData.NodeName.ToString();
if (bNodeSuccess)
{
UE_LOG(LogTemp, Display, TEXT("FLESH: Successfully executed node %s"), *NodeName);
}
else
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Failed to execute node %s"), *NodeName);
bSuccess = false;
}
}
}
return bSuccess;
}
bool UDismembermentExecutor::FindTargetSkeletalMesh()
{
// Check if target actor is valid
if (!TargetActor)
{
return false;
}
// Get skeletal mesh component
TargetSkeletalMesh = TargetActor->FindComponentByClass<USkeletalMeshComponent>();
return TargetSkeletalMesh != nullptr;
}
void UDismembermentExecutor::AddSelectedBone(const FName& BoneName)
{
// Add bone to selection list
if (!SelectedBones.Contains(BoneName))
{
SelectedBones.Add(BoneName);
}
}
bool UDismembermentExecutor::ApplyCut(const FVector& Location, const FVector& Direction, float Width, float Depth, UMaterialInterface* Material)
{
// Check if target skeletal mesh is valid
if (!TargetSkeletalMesh)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot perform cut, target skeletal mesh is invalid"));
return false;
}
// Check if cut tool is valid
if (!CutTool)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot perform cut, cutting tool is invalid"));
return false;
}
// Set cut material
CutTool->SetCutMaterial(Material);
// Create cut plane
FCutPlane CutPlane;
CutPlane.Location = Location;
CutPlane.Normal = Direction.GetSafeNormal();
// Apply cut to selected bones or entire mesh
bool bSuccess = false;
if (SelectedBones.Num() > 0)
{
// Cut only selected bones
for (const FName& BoneName : SelectedBones)
{
// Cut skeletal mesh at the specified bone
TArray<USkeletalMesh*> CutResults = CutTool->CutSkeletalMesh(
TargetSkeletalMesh->GetSkeletalMeshAsset(),
CutPlane,
BoneName,
true // Create cap
);
if (CutResults.Num() >= 2)
{
// Replace the original skeletal mesh with the first cut result
TargetSkeletalMesh->SetSkeletalMeshAsset(CutResults[0]);
// TODO: Handle the second cut result (detached part)
// This would involve creating a new actor with the second mesh
// and applying physics to it
bSuccess = true;
}
}
}
else
{
// Cut the entire mesh
TArray<USkeletalMesh*> CutResults = CutTool->CutSkeletalMesh(
TargetSkeletalMesh->GetSkeletalMeshAsset(),
CutPlane,
NAME_None, // No specific bone
true // Create cap
);
if (CutResults.Num() >= 2)
{
// Replace the original skeletal mesh with the first cut result
TargetSkeletalMesh->SetSkeletalMeshAsset(CutResults[0]);
// TODO: Handle the second cut result (detached part)
// This would involve creating a new actor with the second mesh
// and applying physics to it
bSuccess = true;
}
}
return bSuccess;
}
bool UDismembermentExecutor::SpawnBloodEffect(const FVector& Location, UNiagaraSystem* BloodEffect, float BloodAmount, float BloodPressure, bool CreateBloodPool, float BloodPoolSize, UMaterialInterface* BloodPoolMaterial)
{
// Check if target actor is valid
if (!TargetActor)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn blood effect, target actor is invalid"));
return false;
}
// Check if blood effect is valid
if (!BloodEffect)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn blood effect, blood particle system is invalid"));
return false;
}
// Get world
UWorld* World = TargetActor->GetWorld();
if (!World)
{
return false;
}
// Spawn blood effect
UNiagaraComponent* BloodComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
World,
BloodEffect,
Location,
FRotator::ZeroRotator,
FVector(1.0f, 1.0f, 1.0f),
true,
true,
ENCPoolMethod::AutoRelease,
true
);
if (!BloodComponent)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn blood effect, failed to create particle system component"));
return false;
}
// Set blood parameters
BloodComponent->SetVariableFloat(FName("BloodAmount"), BloodAmount);
BloodComponent->SetVariableFloat(FName("BloodPressure"), BloodPressure);
// Create blood pool if needed
if (CreateBloodPool && BloodPoolMaterial)
{
// Create a decal component for the blood pool
UDecalComponent* BloodPoolDecal = UGameplayStatics::SpawnDecalAtLocation(
World,
BloodPoolMaterial,
FVector(BloodPoolSize, BloodPoolSize, 10.0f), // Size of the decal
Location,
FRotator(-90.0f, 0.0f, 0.0f), // Rotate to face the ground
10.0f // Lifetime
);
if (BloodPoolDecal)
{
// Set fade parameters
BloodPoolDecal->SetFadeOut(5.0f, 5.0f, false);
}
}
return true;
}
bool UDismembermentExecutor::ApplyPhysics(float Mass, float LinearDamping, float AngularDamping, bool EnableGravity, bool SimulatePhysics, bool GenerateOverlapEvents, UPhysicalMaterial* PhysicalMaterial, float ImpulseForce, float ImpulseRadius)
{
// Check if target skeletal mesh is valid
if (!TargetSkeletalMesh)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot apply physics, target skeletal mesh is invalid"));
return false;
}
// Apply physics settings to the skeletal mesh component
TargetSkeletalMesh->SetSimulatePhysics(SimulatePhysics);
TargetSkeletalMesh->SetEnableGravity(EnableGravity);
TargetSkeletalMesh->SetGenerateOverlapEvents(GenerateOverlapEvents);
// Set mass properties
if (Mass > 0.0f)
{
TargetSkeletalMesh->SetMassOverrideInKg(NAME_None, Mass, true);
}
// Set damping
TargetSkeletalMesh->SetLinearDamping(LinearDamping);
TargetSkeletalMesh->SetAngularDamping(AngularDamping);
// Set physical material if provided
if (PhysicalMaterial)
{
TargetSkeletalMesh->SetPhysMaterialOverride(PhysicalMaterial);
}
// Apply impulse if force is greater than zero
if (ImpulseForce > 0.0f)
{
// Get the component's location
FVector ComponentLocation = TargetSkeletalMesh->GetComponentLocation();
// Apply radial impulse
TargetSkeletalMesh->AddRadialImpulse(
ComponentLocation,
ImpulseRadius,
ImpulseForce,
ERadialImpulseFalloff::RIF_Linear,
true // Apply impulse to all bodies
);
}
return true;
}
bool UDismembermentExecutor::SpawnOrgan(UStaticMesh* OrganMesh, UMaterialInterface* OrganMaterial, const FName& AttachBoneName, const FVector& RelativeLocation, const FRotator& RelativeRotation, const FVector& RelativeScale, bool SimulatePhysics, float DamageMultiplier, bool IsCriticalOrgan, float BloodAmount)
{
// Check if target actor is valid
if (!TargetActor)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn organ, target actor is invalid"));
return false;
}
// Check if organ mesh is valid
if (!OrganMesh)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn organ, organ mesh is invalid"));
return false;
}
// Get world
UWorld* World = TargetActor->GetWorld();
if (!World)
{
return false;
}
// Create a static mesh component for the organ
UStaticMeshComponent* OrganComponent = NewObject<UStaticMeshComponent>(TargetActor, TEXT("PreviewOrganComponent"));
if (!OrganComponent)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot spawn organ, failed to create static mesh component"));
return false;
}
// Register the component
OrganComponent->RegisterComponent();
// Set the mesh and material
OrganComponent->SetStaticMesh(OrganMesh);
if (OrganMaterial)
{
OrganComponent->SetMaterial(0, OrganMaterial);
}
// Set transform
if (TargetSkeletalMesh && !AttachBoneName.IsNone())
{
// Attach to bone if specified
OrganComponent->AttachToComponent(
TargetSkeletalMesh,
FAttachmentTransformRules::KeepRelativeTransform,
AttachBoneName
);
}
else
{
// Attach to actor root
OrganComponent->AttachToComponent(
TargetActor->GetRootComponent(),
FAttachmentTransformRules::KeepRelativeTransform
);
}
// Set relative transform
OrganComponent->SetRelativeLocation(RelativeLocation);
OrganComponent->SetRelativeRotation(RelativeRotation);
OrganComponent->SetRelativeScale3D(RelativeScale);
// Apply physics if needed
if (SimulatePhysics)
{
OrganComponent->SetSimulatePhysics(true);
OrganComponent->SetEnableGravity(true);
OrganComponent->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics);
// Add a tag to identify this as an organ
OrganComponent->ComponentTags.Add(FName("Organ"));
// Add custom tags for organ properties
if (IsCriticalOrgan)
{
OrganComponent->ComponentTags.Add(FName("CriticalOrgan"));
}
// Store damage multiplier as a custom float
OrganComponent->SetCustomPrimitiveDataFloat(0, DamageMultiplier);
OrganComponent->SetCustomPrimitiveDataFloat(1, BloodAmount);
}
return true;
}
bool UDismembermentExecutor::ApplyWoundEffect(float WoundSize, float WoundDepth, UMaterialInterface* WoundMaterial, UNiagaraSystem* WoundEffect, bool CreateDecal, UMaterialInterface* DecalMaterial, float DecalSize, float DecalLifetime, bool AffectBoneHealth, float BoneDamage)
{
// Check if target actor is valid
if (!TargetActor)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot apply wound effect, target actor is invalid"));
return false;
}
// Get world
UWorld* World = TargetActor->GetWorld();
if (!World)
{
return false;
}
// Get the location from the selected bone or actor location
FVector WoundLocation;
FRotator WoundRotation;
if (TargetSkeletalMesh && SelectedBones.Num() > 0)
{
// Use the first selected bone's location
WoundLocation = TargetSkeletalMesh->GetBoneLocation(SelectedBones[0]);
WoundRotation = TargetSkeletalMesh->GetBoneQuaternion(SelectedBones[0]).Rotator();
}
else if (TargetSkeletalMesh)
{
// Use the skeletal mesh component's location
WoundLocation = TargetSkeletalMesh->GetComponentLocation();
WoundRotation = TargetSkeletalMesh->GetComponentRotation();
}
else
{
// Use the actor's location
WoundLocation = TargetActor->GetActorLocation();
WoundRotation = TargetActor->GetActorRotation();
}
// Spawn wound effect if provided
if (WoundEffect)
{
UNiagaraComponent* WoundEffectComponent = UNiagaraFunctionLibrary::SpawnSystemAtLocation(
World,
WoundEffect,
WoundLocation,
WoundRotation,
FVector(WoundSize, WoundSize, WoundSize),
true,
true,
ENCPoolMethod::AutoRelease,
true
);
if (WoundEffectComponent)
{
// Set wound parameters
WoundEffectComponent->SetVariableFloat(FName("WoundSize"), WoundSize);
WoundEffectComponent->SetVariableFloat(FName("WoundDepth"), WoundDepth);
}
}
// Create decal if needed
if (CreateDecal && DecalMaterial)
{
// Create a decal component for the wound
UDecalComponent* WoundDecal = UGameplayStatics::SpawnDecalAttached(
DecalMaterial,
FVector(DecalSize, DecalSize, DecalSize),
TargetSkeletalMesh ? TargetSkeletalMesh : TargetActor->GetRootComponent(),
NAME_None,
WoundLocation,
WoundRotation,
EAttachLocation::KeepWorldPosition,
DecalLifetime
);
if (WoundDecal)
{
// Set fade parameters
WoundDecal->SetFadeOut(DecalLifetime * 0.8f, DecalLifetime * 0.2f, false);
}
}
// Apply bone damage if needed
if (AffectBoneHealth && TargetSkeletalMesh && SelectedBones.Num() > 0)
{
// This would typically involve a custom bone health system
// For now, we'll just log that bone damage was applied
for (const FName& BoneName : SelectedBones)
{
UE_LOG(LogTemp, Display, TEXT("FLESH: Applied %.2f damage to bone %s"), BoneDamage, *BoneName.ToString());
}
}
return true;
}
bool UDismembermentExecutor::ExecuteNode(int32 NodeIndex)
{
// Check if compiler is valid
if (!Compiler)
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute node, compiler is invalid"));
return false;
}
// Get node data from compiler
FDismembermentNodeData NodeData;
if (!Compiler->GetNodeData(NodeIndex, NodeData))
{
UE_LOG(LogTemp, Warning, TEXT("FLESH: Cannot execute node, node data is invalid"));
return false;
}
// Execute node based on its type
bool bSuccess = false;
switch (NodeData.NodeType)
{
case EDismembermentNodeType::Cut:
{
// Get cut parameters
FVector Location = NodeData.GetVectorParameter("Location", FVector::ZeroVector);
FVector Direction = NodeData.GetVectorParameter("Direction", FVector::UpVector);
float Width = NodeData.GetFloatParameter("Width", 1.0f);
float Depth = NodeData.GetFloatParameter("Depth", 10.0f);
UMaterialInterface* Material = Cast<UMaterialInterface>(NodeData.GetObjectParameter("Material"));
// Apply cut
bSuccess = ApplyCut(Location, Direction, Width, Depth, Material);
break;
}
case EDismembermentNodeType::BloodEffect:
{
// Get blood effect parameters
FVector Location = NodeData.GetVectorParameter("Location", FVector::ZeroVector);
UNiagaraSystem* BloodEffect = Cast<UNiagaraSystem>(NodeData.GetObjectParameter("BloodEffect"));
float BloodAmount = NodeData.GetFloatParameter("BloodAmount", 1.0f);
float BloodPressure = NodeData.GetFloatParameter("BloodPressure", 1.0f);
bool CreateBloodPool = NodeData.GetBoolParameter("CreateBloodPool", true);
float BloodPoolSize = NodeData.GetFloatParameter("BloodPoolSize", 100.0f);
UMaterialInterface* BloodPoolMaterial = Cast<UMaterialInterface>(NodeData.GetObjectParameter("BloodPoolMaterial"));
// Spawn blood effect
bSuccess = SpawnBloodEffect(Location, BloodEffect, BloodAmount, BloodPressure, CreateBloodPool, BloodPoolSize, BloodPoolMaterial);
break;
}
case EDismembermentNodeType::Physics:
{
// Get physics parameters
float Mass = NodeData.GetFloatParameter("Mass", 10.0f);
float LinearDamping = NodeData.GetFloatParameter("LinearDamping", 0.01f);
float AngularDamping = NodeData.GetFloatParameter("AngularDamping", 0.01f);
bool EnableGravity = NodeData.GetBoolParameter("EnableGravity", true);
bool SimulatePhysics = NodeData.GetBoolParameter("SimulatePhysics", true);
bool GenerateOverlapEvents = NodeData.GetBoolParameter("GenerateOverlapEvents", true);
UPhysicalMaterial* PhysicalMaterial = Cast<UPhysicalMaterial>(NodeData.GetObjectParameter("PhysicalMaterial"));
float ImpulseForce = NodeData.GetFloatParameter("ImpulseForce", 1000.0f);
float ImpulseRadius = NodeData.GetFloatParameter("ImpulseRadius", 100.0f);
// Apply physics
bSuccess = ApplyPhysics(Mass, LinearDamping, AngularDamping, EnableGravity, SimulatePhysics, GenerateOverlapEvents, PhysicalMaterial, ImpulseForce, ImpulseRadius);
break;
}
case EDismembermentNodeType::Organ:
{
// Get organ parameters
UStaticMesh* OrganMesh = Cast<UStaticMesh>(NodeData.GetObjectParameter("OrganMesh"));
UMaterialInterface* OrganMaterial = Cast<UMaterialInterface>(NodeData.GetObjectParameter("OrganMaterial"));
FName AttachBoneName = NodeData.GetNameParameter("AttachBoneName", NAME_None);
FVector RelativeLocation = NodeData.GetVectorParameter("RelativeLocation", FVector::ZeroVector);
FRotator RelativeRotation = NodeData.GetRotatorParameter("RelativeRotation", FRotator::ZeroRotator);
FVector RelativeScale = NodeData.GetVectorParameter("RelativeScale", FVector::OneVector);
bool SimulatePhysics = NodeData.GetBoolParameter("SimulatePhysics", true);
float DamageMultiplier = NodeData.GetFloatParameter("DamageMultiplier", 1.0f);
bool IsCriticalOrgan = NodeData.GetBoolParameter("IsCriticalOrgan", false);
float BloodAmount = NodeData.GetFloatParameter("BloodAmount", 1.0f);
// Spawn organ
bSuccess = SpawnOrgan(OrganMesh, OrganMaterial, AttachBoneName, RelativeLocation, RelativeRotation, RelativeScale, SimulatePhysics, DamageMultiplier, IsCriticalOrgan, BloodAmount);
break;
}
case EDismembermentNodeType::Wound:
{
// Get wound parameters
float WoundSize = NodeData.GetFloatParameter("WoundSize", 10.0f);
float WoundDepth = NodeData.GetFloatParameter("WoundDepth", 5.0f);
UMaterialInterface* WoundMaterial = Cast<UMaterialInterface>(NodeData.GetObjectParameter("WoundMaterial"));
UNiagaraSystem* WoundEffect = Cast<UNiagaraSystem>(NodeData.GetObjectParameter("WoundEffect"));
bool CreateDecal = NodeData.GetBoolParameter("CreateDecal", true);
UMaterialInterface* DecalMaterial = Cast<UMaterialInterface>(NodeData.GetObjectParameter("DecalMaterial"));
float DecalSize = NodeData.GetFloatParameter("DecalSize", 50.0f);
float DecalLifetime = NodeData.GetFloatParameter("DecalLifetime", 10.0f);
bool AffectBoneHealth = NodeData.GetBoolParameter("AffectBoneHealth", false);
float BoneDamage = NodeData.GetFloatParameter("BoneDamage", 10.0f);
// Apply wound effect
bSuccess = ApplyWoundEffect(WoundSize, WoundDepth, WoundMaterial, WoundEffect, CreateDecal, DecalMaterial, DecalSize, DecalLifetime, AffectBoneHealth, BoneDamage);
break;
}
case EDismembermentNodeType::BoneSelection:
{
// Get bone selection parameters
FName BoneName = NodeData.GetNameParameter("BoneName", NAME_None);
// Add bone to selection
if (!BoneName.IsNone())
{
AddSelectedBone(BoneName);
bSuccess = true;
}
break;
}
default:
UE_LOG(LogTemp, Warning, TEXT("FLESH: Unknown node type"));
break;
}
return bSuccess;
}