/// Utility function for returning a test result to the manager and updating any associated entity passed trigger
void CFlowNode_FeatureTest::SetResult(bool result, const char *reason)
{
#if ENABLE_FEATURE_TESTER
	CFeatureTester *pFeatureTester = CFeatureTester::GetInstance();

	if(pFeatureTester)
	{
		CFeatureTestMgr &ftMgr = pFeatureTester->GetMapFeatureTestMgr();

		// If this is the result for a sequence test
		if(m_entitySeqIndex >= 0 && m_entitySeqIndex < SEQ_ENTITY_COUNT)
		{
			IEntity *pEnt = NULL;
			GetEntityAtIndex(m_entitySeqIndex, pEnt);

			if(pEnt)
			{
				ActivateOutput(&m_actInfo, SEQ_ENTITY_FIRST_OUTPUT_PORT + m_entitySeqIndex, result);

				// If test has actually run
				if(TestHasRun())
				{
					// Inform manager of results
					string testSeqName(Name());
					testSeqName.append("[");
					testSeqName.append(pEnt->GetEntityTextDescription());
					testSeqName.append("]");

					const string desc(GetPortString(&m_actInfo, eInputPorts_Description));

					//Need to ensure non-owned tests pass null pointer
					const char *owners = m_owners.empty() ? NULL : m_owners.c_str();

					ftMgr.OnTestResults(testSeqName.c_str(), desc.c_str(), (result) ? NULL : reason, m_timeRunning, owners);
				}
			}
		}
		else	// Single test (not sequence)
		{
			// If test has actually run
			if(TestHasRun())
			{
				// Inform manager of results
				string testSeqName(Name());
				const string desc(GetPortString(&m_actInfo, eInputPorts_Description));

				//Need to ensure non-owned tests pass null pointer
				const char *owners = m_owners.empty() ? NULL : m_owners.c_str();

				ftMgr.OnTestResults(testSeqName.c_str(), desc.c_str(), (result) ? NULL : reason, m_timeRunning, owners);
			}
		}
	}

#endif
	// NOTE: AllPassed should be triggered by the caller if no more tests are left to run
}
/// Attempts to start the next test. Returns true if successful.
bool CFlowNode_FeatureTest::StartNextTestRun()
{
	// Ensure this is not marked as running
	CRY_ASSERT(!m_running);

	CODECHECKPOINT(FeatureTest_StartNextTestRun_Start);

	const int entityCount = GetTestEntityCount();

	// Is this a sequential test?
	const bool sequential = (entityCount > 0) && GetPortBool(&m_actInfo, eInputPorts_Sequential);

	if(sequential)
	{
		// Ensure sequence index is within a valid range (-1 indicates first run)
		CRY_ASSERT(m_entitySeqIndex >= -1 && m_entitySeqIndex < SEQ_ENTITY_COUNT);

		// If first run
		if(!TestHasRun())
		{
			CryLogAlways("Running sequential test \"%s\" for %d entities...", Name(), entityCount);
		}

		bool bHasEntry = false;
		IEntity *pSeqEntity = NULL;

		for(int i = m_entitySeqIndex + 1; i < SEQ_ENTITY_COUNT; ++i)
		{
			// Prepare the entity from pool if needed
			bHasEntry = GetEntityAtIndex(i, pSeqEntity, true);

			// If there's a valid entity at this index, use it
			if(pSeqEntity)
			{
				m_entitySeqIndex = i;
				break;
			}
			else if(bHasEntry)
			{
				// Fail this test and continue on to the next one
				m_entitySeqIndex = i;
				m_running = true;
				OnTestResult(false, "Test failed: Entity could not be found. Check Entity Pools or the Flowgraph setup.");
				break;
			}
		}

		// Prepare entity ready for test run
		if(pSeqEntity)
		{
			pSeqEntity->Hide(false);
			pSeqEntity->Activate(true);

			m_running = true;
			m_timeRunning = 0.0f;
			m_hasBeenStarted = true;

			CryLogAlways("Starting test: \"%s[%s]\". Max time: %fs.",
						 Name(),
						 pSeqEntity->GetEntityTextDescription(),
						 GetPortFloat(&m_actInfo, eInputPorts_MaxTime));

			// Output entity ID and trigger start
			ActivateOutput(&m_actInfo, eOutputPorts_SequenceEntity, pSeqEntity->GetId());
			ActivateOutput(&m_actInfo, eOutputPorts_Start, true);
		}
		else if(!bHasEntry)
		{
			// Indicate end of sequence
			m_entitySeqIndex = -1;
			CryLogAlways("Finished running sequential test \"%s\" for %d entities.", Name(), entityCount);
		}
	}
	else if(!TestHasRun())	// If test has not yet been run
	{
		// Not using sequence
		m_entitySeqIndex = -1;

		// Activate any associated entities
		ActivateAllEntities(true);

		m_running = true;
		m_timeRunning = 0.0f;
		m_hasBeenStarted = true;

		CryLogAlways("Starting test: \"%s\". Max time: %fs.",
					 Name(),
					 GetPortFloat(&m_actInfo, eInputPorts_MaxTime));

		// Start test
		ActivateOutput(&m_actInfo, eOutputPorts_Start, true);
	}

	// Additional workaround to ensure eyePosition (and therefore AI) doesn't track camera position.
	// See CPlayerMovementController::UpdateMovementState() for details.
	CPlayer *pPlayerActor = static_cast<CPlayer *>(gEnv->pGame->GetIGameFramework()->GetClientActor());

	if(pPlayerActor)
		pPlayerActor->SetThirdPerson(m_running);

	return m_running;
}
bool CPlayerStateGround::CheckForVaultTrigger(CPlayer & player, float frameTime)
{
	const int enableVaultFromStandingCVar = g_pGameCVars->pl_ledgeClamber.enableVaultFromStanding;
	const bool doCheck = (enableVaultFromStandingCVar == 3) || ((enableVaultFromStandingCVar > 0) && player.m_jumpButtonIsPressed);

	if (doCheck)
	{
		SLedgeTransitionData ledgeTransition(LedgeId::invalid_id);
		const float zPos = player.GetEntity()->GetWorldPos().z;
		const bool ignoreMovement = (enableVaultFromStandingCVar == 2);

		if (CPlayerStateLedge::TryLedgeGrab(player, zPos, zPos, true, &ledgeTransition, ignoreMovement) && ledgeTransition.m_ledgeTransition != SLedgeTransitionData::eOLT_None)
		{
			CRY_ASSERT( LedgeId(ledgeTransition.m_nearestGrabbableLedgeId).IsValid() );
			const SLedgeInfo ledgeInfo = g_pGame->GetLedgeManager()->GetLedgeById( LedgeId(ledgeTransition.m_nearestGrabbableLedgeId) );
			
			CRY_ASSERT( ledgeInfo.IsValid() );

			if (ledgeInfo.AreAnyFlagsSet(kLedgeFlag_useVault|kLedgeFlag_useHighVault))
			{
#ifdef STATE_DEBUG

				if (g_pGameCVars->pl_ledgeClamber.debugDraw)
				{
					const char * transitionName = s_ledgeTransitionNames[ledgeTransition.m_ledgeTransition];

					IEntity* pEntity = gEnv->pEntitySystem->GetEntity(ledgeInfo.GetEntityId());
					CryWatch ("[LEDGEGRAB] $5%s nearest ledge: %s%s%s%s, transition=%s", player.GetEntity()->GetEntityTextDescription(), pEntity ? pEntity->GetEntityTextDescription() : "none", ledgeInfo.AreFlagsSet(kLedgeFlag_isThin) ? " THIN" : "", ledgeInfo.AreFlagsSet(kLedgeFlag_isWindow) ? " WINDOW" : "", ledgeInfo.AreFlagsSet(kLedgeFlag_endCrouched) ? " ENDCROUCHED" : "", transitionName);
				}

#endif

				if (player.m_jumpButtonIsPressed || enableVaultFromStandingCVar == 3)
				{
					ledgeTransition.m_comingFromOnGround=true;
					ledgeTransition.m_comingFromSprint=player.IsSprinting();

					SStateEventLedge ledgeEvent(ledgeTransition);
					player.StateMachineHandleEventMovement(ledgeEvent);
					return true;
				}
				else
				{
#ifdef STATE_DEBUG
					if (g_pGameCVars->pl_ledgeClamber.debugDraw)
					{
						const char * message = NULL;
						switch (ledgeTransition.m_ledgeTransition)
						{
							case SLedgeTransitionData::eOLT_VaultOnto:
							message = "CLIMB";
							break;

							case SLedgeTransitionData::eOLT_VaultOver:
							message = "VAULT";
							break;

							default:
							CRY_ASSERT_TRACE(0, ("Unexpected ledge transition #%d when trying to display HUD prompt for vault-from-standing!", ledgeTransition.m_ledgeTransition));
							break;
						}

						if (message)
						{
							const float textColor[4] = {1.f, 1.f, 1.f, 1.0f};
							const float bracketColor[4] = {0.7f, 0.7f, 0.7f, 1.0f};
							const float iconSize = 4.f;
							const float textSize = 2.f;
							const float iconColor[4] = {0.3f, 1.f, 0.3f, 1.0f};
							const char * iconText = "A";

							gEnv->pRenderer->Draw2dLabel((gEnv->pRenderer->GetWidth() * 0.5f), (gEnv->pRenderer->GetHeight() * 0.65f), iconSize, bracketColor, true, "( )");
							gEnv->pRenderer->Draw2dLabel((gEnv->pRenderer->GetWidth() * 0.5f), (gEnv->pRenderer->GetHeight() * 0.65f), iconSize, iconColor, true, "%s", iconText);
							gEnv->pRenderer->Draw2dLabel((gEnv->pRenderer->GetWidth() * 0.5f), (gEnv->pRenderer->GetHeight() * 0.72f), textSize, textColor, true, "%s", message);
						}
					}
#endif
				}
			}
		}
	}

	return false;
}