#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(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 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(); 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 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 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(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(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(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(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(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(NodeData.GetObjectParameter("OrganMesh")); UMaterialInterface* OrganMaterial = Cast(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(NodeData.GetObjectParameter("WoundMaterial")); UNiagaraSystem* WoundEffect = Cast(NodeData.GetObjectParameter("WoundEffect")); bool CreateDecal = NodeData.GetBoolParameter("CreateDecal", true); UMaterialInterface* DecalMaterial = Cast(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; }