CExactPositioningTrigger::CExactPositioningTrigger( const Vec3& pos, float width, const Vec3& triggerSize, const Quat& orient, float orientTolerance, float animMovementLength ) : m_pos(pos) , m_width(width) , m_posSize(triggerSize) , m_orient(orient) , m_cosOrientTolerance(cos(orientTolerance)) , m_sideTime(0) , m_distanceErrorFactor(1.0f) , m_animMovementLength(0) , m_distanceError(1000.0f) , m_orientError(1000.0f) , m_oldFwdDir(0.0f) , m_state(eS_Initializing) , m_userPos(pos) , m_userOrient(orient) { CRY_ASSERT(m_pos.IsValid()); CRY_ASSERT(m_userPos.IsValid()); CRY_ASSERT(m_orient.IsValid()); CRY_ASSERT(m_userOrient.IsValid()); CRY_ASSERT(m_posSize.IsValid()); CRY_ASSERT(NumberValid(m_cosOrientTolerance)); CRY_ASSERT(NumberValid(m_width)); // Make sure we have bigger than zero length, // so that the error measurement is not zero no matter what direction. m_animMovementLength = max(animMovementLength, 2.0f); // Make sure the trigger is not rotated, only Z-axis rotations are allowed m_orient.v.x = m_orient.v.y = 0.0f; m_orient.Normalize(); m_userOrient = m_orient; }
void CExactPositioningTrigger::ResetRadius( const Vec3& triggerSize, float orientTolerance ) { CRY_ASSERT(m_pos.IsValid()); CRY_ASSERT(m_userPos.IsValid()); CRY_ASSERT(m_orient.IsValid()); CRY_ASSERT(m_userOrient.IsValid()); CRY_ASSERT(m_posSize.IsValid()); CRY_ASSERT(NumberValid(m_cosOrientTolerance)); if (m_state == eS_Invalid) return; m_state = eS_Initializing; m_posSize = triggerSize; m_cosOrientTolerance = cos(orientTolerance); // TODO: Update should not be called here. Maybe it's only used to set some values or something, not really an update. Update( 0.0f, m_userPos, m_userOrient, false ); }
void CPlayerStateUtil::ApplyFallDamage( CPlayer& player, const float startFallingHeight, const float fHeightofEntity ) { CRY_ASSERT(player.IsClient()); // Zero downwards impact velocity used for fall damage calculations if player was in water within the last 0.5 seconds. // Strength jumping straight up and down should theoretically land with a safe velocity, // but together with the water surface stickyness the velocity can sometimes go above the safe impact velocity threshold. // DEPRECATED: comment left for prosterity in case dedicated server problems re-appear (author cannot test it). // On dedicated server the player can still be flying this frame as well, // since synced pos from client is interpolated/smoothed and will not land immediately, // even though the velocity is set to zero. // Thus we need to use the velocity change instead of landing to identify impact. // DT: 12475: Falling a huge distance to a ledge grab results in no damage. // Now using the last velocity because when ledge grabbing the velocity is unchanged for this frame, thus zero damage is applied. // Assuming this a physics lag issue, using the last good velocity should be more-or-less ok. const float downwardsImpactSpeed = -(float)__fsel(-(player.m_playerStateSwim_WaterTestProxy.GetSwimmingTimer() + 0.5f), player.GetActorPhysics().velocityUnconstrainedLast.z, 0.0f); const SPlayerStats& stats = *player.GetActorStats(); CRY_ASSERT(NumberValid(downwardsImpactSpeed)); const float MIN_FALL_DAMAGE_DISTANCE = 3.0f; const float fallDist = startFallingHeight - fHeightofEntity; if ((downwardsImpactSpeed > 0.0f) && (fallDist > MIN_FALL_DAMAGE_DISTANCE)) { const SPlayerHealth& healthCVars = g_pGameCVars->pl_health; float velSafe = healthCVars.fallDamage_SpeedSafe; float velFatal = healthCVars.fallDamage_SpeedFatal; float velFraction = (float)__fsel(-(velFatal - velSafe), 1.0f , (downwardsImpactSpeed - velSafe) * (float)__fres(velFatal - velSafe)); CRY_ASSERT(NumberValid(velFraction)); if (velFraction > 0.0f) { //Stop crouching after taking falling damage if(player.GetStance() == STANCE_CROUCH) { static_cast<CPlayerInput*>(player.GetPlayerInput())->ClearCrouchAction(); } velFraction = powf(velFraction, gEnv->bMultiplayer ? healthCVars.fallDamage_CurveAttackMP : healthCVars.fallDamage_CurveAttack); const float maxHealth = player.GetMaxHealth(); const float currentHealth = player.GetHealth(); HitInfo hit; hit.dir.zero(); hit.type = CGameRules::EHitType::Fall; hit.shooterId = hit.targetId = hit.weaponId = player.GetEntityId(); const float maxDamage = (float)__fsel(velFraction - 1.0f, maxHealth, max(0.0f, (gEnv->bMultiplayer?maxHealth:currentHealth) - healthCVars.fallDamage_health_threshold)); hit.damage = velFraction * maxDamage; g_pGame->GetGameRules()->ClientHit(hit); #ifdef PLAYER_MOVEMENT_DEBUG_ENABLED player.GetMovementDebug().LogFallDamage(player.GetEntity(), velFraction, downwardsImpactSpeed, hit.damage); } else { player.GetMovementDebug().LogFallDamageNone(player.GetEntity(), downwardsImpactSpeed); } #else } #endif }
void CExactPositioningTrigger::Update( float frameTime, Vec3 userPos, Quat userOrient, bool allowTriggering ) { if (m_state == eS_Invalid) return; CRY_ASSERT(m_pos.IsValid()); CRY_ASSERT(m_userPos.IsValid()); CRY_ASSERT(m_orient.IsValid()); CRY_ASSERT(m_userOrient.IsValid()); CRY_ASSERT(m_posSize.IsValid()); CRY_ASSERT(NumberValid(m_cosOrientTolerance)); CRY_ASSERT(NumberValid(frameTime)); CRY_ASSERT(userPos.IsValid()); CRY_ASSERT(userOrient.IsValid()); m_userPos = userPos; m_userOrient = userOrient; if (m_state == eS_Initializing) m_state = eS_Before; Plane threshold; threshold.SetPlane( m_orient.GetColumn1(), m_pos ); if (threshold.DistFromPlane(userPos) >= 0.0f) { if (m_sideTime < 0.0f) m_sideTime = 0.0f; else m_sideTime += frameTime; } else { if (m_sideTime > 0.0f) m_sideTime = 0.0f; else m_sideTime -= frameTime; } Vec3 curDir = userOrient.GetColumn1(); Vec3 wantDir = m_orient.GetColumn1(); if (m_state == eS_Before) { OBB triggerBox; triggerBox.SetOBB( Matrix33(m_orient), m_posSize+Vec3(0.5f,0.5f,0), ZERO ); if (Overlap::Point_OBB(m_userPos, m_pos, triggerBox)) m_state = eS_Optimizing; } if ((m_state == eS_Optimizing) && allowTriggering) { #ifdef INCLUDE_EXACTPOS_DEBUGGING bool debug = (CAnimationGraphCVars::Get().m_debugExactPos != 0); CPersistantDebug* pPD = CCryAction::GetCryAction()->GetPersistantDebug(); #endif Vec3 bump(0.0f, 0.0f, 0.1f); Vec3 posDistanceError = m_userPos - m_pos; if ( posDistanceError.z > -1.0f && posDistanceError.z < 1.0f ) posDistanceError.z = 0; Vec3 orientFwd = m_orient.GetColumn1(); orientFwd.z = 0.0f; orientFwd.Normalize(); Vec3 rotAnimMovementWanted = orientFwd * m_animMovementLength; Vec3 userFwd = m_userOrient.GetColumn1(); userFwd.z = 0.0f; userFwd.Normalize(); Vec3 rotAnimMovementUser = userFwd * m_animMovementLength; float cosRotError = orientFwd.Dot( userFwd ); float rotError = CLAMP(m_cosOrientTolerance - cosRotError, 0.0f, 1.0f); //Vec3 rotDistanceError = rotAnimMovementUser - rotAnimMovementWanted; float fwdDistance = fabsf(orientFwd.Dot( posDistanceError )); float sideDistance = max( 0.0f, sqrtf( MAX(0,posDistanceError.GetLengthSquared2D() - sqr(fwdDistance)) ) - m_width ); float deltaFwd = m_oldFwdDir < fwdDistance ? fwdDistance - m_oldFwdDir : 0.0f; m_oldFwdDir = fwdDistance; fwdDistance += deltaFwd * 0.5f; deltaFwd = max(0.1f, deltaFwd); f32 distanceError = sqrtf(sqr(fwdDistance) + sqr(sideDistance)); // posDistanceError.len() * m_distanceErrorFactor; f32 temp = 1.0f-sqr(1.0f-rotError*rotError); temp = max(temp,0.0f); //never do a sqrtf with a negative value f32 orientError = sqrtf(temp) * m_animMovementLength; // rotDistanceError.len(); f32 totalDistanceError = distanceError + orientError; if (((m_distanceError * 1.05f) < distanceError) && ((m_orientError * 1.05f) < orientError) && (totalDistanceError < deltaFwd) || (totalDistanceError < deltaFwd*0.5f)) { // found local minimum in distance error, force triggering. m_state = eS_Triggered; m_oldFwdDir = 0.0f; #ifdef INCLUDE_EXACTPOS_DEBUGGING if (debug) { pPD->Begin("AnimationTrigger LocalMinima Triggered", false); pPD->AddPlanarDisc(m_pos + bump, 0.0f, m_distanceError, ColorF(0,1,0,0.5), 10.0f); } #endif } else { m_distanceError = m_distanceError > distanceError ? distanceError : m_distanceError * 0.999f; // should timeout in ~2 secs. on 50 FPS m_orientError = m_orientError > orientError ? orientError : m_orientError - 0.0001f; #ifdef INCLUDE_EXACTPOS_DEBUGGING if (debug) { pPD->Begin("AnimationTrigger LocalMinima Optimizing", true); pPD->AddPlanarDisc(m_pos + bump, 0.0f, m_distanceError, ColorF(1,1,0,0.5), 10.0f); } #endif } #ifdef INCLUDE_EXACTPOS_DEBUGGING if (debug) { pPD->AddLine(m_userPos + bump, m_pos + bump, ColorF(1,0,0,1), 10.0f); pPD->AddLine(m_userPos + rotAnimMovementUser + bump, m_pos + rotAnimMovementWanted + bump, ColorF(1,0,0,1), 10.0f); pPD->AddLine(m_pos + bump, m_pos + rotAnimMovementWanted + bump, ColorF(1,0.5,0,1), 10.0f); pPD->AddLine(m_userPos + bump, m_pos + rotAnimMovementUser + bump, ColorF(1,0.5,0,1), 10.0f); } #endif } CRY_ASSERT(m_pos.IsValid()); CRY_ASSERT(m_userPos.IsValid()); CRY_ASSERT(m_orient.IsValid()); CRY_ASSERT(m_userOrient.IsValid()); CRY_ASSERT(m_posSize.IsValid()); CRY_ASSERT(NumberValid(m_cosOrientTolerance)); }
// ---------------------------------------------------------------------------- void CExactPositioning::UpdateTargetPointToFinishPoint() { if ( !m_pAnimatedCharacter ) return; IEntity* pEntity = m_pAnimatedCharacter->GetEntity(); if ( !pEntity ) return; IActionController* pActionController = m_pAnimatedCharacter->GetActionController(); if ( !pActionController ) return; /* CPersistantDebug* pPD = CCryAction::GetCryAction()->GetPersistantDebug(); bool debug = CAnimationGraphCVars::Get().m_debugExactPos != 0; if (debug) pPD->Begin( string( pEntity->GetName() ) + "_recalculatetriggerpositions", true ); Quat actorRot = pEntity->GetWorldRotation(); Vec3 targetPosition = m_animationTargetRequest.position; Vec3 targetDirection = m_animationTargetRequest.direction.GetNormalizedSafe(ZERO); Vec3 bumpUp(0,0, pEntity->GetWorldPos().z+4); SAnimationMovement movement; float seconds = 0.0f; ColorF dbgClr1, dbgClr2; // TODO: figure out animationmovement and duration //seconds = m_triggerMovement.duration; //movement = m_triggerMovement; if ( m_pExactPositioningTarget->isNavigationalSO && movement.translation.IsZero( FLT_EPSILON ) ) { #if STORE_TAG_STRINGS const char* fragmentName = pActionController->GetContext().controllerDef.m_fragmentIDs.GetTagName( m_fragmentID ); #else const char* fragmentName = "<n/a>"; #endif CryWarning( VALIDATOR_MODULE_GAME, VALIDATOR_WARNING, "MannequinAGExactPositioning: Beaming back to the beginning of the smart object because the fragment had no translation: '%s' (id = %d) (entity = '%s'; controllerDef = '%s')", fragmentName ? fragmentName : "<invalid>", m_fragmentID, pEntity ? pEntity->GetName() : "<invalid>", pActionController->GetContext().controllerDef.m_filename ); } targetPosition += Quat::CreateRotationV0V1( FORWARD_DIRECTION, targetDirection ) * movement.translation; targetDirection = movement.rotation * targetDirection; seconds = movement.duration; dbgClr1 = ColorF(1,1,0,1); dbgClr2 = ColorF(1,0,0,1); Vec3 realStartPosition = pEntity->GetWorldPos(); Quat realStartOrientation = actorRot; if (debug) { pPD->Begin( string(pEntity->GetName()) + "_finalpathline", true ); Vec3 targetPosition2D(targetPosition.x, targetPosition.y, realStartPosition.z); Vec3 realStartDirection = realStartOrientation.GetColumn1(); pPD->AddLine(realStartPosition, targetPosition2D, ColorF(1,1,0,1), 5.0f); pPD->AddDirection(realStartPosition, 0.5f, realStartDirection, ColorF(1,0,0,1), 5.0f); pPD->AddDirection(targetPosition2D, 0.5f, targetDirection, ColorF(1,1,1,1), 5.0f); pPD->AddDirection(realStartPosition + bumpUp, 1, realStartOrientation.GetColumn1(), dbgClr1, 5); pPD->AddSphere(realStartPosition + bumpUp, 0.1f, dbgClr1, 5); } Quat startOrientation = realStartOrientation; // start orientation may not be within bounds; we'll fake things to get it there // figure out our expected end orientation, and hence direction Quat realEndOrientation = startOrientation * movement.rotation; Quat endOrientation = realEndOrientation; Vec3 realEndDirection = realEndOrientation.GetColumn1(); Vec3 endDirection = realEndDirection; // how much are we out from our limitations? float dot = realEndDirection.Dot(targetDirection); float angleToDesiredDirection = acos_tpl( clamp_tpl(dot,-1.f,1.f) ); if (angleToDesiredDirection > m_animationTargetRequest.directionTolerance) { float fractionOfTurnNeeded = m_animationTargetRequest.directionTolerance / angleToDesiredDirection; Quat rotationFromDesiredToCurrent = Quat::CreateRotationV0V1( targetDirection, realEndDirection ); Quat amountToRotate = Quat::CreateSlerp( Quat::CreateIdentity(), rotationFromDesiredToCurrent, fractionOfTurnNeeded ); endDirection = amountToRotate * targetDirection; endOrientation = Quat::CreateRotationV0V1( FORWARD_DIRECTION, endDirection ); startOrientation = endOrientation * movement.rotation.GetInverted(); } // find out approx. where the animation ends Vec3 realEndPoint = realStartPosition + realStartOrientation * movement.translation; Vec3 endPoint = realStartPosition + startOrientation * movement.translation; if (debug) { pPD->AddDirection(realEndPoint + bumpUp, 1, realEndDirection, dbgClr1, 5); pPD->AddSphere(realEndPoint + bumpUp, 0.1f, dbgClr1, 5); pPD->AddSphere(endPoint + bumpUp, 0.1f, dbgClr2, 5); } // if the animation is outside of the target sphere, project it to the boundary Vec3 dirTargetToEndPoint = endPoint - targetPosition; float distanceTargetToEndPoint = dirTargetToEndPoint.GetLength(); if (distanceTargetToEndPoint > 0.1f) { dirTargetToEndPoint /= distanceTargetToEndPoint; dirTargetToEndPoint *= 0.08f; endPoint = targetPosition + dirTargetToEndPoint; } // calculate error velocities (between where we expect we'll end up, and where we'll really end up) if (seconds > FLT_EPSILON) { m_pExactPositioningTarget->errorRotationalVelocity = Quat::CreateSlerp( Quat::CreateIdentity(), realEndOrientation.GetInverted() * endOrientation, 1.0f/seconds ); m_pExactPositioningTarget->errorVelocity = (endPoint - realEndPoint)/ seconds; if (debug) pPD->AddDirection(endPoint + bumpUp, 1, m_pExactPositioningTarget->errorVelocity, dbgClr2, 5); } else { m_pExactPositioningTarget->errorRotationalVelocity = Quat::CreateIdentity(); m_pExactPositioningTarget->errorVelocity = ZERO; endPoint = targetPosition; endOrientation = m_pExactPositioningTarget->orientation; } */ // Until we can figure out where the animation is going to end, only // update the animtarget on Completing if (m_state == eTS_Completing) { Vec3 endPoint = pEntity->GetWorldPos(); const Quat& endOrientation = pEntity->GetWorldRotation(); // Project end point on ground. if (m_actorTargetParams.projectEnd == true) { ray_hit hit; int rayFlags = rwi_stop_at_pierceable|(geom_colltype_player<<rwi_colltype_bit); IPhysicalEntity* skipEnts[1]; skipEnts[0] = pEntity->GetPhysics(); if (gEnv->pPhysicalWorld->RayWorldIntersection(endPoint + Vec3(0,0,1.0f), Vec3(0,0,-2.0f), ent_terrain | ent_static | ent_rigid | ent_sleeping_rigid, rayFlags, &hit, 1, skipEnts, 1)) { endPoint = hit.pt; } } // set the position/orientation m_pExactPositioningTarget->location.t = endPoint; m_pExactPositioningTarget->location.q = endOrientation; m_pExactPositioningTarget->orientationTolerance = m_actorTargetParams.directionTolerance; m_pExactPositioningTarget->startWidth = m_actorTargetParams.startWidth; m_pExactPositioningTarget->positionWidth = 0.05f; m_pExactPositioningTarget->positionDepth = 0.05f; CRY_ASSERT(m_pExactPositioningTarget->location.IsValid()); CRY_ASSERT(NumberValid(m_pExactPositioningTarget->startWidth)); CRY_ASSERT(NumberValid(m_pExactPositioningTarget->positionWidth)); CRY_ASSERT(NumberValid(m_pExactPositioningTarget->positionDepth)); CRY_ASSERT(NumberValid(m_pExactPositioningTarget->orientationTolerance)); } // tell AI the ending position if ( m_pExactPositioningListener ) { m_pExactPositioningListener->ExactPositioningNotifyFinishPoint( m_pExactPositioningTarget->location.t ); } }
void CMFXEffect::Execute(SMFXRunTimeEffectParams& params) { FUNCTION_PROFILER(gEnv->pSystem, PROFILE_ACTION); // TEMP Code: testing footsteps; can this stay here??? IEntity *pEnt = NULL; if (m_effectParams.libName == "footstep_player") { if (params.audioProxyEntityId != 0) { pEnt = gEnv->pEntitySystem->GetEntity(params.audioProxyEntityId); if (pEnt != NULL) { IEntityAudioProxyPtr pIEntityAudioProxy = crycomponent_cast<IEntityAudioProxyPtr>(pEnt->CreateProxy(ENTITY_PROXY_AUDIO)); TAudioControlID nFootstepTriggerID = INVALID_AUDIO_CONTROL_ID; gEnv->pAudioSystem->GetAudioTriggerID(m_effectParams.libName, nFootstepTriggerID); TAudioControlID nFirstPersonSwitchID = INVALID_AUDIO_CONTROL_ID; TAudioSwitchStateID nFirstPersonStateID = INVALID_AUDIO_SWITCH_STATE_ID; gEnv->pAudioSystem->GetAudioSwitchID("1stOr3rdP", nFirstPersonSwitchID); gEnv->pAudioSystem->GetAudioSwitchStateID(nFirstPersonSwitchID, params.playSoundFP ? "1stP" : "3rdP", nFirstPersonStateID); TAudioControlID nSurfaceSwitchID = INVALID_AUDIO_CONTROL_ID; TAudioSwitchStateID nSurfaceStateID = INVALID_AUDIO_SWITCH_STATE_ID; gEnv->pAudioSystem->GetAudioSwitchID("SurfaceType", nSurfaceSwitchID); gEnv->pAudioSystem->GetAudioSwitchStateID(nSurfaceSwitchID, m_effectParams.name, nSurfaceStateID); TAudioControlID nSpeedRtpcId = INVALID_AUDIO_CONTROL_ID; gEnv->pAudioSystem->GetAudioRtpcID("character_speed", nSpeedRtpcId); float fSpeed = 0.0f; for (int i=0; i < params.MAX_SOUND_PARAMS; ++i) { const char* soundParamName = params.soundParams[i].paramName; if ((soundParamName != NULL) && (soundParamName[0] != '\0') && (_stricmp(soundParamName, "speed") == 0)) { float const fValue = params.soundParams[i].paramValue; CRY_ASSERT(NumberValid(fValue)); fSpeed = fValue; break; } } pIEntityAudioProxy->SetSwitchState(nFirstPersonSwitchID, nFirstPersonStateID); pIEntityAudioProxy->SetSwitchState(nSurfaceSwitchID, nSurfaceStateID); pIEntityAudioProxy->SetRtpcValue(nSpeedRtpcId, fSpeed); pIEntityAudioProxy->ExecuteTrigger(nFootstepTriggerID, eLSM_None); } } } else if ((m_effectParams.libName == "bulletimpacts") && ((params.playflags & MFX_PLAY_SOUND) != 0)) { TAudioControlID nAudioTriggerID = INVALID_AUDIO_CONTROL_ID; gEnv->pAudioSystem->GetAudioTriggerID(m_effectParams.name, nAudioTriggerID); if (nAudioTriggerID != INVALID_AUDIO_CONTROL_ID) { IAudioProxy* const pIAudioProxy = gEnv->pAudioSystem->GetFreeAudioProxy(); if (pIAudioProxy != NULL) { pIAudioProxy->Initialize("Projectile"); pIAudioProxy->SetPosition(params.pos); pIAudioProxy->SetObstructionCalcType(eAOOCT_SINGLE_RAY); pIAudioProxy->SetCurrentEnvironments(); pIAudioProxy->ExecuteTrigger(nAudioTriggerID, eLSM_None); pIAudioProxy->Release(); } } } else if ((m_effectParams.libName == "collisions") && ((params.playflags & MFX_PLAY_SOUND) != 0)) { TAudioControlID nAudioTriggerID = INVALID_AUDIO_CONTROL_ID; gEnv->pAudioSystem->GetAudioTriggerID(m_effectParams.name, nAudioTriggerID); if (nAudioTriggerID != INVALID_AUDIO_CONTROL_ID) { IAudioProxy* const pIAudioProxy = gEnv->pAudioSystem->GetFreeAudioProxy(); if (pIAudioProxy != NULL) { pIAudioProxy->Initialize("Collision"); pIAudioProxy->SetPosition(params.pos); pIAudioProxy->SetObstructionCalcType(eAOOCT_SINGLE_RAY); pIAudioProxy->SetCurrentEnvironments(); pIAudioProxy->ExecuteTrigger(nAudioTriggerID, eLSM_None); pIAudioProxy->Release(); } } } std::vector<IMFXEffectPtr>::iterator iter = m_effects.begin(); std::vector<IMFXEffectPtr>::iterator iterEnd = m_effects.end(); while (iter != iterEnd) { IMFXEffectPtr& cur = *iter; if (cur) { if (cur->CanExecute(params)) cur->Execute(params); } ++iter; } }