Пример #1
0
void CPlayerStateJump::SetJumpState(CPlayer& player, EJumpState jumpState)
{
	if (jumpState != m_jumpState)
	{
		CRY_ASSERT( m_jumpState >= JState_None && m_jumpState < JState_Total );
		const EJumpState previousJumpState = m_jumpState;

		m_jumpState = jumpState;

		if (IActionController *actionController =	player.GetAnimatedCharacter()->GetActionController())
		{
			if (!m_jumpAction && !player.IsAIControlled())
			{
				FragmentID fragID = FRAGMENT_ID_INVALID;
				switch (jumpState)
				{
				case JState_Jump:
					fragID = PlayerMannequin.fragmentIDs.MotionJump;
					break;
				case JState_Falling:
					fragID = PlayerMannequin.fragmentIDs.MotionInAir;
					break;
				}
				if (fragID != FRAGMENT_ID_INVALID)
				{
					m_jumpAction = new CPlayerJump(fragID, PP_PlayerAction);
					m_jumpAction->AddRef();
					actionController->Queue(*m_jumpAction);
				}
			}
		}
	}
}
Пример #2
0
void CCameraTracking::UpdateAutoFollow(const SViewParams &viewParams, const CPlayer &hero)
{
	if(g_pGameCVars->cl_cam_auto_follow_rate > 0.0f) //auto-tracking
	{
		//if there is an obstacle nearby, don't try to rotate into it
		float lastObstacleDist = (m_vLastObstaclePos - viewParams.position).len();

		if(lastObstacleDist < g_fAutoTrackObstacleDistance)
			return;

		if(hero.GetActorStats()->speedFlat < g_pGameCVars->cl_cam_auto_follow_movement_speed)
		{
			//only rotate when player moves
			m_fAutoRotateSpeed = 0.0f;
			return;
		}

		//get camera direction
		Vec3 camDir = -m_pCamRayScan->GetRayDir(eRAY_CENTER);
		camDir.z = 0.0f;
		camDir.Normalize();
		//get Player direction
		Vec3 heroDirection = (hero.GetAnimatedCharacter()->GetAnimLocation().q * FORWARD_DIRECTION);

		//compute angle between directions
		float dt = camDir.Dot(heroDirection);
		dt = clamp(dt, -1.0f, 1.0f);
		float angle = cry_acosf(dt);

		//check angle being bigger than threshold
		if(angle > g_pGameCVars->cl_cam_auto_follow_threshold)
		{
			float moveSpeed = max(0.002f, gf_PI*m_fFrameTime*g_pGameCVars->cl_cam_auto_follow_rate);
			m_fAutoRotateSpeed = InterpolateTo(max(0.002f, m_fAutoRotateSpeed), moveSpeed, 0.2f);

			//compute rotation direction by taking height part of cross-prod
			float dirVal = camDir.x * heroDirection.y - camDir.y * heroDirection.x;

			CCameraInputHelper *const pCamHelper = hero.GetCameraInputHelper();
			CRY_ASSERT(pCamHelper);

			if(dirVal > 0) //rotate right
				pCamHelper->SetTrackingDelta(-m_fAutoRotateSpeed, 0.0f);
			else //rotate left
				pCamHelper->SetTrackingDelta(m_fAutoRotateSpeed, 0.0f);
		}
		else
			m_fAutoRotateSpeed = 0.0f;
	}
}
Пример #3
0
void CPlayerStateUtil::RestorePlayerPhysics( CPlayer& player )
{
	IAnimatedCharacter* pAC = player.GetAnimatedCharacter();
	if( pAC )
	{
		SAnimatedCharacterParams params;

		params = pAC->GetParams();

		params.inertia = player.GetInertia();
		params.inertiaAccel = player.GetInertiaAccel();
		params.timeImpulseRecover = player.GetTimeImpulseRecover();

		player.SetAnimatedCharacterParams( params );
	}
}
Пример #4
0
//-----------------------------------------------------------------------------------------------
void CPlayerStateGround::ProcessAlignToTarget(const CAutoAimManager& autoAimManager, CPlayer& player, const IActor* pTarget)
{
	CRY_ASSERT(pTarget);

	if( !m_inertiaIsZero )
	{
		SAnimatedCharacterParams params = player.m_pAnimatedCharacter->GetParams();

		params.inertia = 0.f;

		player.SetAnimatedCharacterParams(params);

		m_inertiaIsZero = true;
	}


	Vec3 targetPos = pTarget->GetAnimatedCharacter()->GetAnimLocation().t;
	Vec3 playerPos = player.GetAnimatedCharacter()->GetAnimLocation().t;


	Vec3 displacement = (targetPos - playerPos);

	float targetDistance = autoAimManager.GetCloseCombatSnapTargetRange();

	SCharacterMoveRequest& moveRequest = player.GetMoveRequest();

	moveRequest.type = eCMT_Fly;
	moveRequest.velocity.zero();

	if(displacement.len2() > sqr(targetDistance))
	{
		float distanceToTravel = displacement.len() - targetDistance;

		displacement.z = 0.f;
		displacement.Normalize();		

		const CActor *pTargetActor = static_cast<const CActor*>(pTarget);
		const Vec3 &targetVelocity = pTargetActor->GetActorPhysics().velocity;

		moveRequest.velocity = (displacement * distanceToTravel * autoAimManager.GetCloseCombatSnapTargetMoveSpeed()) + targetVelocity;
	}
}
Пример #5
0
STransitionSelectionParams::STransitionSelectionParams(
	const CMovementTransitions& transitions,
	const CPlayer& player,
	const CMovementRequest& request,
	const Vec3& playerPos,
	const SMovementTransitionsSample& oldSample,
	const SMovementTransitionsSample& newSample,
	const bool bHasLockedBodyTarget,
	const Vec3& targetBodyDirection,
	const Lineseg& safeLine,
	const CTimeValue runningDuration,
	const uint8 _allowedTransitionFlags,
	const float entitySpeed2D,
	const float entitySpeed2DAvg,
	const SExactPositioningTarget*const pExactPositioningTarget,
	const EStance stance,

	SActorFrameMovementParams*const pMoveParams) : m_transitionType(eTT_None), m_transitionDistance(0.0f), m_pseudoSpeed(0.0f), m_travelAngle(0.0f), m_jukeAngle(0.0f), m_stance(stance)
{
	// TODO: check for flatness?

	m_travelAngle = Ang3::CreateRadZ( newSample.bodyDirection, oldSample.moveDirection ); // probably should be oldSample?
	m_context = request.HasContext() ? request.GetContext() : 0;

	// --------------------------------------------------------------------------
	// Calculate vToMoveTarget, vAfterMoveTarget, distToMoveTarget, distAfterMoveTarget & allowedTransitionFlags

	Vec3 vToMoveTarget, vAfterMoveTarget;
	float distToMoveTarget, distAfterMoveTarget;
	uint8 allowedTransitionFlags = _allowedTransitionFlags;

	{
		if (request.HasMoveTarget())
		{
			const Vec3& vMoveTarget = request.GetMoveTarget();
			vToMoveTarget = vMoveTarget - playerPos;
			distToMoveTarget = vToMoveTarget.GetLength2D();

			m_future.hasMoveTarget = true;
			m_future.vMoveTarget = vMoveTarget;

			// Disallow certain transitions when preparing an exact positioning target
			// and fudge the distance to make sure we don't start when too close to it
			if (pExactPositioningTarget && pExactPositioningTarget->preparing && !pExactPositioningTarget->activated)
			{
				allowedTransitionFlags &= ~BIT(eTT_Stop);
				allowedTransitionFlags &= ~BIT(eTT_DirectionChange);

				const Vec3& exactPosLocation = pExactPositioningTarget->location.t;
				const float distFromMoveTargetToExactPosSquared = vMoveTarget.GetSquaredDistance(exactPosLocation);

				const float minimumDangerDistance = 0.1f;
				const float maxDistanceTraveledPerFrame = gEnv->pTimer->GetFrameTime() * 12.5f;
				const float dangerDistance = max(minimumDangerDistance, maxDistanceTraveledPerFrame);

				const bool moveTargetIsWithinDangerDistance = (distFromMoveTargetToExactPosSquared <= sqr(dangerDistance));
				if (moveTargetIsWithinDangerDistance)
				{
					// Fudge distToMoveTarget so we start at least distanceTraveledInTwoFrames
					// This only works for eTT_Start transitions (but we disabled the others above)
					distToMoveTarget = max(0.0f, distToMoveTarget - dangerDistance);
				}
			}

			if (request.HasInflectionPoint())
			{
				const Vec3& vInflectionPoint = request.GetInflectionPoint();
				vAfterMoveTarget = vInflectionPoint - vMoveTarget;
				distAfterMoveTarget = vAfterMoveTarget.GetLength2D();
			}
			else
			{
				vAfterMoveTarget.zero();
				distAfterMoveTarget = 0.0f;
			}
		}
		else
		{
			m_future.hasMoveTarget = false;

			vToMoveTarget.zero();
			vAfterMoveTarget.zero();
			distToMoveTarget = distAfterMoveTarget = 0.0f;
		}
	}

	// --------------------------------------------------------------------------

	const float maximumSpeedForStart = 0.5f;
	const float minimumSpeedForWalkStop = 1.0f;
	const float minimumSpeedForRunStop = 3.5f;
	const float minimumRunningDurationForRunStop = 1.0f; // (seconds)

	const float minimumSpeedForJuke = 4.4f*0.6f; // 4.4 is slowest runspeed in Crysis2; 0.6 is the strafing slowdown
	const float minimumRunningDurationForJuke = 0.1f; // (seconds)

	if (newSample.pseudoSpeed > 0.0f)
	{
		// Either:
		// - we are in a Stop and want to start again <-- note oldPseudoSpeed cannot be used to detect this, it could be already 0 from prev. frame
		// - we are in a Start and want to continue starting [but possibly stop or change direction at the movetarget]
		// - we are stopped and want to Start [but possibly stop or change direction at the movetarget]
		// - we are moving and want to continue moving [but possibly stop or change direction at the movetarget]

		m_pseudoSpeed = newSample.pseudoSpeed;

		if ( (allowedTransitionFlags & (1<<eTT_Start)) && (entitySpeed2DAvg <= maximumSpeedForStart) )
		{
			//New sample's movement direction is accurate for start transitions.
			m_travelAngle = Ang3::CreateRadZ( newSample.bodyDirection, newSample.moveDirection );
			m_transitionType = eTT_Start;
			m_bPredicted = true;
			m_transitionDistance = distToMoveTarget;
			m_future.vMoveDirection = newSample.moveDirection;
			const Vec3 realTargetBodyDirection = bHasLockedBodyTarget ? targetBodyDirection : m_future.vMoveDirection;
			m_targetTravelAngle = Ang3::CreateRadZ( realTargetBodyDirection, m_future.vMoveDirection );
			m_future.qOrientation = Quat::CreateRotationVDir( realTargetBodyDirection );
			MovementTransitionsLog("[%x] Juke failed because we are trying to start", gEnv->pRenderer->GetFrameID());
		}
		else // at the moment start & stop are mutually exclusive
		{
			if (!(allowedTransitionFlags & (1<<eTT_Start)))
				MovementTransitionsLog("[%x] Start failed because current animation state (%s) doesn't support starting", gEnv->pRenderer->GetFrameID(), const_cast<CPlayer&>(player).GetAnimationGraphState() ? const_cast<CPlayer&>(player).GetAnimationGraphState()->GetCurrentStateName() : "");
			else
				MovementTransitionsLog("[%x] Start failed because speed is %f while maximum %f is allowed", gEnv->pRenderer->GetFrameID(), player.GetAnimatedCharacter()->GetEntitySpeedHorizontal(), maximumSpeedForStart);

			m_transitionType = eTT_None;

			// try immediate directionchange first
			if (allowedTransitionFlags & (1<<eTT_DirectionChange))
			{
				if (
						((oldSample.pseudoSpeed == AISPEED_RUN) || (oldSample.pseudoSpeed == AISPEED_SPRINT)) &&
						(runningDuration > minimumRunningDurationForJuke) &&
						(entitySpeed2D >= minimumSpeedForJuke)
					)
				{
					if (gEnv->pAISystem && !gEnv->pAISystem->GetSmartObjectManager()->CheckSmartObjectStates(player.GetEntity(), "Busy"))
					{
						// === IMMEDIATE DIRECTIONCHANGE ===

						// ---------------------------------
						// Assume a directionchange after moving forward for one meter (assumedDistanceToJuke=1)
						// Look up a transition math for that proposed directionchange
						// ---------------------------------
						m_pseudoSpeed = oldSample.pseudoSpeed;
						m_transitionDistance = -1;

						float assumedDistanceToJuke = 1.0f;
						CRY_ASSERT(assumedDistanceToJuke > FLT_EPSILON);

						Vec3 vToProposedMoveTarget = newSample.moveDirection * distToMoveTarget; // vector from current position to current movetarget
						Vec3 vToProposedJukePoint = oldSample.moveDirection * assumedDistanceToJuke;
						Vec3 vAfterProposedJukePoint = (vToProposedMoveTarget - vToProposedJukePoint).GetNormalizedSafe(newSample.moveDirection);

						m_jukeAngle = Ang3::CreateRadZ( vToProposedJukePoint, vAfterProposedJukePoint );
						m_transitionType = eTT_DirectionChange;
						m_bPredicted = false;
						m_future.vMoveDirection = vAfterProposedJukePoint;
						Vec3 realTargetBodyDirection = bHasLockedBodyTarget ? targetBodyDirection : vAfterProposedJukePoint;
						m_targetTravelAngle = Ang3::CreateRadZ( realTargetBodyDirection, vAfterProposedJukePoint );

						MovementTransitionsLog("[%x] Considering angle %+3.2f", gEnv->pRenderer->GetFrameID(), RAD2DEG(m_jukeAngle));

						const STransition* pTransition = NULL;
						int index = -1;
						STransitionMatch bestMatch;
						transitions.FindBestMatch(*this, &pTransition, &index, &bestMatch);

						if (pTransition)
						{
							// -------------------------------------------------------
							// We found a transition matching our guess. Adjust juke point to match the distance of the transition we found
							// -------------------------------------------------------
							float proposedTransitionDistance = (pTransition->minDistance + pTransition->maxDistance)*0.5f;
							CRY_ASSERT(proposedTransitionDistance > FLT_EPSILON);
							vToProposedJukePoint = oldSample.moveDirection * proposedTransitionDistance;
							vAfterProposedJukePoint = vToProposedMoveTarget - vToProposedJukePoint;
							float proposedDistAfterMoveTarget = vAfterProposedJukePoint.GetLength2D();
							vAfterProposedJukePoint.NormalizeSafe(newSample.moveDirection);

							if (proposedDistAfterMoveTarget >= transitions.GetMinDistanceAfterDirectionChange())
							{
								m_jukeAngle = Ang3::CreateRadZ( vToProposedJukePoint, vAfterProposedJukePoint );
								m_future.vMoveDirection = vAfterProposedJukePoint;
								realTargetBodyDirection = bHasLockedBodyTarget ? targetBodyDirection : vAfterProposedJukePoint;
								m_targetTravelAngle = Ang3::CreateRadZ( realTargetBodyDirection, vAfterProposedJukePoint );

								MovementTransitionsLog("[%x] Proposing angle %+3.2f", gEnv->pRenderer->GetFrameID(), RAD2DEG(m_jukeAngle));

								m_transitionDistance = proposedTransitionDistance;
								m_future.qOrientation = Quat::CreateRotationVDir( realTargetBodyDirection );
							}
							else
							{
								MovementTransitionsLog("[%x] Immediate Juke failed because not enough distance after the juke (distance needed = %f, max distance = %f)", gEnv->pRenderer->GetFrameID(), transitions.GetMinDistanceAfterDirectionChange(), proposedDistAfterMoveTarget);
								m_transitionType = eTT_None;
							}
						}
						else
						{
							MovementTransitionsLog("[%x] Immediate Juke failed because no animation found for this angle/stance/speed", gEnv->pRenderer->GetFrameID());
							m_transitionType = eTT_None;
						}
					}
					else
					{
						MovementTransitionsLog("[%x] Immediate Juke failed because smart object is playing", gEnv->pRenderer->GetFrameID());
					}
				}
				else
				{
					if (!((oldSample.pseudoSpeed == AISPEED_RUN) || (oldSample.pseudoSpeed == AISPEED_SPRINT)))
					{
						MovementTransitionsLog("[%x] Immediate Juke failed because current pseudospeed (%f) is not supported", gEnv->pRenderer->GetFrameID(), oldSample.pseudoSpeed);
					}
					else if (runningDuration <= minimumRunningDurationForJuke)
					{
						MovementTransitionsLog("[%x] Immediate Juke failed because running only %f seconds while more than %f is needed", gEnv->pRenderer->GetFrameID(), runningDuration.GetSeconds(), minimumRunningDurationForJuke);
					}
					else //if (entitySpeed2D < minimumSpeedForJuke)
					{
						MovementTransitionsLog("[%x] Immediate Juke failed because speed is only %f while %f is needed", gEnv->pRenderer->GetFrameID(), entitySpeed2D, minimumSpeedForJuke);
					}
				}
			}
			else
			{
				MovementTransitionsLog("[%x] Immediate Juke failed because current animation state (%s) doesn't support juking", gEnv->pRenderer->GetFrameID(), const_cast<CPlayer&>(player).GetAnimationGraphState() ? const_cast<CPlayer&>(player).GetAnimationGraphState()->GetCurrentStateName() : NULL);
			}

			if (m_transitionType == eTT_None) // directionchange wasn't found
			{
				if ((allowedTransitionFlags & (1<<eTT_Stop)) && (distAfterMoveTarget < FLT_EPSILON))
				{
					// === PREDICTED STOP ===
					// We want to stop in the future
					m_transitionType = eTT_Stop;
					m_bPredicted = true;
					m_transitionDistance = distToMoveTarget;
					m_future.vMoveDirection = vToMoveTarget.GetNormalizedSafe(newSample.moveDirection);
					m_arrivalAngle = request.HasDesiredBodyDirectionAtTarget() ? Ang3::CreateRadZ( request.GetDesiredBodyDirectionAtTarget(), m_future.vMoveDirection ) : 0.0f;
					m_future.qOrientation = request.HasDesiredBodyDirectionAtTarget() ? Quat::CreateRotationVDir(request.GetDesiredBodyDirectionAtTarget()) : Quat::CreateRotationVDir(m_future.vMoveDirection);
					MovementTransitionsLog("[%x] Predicted Juke failed because we are trying to stop", gEnv->pRenderer->GetFrameID());
				}
				else if ((allowedTransitionFlags & (1<<eTT_DirectionChange)) && (distAfterMoveTarget >= transitions.GetMinDistanceAfterDirectionChange()))
				{
					// === PREDICTED DIRECTIONCHANGE ===
					// We want to change direction in the future
					// NOTE: This logic will fail if we trigger the juke really late, because then the distToMoveTarget will be very small and the angle calculation not precise
					m_transitionType = eTT_DirectionChange;
					m_bPredicted = true;
					m_transitionDistance = distToMoveTarget;
					m_jukeAngle = Ang3::CreateRadZ( vToMoveTarget, vAfterMoveTarget );
					m_future.vMoveDirection = vAfterMoveTarget.GetNormalized();
					const Vec3 realTargetBodyDirection = bHasLockedBodyTarget ? targetBodyDirection : m_future.vMoveDirection;
					m_future.qOrientation = Quat::CreateRotationVDir( realTargetBodyDirection );
					m_targetTravelAngle = Ang3::CreateRadZ( realTargetBodyDirection, m_future.vMoveDirection );
				}
			}
		}
	}
	else // if (newSample.pseudoSpeed <= 0.0f)
	{
		// Either:
		// - we are in a Stop and want to continue stopping
		// - we are moving and suddenly want to stop
		// - we are in a Start and want to stop <-- oldPseudoSpeed logic is wrong, oldPseudoSpeed will be 0 for a while so we should use real velocity
		// - we are stopped already and just want to stay stopped
		MovementTransitionsLog("[%x] Juke failed because we are not running or trying to stop", gEnv->pRenderer->GetFrameID());
		MovementTransitionsLog("[%x] Start failed because we are not requesting movement", gEnv->pRenderer->GetFrameID());

		if (
			(( (oldSample.pseudoSpeed == AISPEED_RUN) || (oldSample.pseudoSpeed == AISPEED_SPRINT)) && (runningDuration > minimumRunningDurationForRunStop) && (entitySpeed2D >= minimumSpeedForRunStop))
			||
			((oldSample.pseudoSpeed == AISPEED_WALK) && (entitySpeed2D >= minimumSpeedForWalkStop))
		)
		{
			if (allowedTransitionFlags & (1<<eTT_Stop))
			{
				// === IMMEDIATE STOP ===
				if( gEnv->pAISystem )
				{
					ISmartObjectManager* pSmartObjectManager = gEnv->pAISystem->GetSmartObjectManager();
					if (!pSmartObjectManager || !pSmartObjectManager->CheckSmartObjectStates(player.GetEntity(), "Busy"))
					{
						// Trigger immediate stop when currently running and suddenly wanting to stop.
						//
						// NOTE: If this happens right before a forbidden area and the safeLine is not correct
						//    or the Stop transition distance isn't configured properly the AI will enter it..
						//
						m_pseudoSpeed = oldSample.pseudoSpeed;
						m_transitionDistance = -1;
						m_arrivalAngle = 0.0f;
							m_transitionType = eTT_Stop;
						m_bPredicted = false;

						const STransition* pTransition = NULL;
						int index = -1;
						STransitionMatch bestMatch;
						transitions.FindBestMatch(*this, &pTransition, &index, &bestMatch);

						float minDistanceForStop = pTransition ? pTransition->minDistance : 0.0f;

						bool bIsOnSafeLine = IsOnSafeLine(safeLine, playerPos, newSample.moveDirection, minDistanceForStop);

						if (bIsOnSafeLine)
						{
							m_transitionDistance = minDistanceForStop;
							m_future.vMoveDirection = newSample.moveDirection;
							m_future.qOrientation = Quat::CreateRotationVDir(newSample.moveDirection);

							pMoveParams->desiredVelocity = player.GetEntity()->GetWorldRotation().GetInverted() * player.GetLastRequestedVelocity();

							float maxSpeed = player.GetStanceMaxSpeed(m_stance);
							if (maxSpeed > 0.01f)
								pMoveParams->desiredVelocity /= maxSpeed;
						}
						else
						{
							m_transitionType = eTT_None;
						}
					}
				}
			}
		}
	}

	if (request.HasDesiredSpeed())
	{
		m_future.speed = request.GetDesiredTargetSpeed();
	}
}