void AZombieCharacter::Attack()
{
	//This function is used only when it's permitted by the zombie's animation instance
	//It creates a raycast in a sphere shape and checks for possible hits
	//If the hits contain our player it makes sure it applies damage to him

	//Setting up the start and end location of the raycast
	FVector StartLocation = GetMesh()->GetSocketLocation(FName("MeleeStartSocket"));
	FVector EndLocation = GetMesh()->GetSocketLocation(FName("MeleeEndSocket"));


	//Raycasting in a sphere to detect collisions
	TArray<FHitResult> HitResults;

	//Setting up the shape of the raycast
	FCollisionShape CollisionShape;
	CollisionShape.ShapeType = ECollisionShape::Sphere;
	CollisionShape.SetSphere(AttackRaycastRadius);

	//Object query parameters
	FCollisionObjectQueryParams ObjectQueryParams;
	ObjectQueryParams.AllDynamicObjects;

	//Handling ignored actors
	FCollisionQueryParams QueryParams;
	QueryParams.AddIgnoredActor(this);

	UWorld* World = GetWorld();
	if (World && ZAnimInstance->bEligibleForAttack)
	{
		//Raycasting...
		bool bHit = World->SweepMultiByObjectType(HitResults, StartLocation, EndLocation, FQuat::Identity, ObjectQueryParams, CollisionShape, QueryParams);

		//Raycast visualization
		/*FVector Center = ((EndLocation - StartLocation) / 2) + StartLocation;
		DrawDebugSphere(World, Center, AttackRaycastRadius, 20, FColor::Green, false, 2.f);*/

		//Checking for possible hits
		if (bHit)
		{
			for (auto It = HitResults.CreateIterator(); It; It++)
			{
				ARoguelikeChar* Char = Cast<ARoguelikeChar>(It->GetActor());
				if (Char && ZAnimInstance && GetCharacterMovement())
				{
					//Calling the attack function from character
					Char->TakeDamageFromZombie(Damage);
					
					//Closing the flag which checks for the attack function
					ZAnimInstance->bEligibleForAttack = false;

					//Updating with new movement speed
					GetCharacterMovement()->MaxWalkSpeed = InitialMaxWalkSpeed;
					ZAnimInstance->Speed = InitialMaxWalkSpeed;
					break;
				}
			}
		}
	}

}
Exemplo n.º 2
0
AEyeXActorBase* AEyeXPlayerController::FindBySweep(FHitResult& OutHit, const FSceneView* const View, const FVector2D& GazePoint,
	const FCollisionObjectQueryParams& ObjectParams, const FCollisionQueryParams& TraceParams)
{
	if (SweepIntervals <= 1)
	{
		UE_LOG(LogEyeX, Warning, TEXT("Invalid value for SweepIntervals: %i. Must be greater than 0."), SweepIntervals);
		return nullptr;
	}

	UWorld* World = GetWorld();
	if (!World) return nullptr;

	FVector Start, Direction;
	View->DeprojectFVector2D(GazePoint, Start, Direction);

	const float TanFOVScaled = GetTanOfFOVAngleScaled();

	// Perform sweeps
	const float DeltaDistance = MaxDistance / SweepIntervals;
	const FVector DeltaDirection = DeltaDistance * Direction;
	float CurrentDistance = DeltaDistance / 2;
	FCollisionShape Shape;
	AEyeXActorBase* EyeXActor = nullptr;
	for (int i = 0; i < SweepIntervals; ++i)	
	{
		const FVector End = Start + DeltaDirection;
		const float Radius = (i == 0) ? 0.0f : TanFOVScaled * CurrentDistance; // Depends on the view frustrum, size of the screen and the distance.
		Shape.SetSphere(Radius);

		if (World->SweepSingleByObjectType(OutHit, Start, End, FQuat::Identity, ObjectParams, Shape, TraceParams))
		{
			EyeXActor = Cast<AEyeXActorBase>(OutHit.GetActor());
			break;
		}

		Start = End;
		CurrentDistance += DeltaDistance;
	}
	
	VisualizeHit(bVisualizeDetection, World, OutHit, Shape.GetSphereRadius());
	VisualizeGazePoint(bVisualizeDetection, World, Start);

	return EyeXActor;
}
Exemplo n.º 3
0
/** Tests shape components more efficiently than the with-adjustment case, but does less-efficient ppr-poly collision for meshes. */
static bool ComponentEncroachesBlockingGeometry_NoAdjustment(UWorld const* World, AActor const* TestActor, UPrimitiveComponent const* PrimComp, FTransform const& TestWorldTransform)
{	
	float const Epsilon = CVarEncroachEpsilon.GetValueOnGameThread();
	
	if (World && PrimComp)
	{
		bool bFoundBlockingHit = false;
		
		static FName NAME_ComponentEncroachesBlockingGeometry_NoAdjustment = FName(TEXT("ComponentEncroachesBlockingGeometry_NoAdjustment"));
		ECollisionChannel const BlockingChannel = PrimComp->GetCollisionObjectType();
		FCollisionShape const CollisionShape = PrimComp->GetCollisionShape(-Epsilon);

		if (CollisionShape.IsBox() && (Cast<UBoxComponent>(PrimComp) == nullptr))
		{
			// we have a bounding box not for a box component, which means this was the fallback aabb
			// since we don't need the penetration info, go ahead and test the component itself for overlaps, which is more accurate
			if (PrimComp->IsRegistered())
			{
				// must be registered
				TArray<FOverlapResult> Overlaps;
				FComponentQueryParams Params(NAME_ComponentEncroachesBlockingGeometry_NoAdjustment, TestActor);
				return World->ComponentOverlapMultiByChannel(Overlaps, PrimComp, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation(), BlockingChannel, Params);
			}
			else
			{
				UE_LOG(LogPhysics, Log, TEXT("Components must be registered in order to be used in a ComponentOverlapMulti call. PriComp: %s TestActor: %s"), *PrimComp->GetName(), *TestActor->GetName());
				return false;
			}
		}
		else
		{
			FCollisionQueryParams Params(NAME_ComponentEncroachesBlockingGeometry_NoAdjustment, false, TestActor);
			return World->OverlapAnyTestByChannel(TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation(), BlockingChannel, CollisionShape, Params);
		}
	}

	return false;
}
Exemplo n.º 4
0
UCollision2PGeom::UCollision2PGeom(const FCollisionShape& CollisionShape)
{
	switch (CollisionShape.ShapeType)
	{
		case ECollisionShape::Box:
		{
			new (Storage)PxBoxGeometry(U2PVector(CollisionShape.GetBox()));
			break;
		}
		case ECollisionShape::Sphere:
		{
			new (Storage)PxSphereGeometry(CollisionShape.GetSphereRadius());
			break;
		}
		case ECollisionShape::Capsule:
		{
			new (Storage)PxCapsuleGeometry(CollisionShape.GetCapsuleRadius(), CollisionShape.GetCapsuleAxisHalfLength());
			break;
		}
		default:
			// invalid point
			ensure(false);
	}
}
bool UWorld::SweepMultiByChannel(TArray<struct FHitResult>& OutHits, const FVector& Start, const FVector& End, const FQuat& Rot, ECollisionChannel TraceChannel, const FCollisionShape& CollisionShape, const FCollisionQueryParams& Params /* = FCollisionQueryParams::DefaultQueryParam */, const FCollisionResponseParams& ResponseParam /* = FCollisionResponseParams::DefaultResponseParam */) const
{
	if (CollisionShape.IsNearlyZero())
	{
		return LineTraceMultiByChannel(OutHits, Start, End, TraceChannel, Params, ResponseParam);
	}
	else
	{
#if UE_WITH_PHYSICS
		return GeomSweepMulti(this, CollisionShape, Rot, OutHits, Start, End, TraceChannel, Params, ResponseParam, FCollisionObjectQueryParams::DefaultObjectQueryParam);
#else
		return false;
#endif
	}
}
float UAISense_Aquaphobia::Update()
{
	AIPerception::FListenerMap& ListenersMap = *GetListeners();

	//For each listener who has this sense we're going to perform a sweep to determine nearby aqua actors
	for (auto& Elem : ListenersMap)
	{
		//Get the listener
		FPerceptionListener Listener = Elem.Value;
		const AActor* ListenerBodyActor = Listener.GetBodyActor();

		for (int32 DigestedPropertyIndex = 0; DigestedPropertyIndex < DigestedProperties.Num(); DigestedPropertyIndex++)
		{
			//Create the sphere for this sense and perform the sweep to determine nearby actors
			FCollisionShape CollisionSphere = FCollisionShape::MakeSphere(DigestedProperties[DigestedPropertyIndex].PhobiaRadius);
			TArray<FHitResult> HitResults;
			GetWorld()->SweepMultiByChannel(HitResults, ListenerBodyActor->GetActorLocation(), ListenerBodyActor->GetActorLocation() + FVector::UpVector*CollisionSphere.GetSphereRadius(), FQuat(), ECollisionChannel::ECC_WorldDynamic, CollisionSphere);

			//Draw debug sphere if we have activated it via the config
			if (DigestedProperties[DigestedPropertyIndex].bDisplayDebugSphere)
			{
				DrawDebugSphere(GetWorld(), ListenerBodyActor->GetActorLocation(), DigestedProperties[DigestedPropertyIndex].PhobiaRadius, 8, FColor::Blue, false, 30.f, 1, 2.f);
			}
			

			//Check hit results for aqua actors
			for (int32 i = 0; i < HitResults.Num(); i++)
			{
				FHitResult hit = HitResults[i];
				//To simplify things, we're going to assume that "water resources" for this post are actors that have the following game tag
				if (hit.GetActor()->ActorHasTag(FName("AquaActor")))
				{
					if ((hit.GetActor()->GetActorLocation() - ListenerBodyActor->GetActorLocation()).Size() <= DigestedProperties[DigestedPropertyIndex].PhobiaRadius)
					{
						Elem.Value.RegisterStimulus(hit.GetActor(), FAIStimulus(*this, 5.f, hit.GetActor()->GetActorLocation(), ListenerBodyActor->GetActorLocation()));
						GLog->Log("registered stimulus!");
					}

				}

			}
		}
		
		
	}
	//Time until next update; in this case we're forcing the update to happen in each frame
	return 0.f;
}
bool UWorld::SweepTestByChannel(const FVector& Start, const FVector& End, const FQuat& Rot, ECollisionChannel TraceChannel, const FCollisionShape& CollisionShape, const FCollisionQueryParams& Params /* = FCollisionQueryParams::DefaultQueryParam */, const FCollisionResponseParams& ResponseParam /* = FCollisionResponseParams::DefaultResponseParam */) const
{
	if (CollisionShape.IsNearlyZero())
	{
		// if extent is 0, we'll just do linetrace instead
		return LineTraceTestByChannel(Start, End, TraceChannel, Params, ResponseParam);
	}
	else
	{
#if UE_WITH_PHYSICS
		return GeomSweepTest(this, CollisionShape, Rot, Start, End, TraceChannel, Params, ResponseParam, FCollisionObjectQueryParams::DefaultObjectQueryParam);
#else
		return false;
#endif
	}
}
bool UWorld::SweepMultiByObjectType(TArray<struct FHitResult>& OutHits, const FVector& Start, const FVector& End, const FQuat& Rot, const FCollisionObjectQueryParams& ObjectQueryParams, const FCollisionShape& CollisionShape, const FCollisionQueryParams& Params /* = FCollisionQueryParams::DefaultQueryParam */) const
{
	if (CollisionShape.IsNearlyZero())
	{
		return LineTraceMultiByObjectType(OutHits, Start, End, ObjectQueryParams, Params);
	}
	else
	{
#if UE_WITH_PHYSICS
		GeomSweepMulti(this, CollisionShape, Rot, OutHits, Start, End, DefaultCollisionChannel, Params, FCollisionResponseParams::DefaultResponseParam, ObjectQueryParams);

		// object query returns true if any hit is found, not only blocking hit
		return (OutHits.Num() > 0);
#else
		return false;
#endif
	}
}
bool UWorld::SweepSingleByObjectType(struct FHitResult& OutHit, const FVector& Start, const FVector& End, const FQuat& Rot, const FCollisionObjectQueryParams& ObjectQueryParams, const FCollisionShape& CollisionShape, const FCollisionQueryParams& Params /* = FCollisionQueryParams::DefaultQueryParam */) const
{
	if (CollisionShape.IsNearlyZero())
	{
		return LineTraceSingleByObjectType(OutHit, Start, End, ObjectQueryParams, Params);
	}
	else
	{
#if UE_WITH_PHYSICS
		return GeomSweepSingle(this, CollisionShape, Rot, OutHit, Start, End, DefaultCollisionChannel, Params, FCollisionResponseParams::DefaultResponseParam, ObjectQueryParams);
#else
		OutHit.TraceStart = Start;
		OutHit.TraceEnd = End;
		return false;
#endif
	}

}
Exemplo n.º 10
0
/** Tests shape components less efficiently than the no-adjustment case, but does quicker aabb collision for meshes. */
static bool ComponentEncroachesBlockingGeometry_WithAdjustment(UWorld const* World, AActor const* TestActor, UPrimitiveComponent const* PrimComp, FTransform const& TestWorldTransform, FVector& OutProposedAdjustment)
{
	// init our output
	OutProposedAdjustment = FVector::ZeroVector;

	float const Epsilon = CVarEncroachEpsilon.GetValueOnGameThread();

	if (World && PrimComp)
	{
		bool bFoundBlockingHit = false;
		bool bComputePenetrationAdjustment = true;
		
		TArray<FOverlapResult> Overlaps;
		static FName NAME_ComponentEncroachesBlockingGeometry_WithAdjustment = FName(TEXT("ComponentEncroachesBlockingGeometry_WithAdjustment"));
		ECollisionChannel const BlockingChannel = PrimComp->GetCollisionObjectType();
		FCollisionShape const CollisionShape = PrimComp->GetCollisionShape(-Epsilon);

		if (CollisionShape.IsBox() && (Cast<UBoxComponent>(PrimComp) == nullptr))
		{
			// we have a bounding box not for a box component, which means this was the fallback aabb
			// so lets test the actual component instead of it's aabb
			// note we won't get penetration adjustment but that's ok
			if (PrimComp->IsRegistered())
			{
				// must be registered
				FComponentQueryParams Params(NAME_ComponentEncroachesBlockingGeometry_WithAdjustment, TestActor);
				bFoundBlockingHit = World->ComponentOverlapMultiByChannel(Overlaps, PrimComp, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation(), BlockingChannel, Params);
				bComputePenetrationAdjustment = false;
			}
			else
			{
				UE_LOG(LogPhysics, Log, TEXT("Components must be registered in order to be used in a ComponentOverlapMulti call. PriComp: %s TestActor: %s"), *PrimComp->GetName(), *TestActor->GetName());
			}
		}
		else
		{
			// overlap our shape
			FCollisionQueryParams Params(NAME_ComponentEncroachesBlockingGeometry_WithAdjustment, false, TestActor);
			bFoundBlockingHit = World->OverlapMultiByChannel(Overlaps, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation(), BlockingChannel, CollisionShape, Params);
		}

		// compute adjustment
		if (bFoundBlockingHit && bComputePenetrationAdjustment)
		{
			// if encroaching, add up all the MTDs of overlapping shapes
			FMTDResult MTDResult;
			for (int32 HitIdx = 0; HitIdx < Overlaps.Num(); HitIdx++)
			{
				UPrimitiveComponent* const OverlapComponent = Overlaps[HitIdx].Component.Get();
				// first determine closest impact point along each axis
				if (OverlapComponent && OverlapComponent->GetCollisionResponseToChannel(BlockingChannel) == ECR_Block)
				{
					FCollisionShape const NonShrunkenCollisionShape = PrimComp->GetCollisionShape();
					bool bSuccess = OverlapComponent->ComputePenetration(MTDResult, NonShrunkenCollisionShape, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation());
					if (bSuccess)
					{
						OutProposedAdjustment += MTDResult.Direction * MTDResult.Distance;
					}
					else
					{
						UE_LOG(LogPhysics, Log, TEXT("OverlapTest says we are overlapping, yet MTD says we're not. Something is wrong"));
					}

					// #hack: sometimes for boxes, physx returns a 0 MTD even though it reports a contact (returns true)
					// to get around this, let's go ahead and test again with the epsilon-shrunken collision shape to see if we're really in 
					// the clear.  if so, we'll say we have no contact (despite what OverlapMultiByChannel said -- it uses a different algorithm)
					if (FMath::IsNearlyZero(MTDResult.Distance))
					{
						FCollisionShape const ShrunkenCollisionShape = PrimComp->GetCollisionShape(-Epsilon);
						bSuccess = OverlapComponent->ComputePenetration(MTDResult, ShrunkenCollisionShape, TestWorldTransform.GetLocation(), TestWorldTransform.GetRotation());
						if (bSuccess)
						{
							OutProposedAdjustment += MTDResult.Direction * MTDResult.Distance;
						}
						else
						{
							// let's call this "no contact" and be done
							return false;
						}
					}
				}
			}
		}

		return bFoundBlockingHit;
	}

	return false;
}
Exemplo n.º 11
0
bool UDestructibleComponent::SweepComponent(FHitResult& OutHit, const FVector Start, const FVector End, const FCollisionShape &CollisionShape, bool bTraceComplex/*=false*/)
{
	bool bHaveHit = false;
#if WITH_APEX
	if (ApexDestructibleActor != NULL)
	{
		PxF32 HitTime = 0.0f;
		PxVec3 HitNormal;

		int32 ChunkIdx = ApexDestructibleActor->obbSweep(HitTime, HitNormal, U2PVector(Start), U2PVector(CollisionShape.GetExtent()), PxMat33::createIdentity(), U2PVector(End - Start), NxDestructibleActorRaycastFlags::AllChunks);

		if (ChunkIdx != NxModuleDestructibleConst::INVALID_CHUNK_INDEX && HitTime <= 1.0f)
		{
			PxRigidDynamic* PActor = ApexDestructibleActor->getChunkPhysXActor(ChunkIdx);

			if (PActor != NULL)
			{
				// Store body instance state
				FFakeBodyInstanceState PrevState;
				SetupFakeBodyInstance(PActor, ChunkIdx, &PrevState);

				bHaveHit = Super::SweepComponent(OutHit, Start, End, CollisionShape, bTraceComplex);

				// Reset original body instance
				ResetFakeBodyInstance(PrevState);
			}
		}
	}
#endif
	return bHaveHit;
}
/** Tick */
void UFluidSurfaceComponent::TickComponent( float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction *ThisTickFunction )
{
	Super::TickComponent( DeltaTime, TickType, ThisTickFunction );
	LastDeltaTime = DeltaTime;

	float SimStep = 1.f / UpdateRate;
	static float Time = 0.0f;

#if WITH_EDITOR

	/* Only update if checked */
	if( !UpdateComponent )
		return;

#endif
	
	/* If this water hasn't been rendered for a while, stop updating */
	if (LastRenderTime > 0 && GetWorld()->TimeSeconds - LastRenderTime > 1)
		return;

	Time += DeltaTime;
	if( Time > SimStep )
	{
		Time = 0.0f;
		LatestVerts = !LatestVerts;

		/* Add ripples for actors in the water */
		TArray<struct FOverlapResult> OverlappingActors;

		FCollisionShape CollisionShape;
		CollisionShape.SetBox( FluidBoundingBox.GetExtent( ) );

		/* Find overlapping actors */
		GetWorld()->OverlapMultiByChannel(OverlappingActors, GetComponentLocation(), GetComponentQuat(), ECC_WorldDynamic, CollisionShape, FCollisionQueryParams(false));

		// @todo: handle better

		/* Iterate through found overlapping actors */
		for( int i = 0; i < OverlappingActors.Num( ); i++ )
		{
			TWeakObjectPtr<AActor> Actor = OverlappingActors[ i ].Actor;

			/* Dont care about self and modifiers */
			if( Actor != NULL && !Actor->IsA( AFluidSurfaceActor::StaticClass( ) ) && !Actor->IsA( AFluidSurfaceModifier::StaticClass( ) ) )
			{
				FVector LocalVel = GetWorldToComponent( ).TransformVector( Actor->GetVelocity( ) );
				float HorizVelMag = LocalVel.Size( );

				Pling( Actor->GetActorLocation( ), RippleVelocityFactor * HorizVelMag, Actor->GetSimpleCollisionRadius( ) );
			}
		}

		/* Do test ripple (moving around in a circle) */
		if( GIsEditor && TestRipple )
		{
			TestRippleAng += SimStep * MyU2Rad * TestRippleSpeed;
			FVector WorldRipplePos, LocalRipplePos;

			float RippleRadius = 0.3f * ( FluidXSize - 1 ) * FluidGridSpacing;
			if( FluidGridType == EFluidGridType::FGT_Hexagonal )
				RippleRadius = FMath::Max( RippleRadius, 0.3f * ( FluidYSize - 1 ) * FluidGridSpacing * ROOT3OVER2 );
			else
				RippleRadius = FMath::Max( RippleRadius, 0.3f * ( FluidYSize - 1 ) * FluidGridSpacing );

			LocalRipplePos.X = ( RippleRadius * FMath::Sin( TestRippleAng ) );
			LocalRipplePos.Y = ( RippleRadius * FMath::Cos( TestRippleAng ) );
			LocalRipplePos.Z = 0.f;

			WorldRipplePos = ComponentToWorld.TransformPosition( LocalRipplePos );
			Pling( WorldRipplePos, TestRippleStrength, TestRippleRadius );
		}

		/* Add modifier effects */
		for( int i = 0; i < Modifiers.Num( ); i++ )
		{
			if( Modifiers[ i ] && Modifiers[ i ]->Active )
				Modifiers[ i ]->Update( DeltaTime );
		}

		/* Need to send new dynamic data */
		MarkRenderDynamicDataDirty( );
	}
}
EBTNodeResult::Type UBTTask_Escape::ExecuteTask(UBehaviorTreeComponent& OwnedTree, uint8* Node)
{
	AWolfAIController* Controller = Cast<AWolfAIController>(OwnedTree.GetAIOwner());
	if (!Controller)
	{
		GLog->Log("Controller fail");
		return EBTNodeResult::Failed;
	}


	FName SensedTargetKey = "SensedTarget";
	AMyCharacter_FirstTry* Caveman = Cast<AMyCharacter_FirstTry>(OwnedTree.GetBlackboardComponent()->GetValueAsObject(SensedTargetKey));
	if (Caveman)
	{
		TArray<FHitResult> HitResults;
		//Start and End Location of the capsule
		FVector StartLocation = Caveman->GetActorLocation();
		FVector EndLocation = StartLocation;
		EndLocation.Z += Caveman->GetCapsuleComponent()->GetUnscaledCapsuleHalfHeight() / 2;

		//Collision Channel
		ECollisionChannel ECC = ECollisionChannel::ECC_WorldDynamic;

		//Collision Shape
		FCollisionShape CollisionShape;

		//Making Collision in sphere
		CollisionShape.ShapeType = ECollisionShape::Sphere;
		CollisionShape.SetSphere(350);

		//Sweeping for possible escape nodes
		if (Caveman->GetWorld()->SweepMultiByChannel(HitResults, StartLocation, EndLocation, FQuat::FQuat(), ECC, CollisionShape))
		{
			//DrawDebugSphere(Caveman->GetWorld(), ((EndLocation - StartLocation) / 2) + StartLocation, CollisionShape.GetSphereRadius(), 100, FColor::Green, true);
			TArray<AActor*> AllPathNodes;
			UGameplayStatics::GetAllActorsOfClass(Caveman->GetWorld(), APathNode::StaticClass(), AllPathNodes);

			APathNode* NextNode;
			NextNode = Cast<APathNode>(AllPathNodes[FMath::RandRange(0, AllPathNodes.Num() - 1)]);
			//Finding next possible node
			while (NextNode->bIsCurrentlyOccupied && AllPathNodes.Contains(NextNode))
			{
				NextNode = Cast<APathNode>(AllPathNodes[FMath::RandRange(0, AllPathNodes.Num() - 1)]);
				GLog->Log("Fear success");
			}

			//Occupy the node
			NextNode->bIsCurrentlyOccupied = true;
			
			//Set the new node in the blackboard
			OwnedTree.GetBlackboardComponent()->SetValue<UBlackboardKeyType_Object>(BlackboardKey.GetSelectedKeyID(), NextNode);
			//OwnedTree.GetBlackboardComponent()->SetValue<UBlackboardKeyType_Object>(Bl)
			
			GLog->Log("Success after while");
			return EBTNodeResult::Succeeded;

		}

	}

	GLog->Log("General Fail");
	return EBTNodeResult::Failed;
}