//----------------------------------------------------------------------- bool CSpectacularKill::CanExecuteOnTarget(const CActor* pTarget, const SSpectacularKillAnimation& anim) const { CRY_ASSERT(pTarget); // can't spectacular kill actors in vehicles if(pTarget->GetLinkedVehicle()) return false; // can't spectacular kill when in a hit/death reaction if (pTarget->GetActorClass() == CPlayer::GetActorClassType()) { CHitDeathReactionsConstPtr pHitDeathReactions = static_cast<const CPlayer*>(pTarget)->GetHitDeathReactions(); if (pHitDeathReactions && pHitDeathReactions->IsInReaction() && pHitDeathReactions->AreReactionsForbidden()) { SK_DEBUG_LOG("Can't start from %s to %s: the target is playing an uninterruptible hit/death reaction", m_pOwner->GetEntity()->GetName(), pTarget->GetEntity()->GetName()); return false; } } if (!CanSpectacularKillOn(pTarget)) return false; // Can't start if they are taking part on other cooperative animation ICooperativeAnimationManager* pCooperativeAnimationManager = gEnv->pGame->GetIGameFramework()->GetICooperativeAnimationManager(); if (pCooperativeAnimationManager->IsActorBusy(m_pOwner->GetEntityId()) || pCooperativeAnimationManager->IsActorBusy(m_targetId)) { SK_DEBUG_LOG("Can't start from %s to %s: some of them are taking part in a cooperative animation already", m_pOwner->GetEntity()->GetName(), pTarget->GetEntity()->GetName()); return false; } const Vec3 vKillerPos = m_pOwner->GetEntity()->GetWorldPos(); const Vec3 vTargetPos = pTarget->GetEntity()->GetWorldPos(); // The height distance between killer and victim needs to be acceptably small (simple check for terrain flatness) // [11/08/2010 davidr] ToDo: Use deferred primitive world intersection checks with asset-dependant dimensions for height // and obstacles detection if (fabs_tpl(vKillerPos.z - vTargetPos.z) > g_pGameCVars->g_spectacularKill.maxHeightBetweenActors) { SK_DEBUG_LOG("Can't start from %s to %s: Ground between killer and target is not flat (height distance is %f)", m_pOwner->GetEntity()->GetName(), pTarget->GetEntity()->GetName(), fabs_tpl(vKillerPos.z - vTargetPos.z)); return false; } // Obstacle check if (ObstacleCheck(vKillerPos, vTargetPos, anim)) { SK_DEBUG_LOG("Can't start from %s to %s: Obstacles have been found between the actors", m_pOwner->GetEntity()->GetName(), pTarget->GetEntity()->GetName()); return false; } return true; }
void CRuntimeAreaObject::UpdateParameterValues(IEntity* const pEntity, TAudioParameterMap& rParamMap) { static float const fParamEpsilon = 0.001f; static float const fMaxDensity = 256.0f; IEntityAudioProxy* const pAudioProxy = static_cast<IEntityAudioProxy*>(pEntity->CreateProxy(ENTITY_PROXY_AUDIO).get()); if (pAudioProxy != NULL) { ISurfaceType* aSurfaceTypes[MMRM_MAX_SURFACE_TYPES]; memset(aSurfaceTypes, 0x0, sizeof(aSurfaceTypes)); float aDensities[MMRM_MAX_SURFACE_TYPES]; memset(aDensities, 0x0, sizeof(aDensities)); gEnv->p3DEngine->GetIMergedMeshesManager()->QueryDensity(pEntity->GetPos(), aSurfaceTypes, aDensities); for (int i = 0; i < MMRM_MAX_SURFACE_TYPES && (aSurfaceTypes[i] != NULL); ++i) { float const fNewParamValue = aDensities[i]/fMaxDensity; TSurfaceCRC const nSurfaceCrc = CCrc32::ComputeLowercase(aSurfaceTypes[i]->GetName()); TAudioParameterMap::iterator iSoundPair = rParamMap.find(nSurfaceCrc); if (iSoundPair == rParamMap.end()) { if (fNewParamValue > 0.0f) { // The sound for this surface is not yet playing on this entity, needs to be started. TAudioControlMap::const_iterator const iAudioControls = m_cAudioControls.find(nSurfaceCrc); if (iAudioControls != m_cAudioControls.end()) { SAudioControls const& rAudioControls = iAudioControls->second; pAudioProxy->SetRtpcValue(rAudioControls.nRtpcID, fNewParamValue); pAudioProxy->ExecuteTrigger(rAudioControls.nTriggerID, eLSM_None); rParamMap.insert( std::pair<TSurfaceCRC, SAreaSoundInfo>( nSurfaceCrc, SAreaSoundInfo(rAudioControls, fNewParamValue))); } } } else { SAreaSoundInfo& oSoundInfo = iSoundPair->second; if (fabs_tpl(fNewParamValue - oSoundInfo.fParameter) >= fParamEpsilon) { oSoundInfo.fParameter = fNewParamValue; pAudioProxy->SetRtpcValue(oSoundInfo.oAudioControls.nRtpcID, oSoundInfo.fParameter); } } } } }
//------------------------------------------------------------------------------------ // Adjust the aim dir before we pass it to the torsoAim pose modifier, this allows us // to have the weapon deviate from the camera in certain circumstances // Should only be called once per frame as it time-steps internal vars //------------------------------------------------------------------------------------ void CLocalPlayerComponent::AdjustTorsoAimDir(float fFrameTime, Vec3 &aimDir) { const f32 HALF_PI = gf_PI * 0.5f; float newElevLimit = HALF_PI; const f32 MIN_FLATTEN_LEVEL = -0.3f; const f32 MAX_FLATTEN_LEVEL = -0.1f; const f32 TARGET_FLATTEN_ELEV = -0.2f; const f32 LIMIT_CHANGE_RATE = HALF_PI; if (g_pGameCVars->pl_swimAlignArmsToSurface && m_rPlayer.IsSwimming() && (m_rPlayer.m_playerStateSwim_WaterTestProxy.GetRelativeWaterLevel() > MIN_FLATTEN_LEVEL)) { newElevLimit = (m_rPlayer.m_playerStateSwim_WaterTestProxy.GetRelativeWaterLevel() - MIN_FLATTEN_LEVEL) / (MAX_FLATTEN_LEVEL - MIN_FLATTEN_LEVEL); newElevLimit = LERP(gf_PI * 0.5f, TARGET_FLATTEN_ELEV, clamp_tpl(newElevLimit, 0.0f, 1.0f)); } float limitDelta = LIMIT_CHANGE_RATE * fFrameTime; float limitDiff = newElevLimit - m_stapElevLimit; float smoothedLimit = (float) fsel(fabs_tpl(limitDiff) - limitDelta, m_stapElevLimit + (fsgnf(limitDiff) * limitDelta), newElevLimit); m_stapElevLimit = smoothedLimit; if (smoothedLimit < HALF_PI) { //--- Need to limit, convert to yaw & elev, limit & then convert back float yaw, elev; float xy = aimDir.GetLengthSquared2D(); if (xy > 0.001f) { yaw = atan2_tpl(aimDir.y,aimDir.x); elev = asin_tpl(clamp_tpl(aimDir.z, -1.f, +1.f)); } else { yaw = 0.f; elev = (float)fsel(aimDir.z, +1.f, -1.f) * (gf_PI*0.5f); } elev = min(elev, smoothedLimit); float sinYaw, cosYaw; float sinElev, cosElev; sincos_tpl(yaw, &sinYaw, &cosYaw); sincos_tpl(elev, &sinElev, &cosElev); aimDir.x = cosYaw * cosElev; aimDir.y = sinYaw * cosElev; aimDir.z = sinElev; } }
//-------------------------------------------------------------------------------------------------- // Name: ValidateExtractedOutline // Desc: Performs a final validation pass on the extracted outline //-------------------------------------------------------------------------------------------------- bool CBreakableGlassSystem::ValidateExtractedOutline(SBreakableGlassPhysData& data, SBreakableGlassInitParams& initParams) { bool valid = true; // Check for overlapping points (leads to FPE during triangulation) if (initParams.pInitialFrag && initParams.numInitialFragPts > 0) { const Vec2* pPts = initParams.pInitialFrag; const uint numEdges = initParams.numInitialFragPts-1; const float minEdgeLen = 0.0001f; for (uint i = 0, j = 1; i < numEdges; ++i, ++j) { const Vec2 edge(pPts[i] - pPts[j]); if (edge.GetLength2() < minEdgeLen) { LOG_GLASS_ERROR("Extracted mesh has invalid edges."); valid = false; break; } } } // Check for overlapping UVs (leads to FPE during uv basis calculation) if (valid) { const Vec2 uvPtA(data.uvBasis[0].x, data.uvBasis[0].y); const Vec2 uvPtB(data.uvBasis[1].x, data.uvBasis[1].y); const Vec2 uvPtC(data.uvBasis[2].x, data.uvBasis[2].y); const Vec2 uvEdge0(uvPtC - uvPtA); const Vec2 uvEdge1(uvPtB - uvPtA); const float dot00 = uvEdge0.Dot(uvEdge0); const float dot01 = uvEdge0.Dot(uvEdge1); const float dot11 = uvEdge1.Dot(uvEdge1); const float epsilon = 0.001f; if (fabs_tpl(dot00 * dot11 - dot01 * dot01) < epsilon) { LOG_GLASS_ERROR("Extracted mesh has invalid uv layout."); valid = false; } } return valid; }//-------------------------------------------------------------------------------------------------
void CPlayerInput::PreUpdate() { CMovementRequest request; // get rotation into a manageable form float mouseSensitivity; if (m_pPlayer->InZeroG()) mouseSensitivity = 0.00333f*MAX(0.01f, g_pGameCVars->cl_sensitivityZeroG); else mouseSensitivity = 0.00333f*MAX(0.01f, g_pGameCVars->cl_sensitivity); mouseSensitivity *= gf_PI / 180.0f;//doesnt make much sense, but after all helps to keep reasonable values for the sensitivity cvars //these 2 could be moved to CPlayerRotation mouseSensitivity *= m_pPlayer->m_params.viewSensitivity; mouseSensitivity *= m_pPlayer->GetMassFactor(); COffHand * pOffHand=static_cast<COffHand*>(m_pPlayer->GetWeaponByClass(CItem::sOffHandClass)); if(pOffHand && (pOffHand->GetOffHandState()&eOHS_HOLDING_NPC)) mouseSensitivity *= pOffHand->GetObjectMassScale(); // When carrying object/enemy, adapt mouse sensitiviy to feel the weight // Designers requested we ignore single-handed objects (1 == m_iCarryingObject) if(2 == m_iCarryingObject) { mouseSensitivity /= 2.0f; } if(m_fCrouchPressedTime>0.0f) { float fNow = gEnv->pTimer->GetAsyncTime().GetMilliSeconds(); if((fNow - m_fCrouchPressedTime) > 300.0f) { if(m_actions & ACTION_CROUCH) { m_actions &= ~ACTION_CROUCH; m_actions |= ACTION_PRONE; } m_fCrouchPressedTime = -1.0f; } } Ang3 deltaRotation(m_deltaRotation * mouseSensitivity); if (m_pStats->isFrozen.Value() && m_pPlayer->IsPlayer() && m_pPlayer->GetHealth()>0) { float sMin = g_pGameCVars->cl_frozenSensMin; float sMax = g_pGameCVars->cl_frozenSensMax; float mult = sMin + (sMax-sMin)*(1.f-m_pPlayer->GetFrozenAmount(true)); deltaRotation *= mult; m_pPlayer->UpdateUnfreezeInput(m_deltaRotation, m_deltaMovement-m_deltaMovementPrev, mult); } // apply rotation from xinput controller if(!m_bDisabledXIRot) { // Controller framerate compensation needs frame time! // The constant is to counter for small frame time values. // adjust some too small values, should be handled differently later on Ang3 xiDeltaRot=m_xi_deltaRotation*gEnv->pTimer->GetFrameTime() * mouseSensitivity * 50.0f; SmoothControllerInput(xiDeltaRot); ControlCameraMode(); // Applying aspect modifiers if (g_pGameCVars->ctrl_aspectCorrection > 0) { int vx, vy, vw, vh; gEnv->pRenderer->GetViewport(&vx, &vy, &vw, &vh); float med=((float)vw+vh)/2.0f; float crW=((float)vw)/med; float crH=((float)vh)/med; xiDeltaRot.x*=g_pGameCVars->ctrl_aspectCorrection == 2 ? crW : crH; xiDeltaRot.z*=g_pGameCVars->ctrl_aspectCorrection == 2 ? crH : crW; } if(g_pGameCVars->cl_invertController) xiDeltaRot.x*=-1; deltaRotation+=xiDeltaRot; IVehicle *pVehicle = m_pPlayer->GetLinkedVehicle(); if (pVehicle) { if (m_pPlayer->m_pVehicleClient) { m_pPlayer->m_pVehicleClient->PreUpdate(pVehicle, m_pPlayer->GetEntityId(), gEnv->pTimer->GetFrameTime()); } //FIXME:not really good m_actions = 0; m_deltaMovement.Set(0,0,0); m_deltaRotation.Set(0,0,0); } } if(m_bUseXIInput) { m_deltaMovement.x = m_xi_deltaMovement.x; m_deltaMovement.y = m_xi_deltaMovement.y; m_deltaMovement.z = 0; if (m_xi_deltaMovement.len2()>0.0f) m_actions |= ACTION_MOVE; else m_actions &= ~ACTION_MOVE; } bool animControlled(m_pPlayer->m_stats.animationControlled); // If there was a recent serialization, ignore the delta rotation, since it's accumulated over several frames. if ((m_lastSerializeFrameID + 2) > gEnv->pRenderer->GetFrameID()) deltaRotation.Set(0,0,0); //if(m_pPlayer->m_stats.isOnLadder) //deltaRotation.z = 0.0f; const SCVars* pGameCVars = g_pGameCVars; if(pGameCVars->cl_cam_orbit != 0 && m_pPlayer->IsClient() && m_pPlayer->IsThirdPerson()) { static bool IsInit = false; if (!IsInit) { m_pPlayer->m_camViewMtxFinal = Matrix33(gEnv->pRenderer->GetCamera().GetViewMatrix()); IsInit = true; } float frameTime=gEnv->pTimer->GetFrameTime(); float frameTimeNormalised=(frameTime>1 ? 1 : frameTime<0.0001f ? 0.0001f : frameTime)*30; // 1/30th => 1 1/60th =>0.5 etc float frameTimeClamped=(frameTime>1 ? 1 : frameTime<0.0001f ? 0.0001f : frameTime); m_pCameraInputHelper->UpdateCameraInput(deltaRotation, frameTimeClamped,frameTimeNormalised); // also modifies deltaRotation. } if (!animControlled) request.AddDeltaRotation( deltaRotation ); // add some movement... if (!m_pStats->isFrozen.Value() && !animControlled) request.AddDeltaMovement( FilterMovement(m_deltaMovement) ); m_deltaMovementPrev = m_deltaMovement; // handle actions if (m_actions & ACTION_JUMP) { if (m_pPlayer->GetStance() != STANCE_PRONE) request.SetJump(); else m_actions &= ~ACTION_JUMP; //m_actions &= ~ACTION_PRONE; /*if (m_pPlayer->GetStance() != STANCE_PRONE) { if(m_pPlayer->GetStance() == STANCE_STAND || m_pPlayer->TrySetStance(STANCE_STAND)) request.SetJump(); } else if(!m_pPlayer->TrySetStance(STANCE_STAND)) m_actions &= ~ACTION_JUMP; else m_actions &= ~ACTION_PRONE;*/ } if (m_pPlayer->m_stats.isOnLadder) { m_actions &= ~ACTION_PRONE; m_actions &= ~ACTION_CROUCH; } request.SetStance(FigureOutStance()); float pseudoSpeed = 0.0f; if (m_deltaMovement.len2() > 0.0f) { pseudoSpeed = m_pPlayer->CalculatePseudoSpeed(m_pPlayer->m_stats.bSprinting); } /* design changed: sprinting with controller is removed from full stick up to Left Bumper if(m_bUseXIInput && m_xi_deltaMovement.len2() > 0.999f) { m_actions |= ACTION_SPRINT; } else if(m_bUseXIInput) { m_actions &= ~ACTION_SPRINT; }*/ request.SetPseudoSpeed(pseudoSpeed); if (m_deltaMovement.GetLength() > 0.1f) { float moveAngle = (float)RAD2DEG(fabs_tpl(cry_atan2f(-m_deltaMovement.x, fabsf(m_deltaMovement.y)<0.01f?0.01f:m_deltaMovement.y))); request.SetAllowStrafing(moveAngle > 20.0f); } else { request.SetAllowStrafing(true); } // send the movement request to the appropriate spot! m_pPlayer->m_pMovementController->RequestMovement( request ); m_pPlayer->m_actions = m_actions; // reset things for next frame that need to be m_lastMouseRawInput = m_deltaRotation; m_deltaRotation = Ang3(0,0,0); //static float color[] = {1,1,1,1}; //gEnv->pRenderer->Draw2dLabel(100,50,1.5,color,false,"deltaMovement:%f,%f", m_deltaMovement.x,m_deltaMovement.y); // PLAYERPREDICTION m_pPlayer->GetGameObject()->ChangedNetworkState(INPUT_ASPECT); // ~PLAYERPREDICTION }
void CPlayerRotation::GetStanceAngleLimits(float &minAngle,float &maxAngle) { EStance stance = m_player.GetStance(); switch(stance) { default: case STANCE_CROUCH: case STANCE_STAND: minAngle = -80.0f; maxAngle = 80.0f; break; case STANCE_PRONE: minAngle = -35.0f; maxAngle = 45.0f; break; } //Limit camera rotation on ladders(to prevent clipping) if(m_player.m_stats.isOnLadder) { minAngle = -40.0f; maxAngle = 80.0f; } if(m_stats.grabbedHeavyEntity!=0) { minAngle = -35.0f; //Limit angle to prevent clipping, throw objects at feet, etc... } // SNH: additional restriction based on weapon type if prone. if(m_player.GetStance() == STANCE_PRONE && g_pGameCVars->g_proneAimAngleRestrict_Enable != 0) { float dist = 0.0f; CItem *pItem = (CItem *)(m_player.GetCurrentItem()); if(pItem) dist = pItem->GetParams().raise_distance; SMovementState movestate; m_player.m_pMovementController->GetMovementState(movestate); // try a cylinder intersection test IPhysicalEntity *pIgnore[2]; pIgnore[0] = m_player.GetEntity()->GetPhysics(); pIgnore[1] = pItem ? pItem->GetEntity()->GetPhysics() : NULL; primitives::cylinder cyl; cyl.r = 0.05f; cyl.axis = movestate.aimDirection; cyl.hh = dist; cyl.center = movestate.weaponPosition + movestate.aimDirection*cyl.hh; //gEnv->pRenderer->GetIRenderAuxGeom()->DrawCylinder(cyl.center, cyl.axis, cyl.r, cyl.hh, ColorF(0.4f,1.0f,0.6f, 0.2f)); float n = 0.0f; geom_contact *contacts; intersection_params params; WriteLockCond lockContacts; params.bStopAtFirstTri = false; params.bSweepTest = false; params.bNoBorder = true; params.bNoAreaContacts = true; n = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(primitives::cylinder::type, &cyl, Vec3(0,0,2), ent_static|ent_terrain, &contacts, 0, geom_colltype_player, ¶ms, 0, 0, pIgnore, pIgnore[1]?2:1), lockContacts; int ret = (int)n; geom_contact *currentc = contacts; for(int i=0; i<ret; i++) { geom_contact *contact = currentc; if(contact && (fabs_tpl(contact->n.z)>0.2f)) { Vec3 dir = contact->pt - movestate.weaponPosition; dir.NormalizeSafe(); Vec3 horiz = dir; horiz.z = 0.0f; horiz.NormalizeSafe(); float cosangle = dir.Dot(horiz); Limit(cosangle, -1.0f, 1.0f); float newMin = acos_tpl(cosangle); newMin = -newMin * 180.0f / gf_PI; //float col[] = {1,1,1,1}; //gEnv->pRenderer->Draw2dLabel(100,100, 1.0f, col, false, "minangle: %.2f", newMin); //gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(contact->pt, 0.03f, ColorF(1,0,0,1)); minAngle = MAX(newMin, minAngle); } ++currentc; } } minAngle *= gf_PI/180.0f; maxAngle *= gf_PI/180.0f; }
////////////////////////////////////////////////////////////////////////// // NOTE: This function must be thread-safe. Before adding stuff contact MarcoC. void CVehicleMovementTank::ProcessMovement(const float deltaTime) { FUNCTION_PROFILER( gEnv->pSystem, PROFILE_GAME ); m_netActionSync.UpdateObject(this); CryAutoCriticalSection lk(m_lock); CVehicleMovementBase::ProcessMovement(deltaTime); if (!(m_actorId && m_isEnginePowered)) { IPhysicalEntity* pPhysics = GetPhysics(); if (m_latFriction != 1.3f) SetLatFriction(1.3f); if (m_axleFriction != m_axleFrictionMax) UpdateAxleFriction(0.f, false, deltaTime); m_action.bHandBrake = 1; m_action.pedal = 0; m_action.steer = 0; pPhysics->Action(&m_action, 1); return; } IPhysicalEntity* pPhysics = GetPhysics(); MARK_UNUSED m_action.clutch; Matrix34 worldTM( m_PhysPos.q ); worldTM.AddTranslation( m_PhysPos.pos ); const Matrix34 invWTM = worldTM.GetInvertedFast(); Vec3 localVel = invWTM.TransformVector(m_PhysDyn.v); Vec3 localW = invWTM.TransformVector(m_PhysDyn.w); float speed = m_PhysDyn.v.len(); float speedRatio = min(1.f, speed/m_maxSpeed); float actionPedal = abs(m_movementAction.power) > 0.001f ? m_movementAction.power : 0.f; // tank specific: // avoid steering input around 0.5 (ask Anton) float actionSteer = m_movementAction.rotateYaw; float absSteer = abs(actionSteer); float steerSpeed = (absSteer < 0.01f && abs(m_currSteer) > 0.01f) ? m_steerSpeedRelax : m_steerSpeed; if (steerSpeed == 0.f) { m_currSteer = (float)sgn(actionSteer); } else { if (m_movementAction.isAI) { m_currSteer = actionSteer; } else { m_currSteer += min(abs(actionSteer-m_currSteer), deltaTime*steerSpeed) * sgn(actionSteer-m_currSteer); } } Limit(m_currSteer, -m_steerLimit, m_steerLimit); if (abs(m_currSteer) > 0.0001f) { // if steering, apply full throttle to have enough turn power actionPedal = (float)sgn(actionPedal); if (actionPedal == 0.f) { // allow steering-on-teh-spot only above maxReverseSpeed (to avoid sudden reverse of controls) const float maxReverseSpeed = -1.5f; actionPedal = max(0.f, min(1.f, 1.f-(localVel.y/maxReverseSpeed))); // todo float steerLim = 0.8f; Limit(m_currSteer, -steerLim*m_steerLimit, steerLim*m_steerLimit); } } if (!pPhysics->GetStatus(&m_vehicleStatus)) return; int currGear = m_vehicleStatus.iCurGear - 1; // indexing for convenience: -1,0,1,2,.. UpdateAxleFriction(m_movementAction.power, true, deltaTime); UpdateSuspension(deltaTime); float absPedal = abs(actionPedal); // pedal ramping if (m_pedalSpeed == 0.f) m_currPedal = actionPedal; else { m_currPedal += deltaTime * m_pedalSpeed * sgn(actionPedal - m_currPedal); m_currPedal = clamp_tpl(m_currPedal, -absPedal, absPedal); } // only apply pedal after threshold is exceeded if (currGear == 0 && fabs_tpl(m_currPedal) < m_pedalThreshold) m_action.pedal = 0; else m_action.pedal = m_currPedal; // change pedal amount based on damages float damageMul = 0.0f; { if (m_movementAction.isAI) { damageMul = 1.0f - 0.30f * m_damage; m_action.pedal *= damageMul; } else { // request from Sten: damage shouldn't affect reversing so much. float effectiveDamage = m_damage; if(m_action.pedal < -0.1f) effectiveDamage = 0.4f * m_damage; m_action.pedal *= GetWheelCondition(); damageMul = 1.0f - 0.7f*effectiveDamage; m_action.pedal *= damageMul; } } // reverse steering value for backward driving float effSteer = m_currSteer * sgn(actionPedal); // update lateral friction float latSlipMinGoal = 0.f; float latFricMinGoal = m_latFricMin; if (abs(effSteer) > 0.01f && !m_movementAction.brake) { latSlipMinGoal = m_latSlipMin; // use steering friction, but not when countersteering if (sgn(effSteer) != sgn(localW.z)) latFricMinGoal = m_latFricMinSteer; } Interpolate(m_currentSlipMin, latSlipMinGoal, 3.f, deltaTime); if (latFricMinGoal < m_currentFricMin) m_currentFricMin = latFricMinGoal; else Interpolate(m_currentFricMin, latFricMinGoal, 3.f, deltaTime); float fractionSpeed = min(1.f, max(0.f, m_avgLateralSlip-m_currentSlipMin) / (m_latSlipMax-m_currentSlipMin)); float latFric = fractionSpeed * (m_latFricMax-m_currentFricMin) + m_currentFricMin; if ( m_movementAction.brake && m_movementAction.isAI ) { // it is natural for ai, apply differnt friction value while handbreaking latFric = m_latFricMax; } if (latFric != m_latFriction) { SetLatFriction(latFric); } const static float maxSteer = gf_PI/4.f; // fix maxsteer, shouldn't change m_action.steer = m_currSteer * maxSteer; if (m_steeringImpulseMin > 0.f && m_wheelContactsLeft != 0 && m_wheelContactsRight != 0) { const float maxW = 0.3f*gf_PI; float steer = abs(m_currSteer)>0.001f ? m_currSteer : 0.f; float desired = steer * maxW; float curr = -localW.z; float err = desired - curr; // err>0 means correction to right Limit(err, -maxW, maxW); if (abs(err) > 0.01f) { float amount = m_steeringImpulseMin + speedRatio*(m_steeringImpulseMax-m_steeringImpulseMin); // bigger correction for relaxing if (desired == 0.f || (desired*curr>0 && abs(desired)<abs(curr))) amount = m_steeringImpulseRelaxMin + speedRatio*(m_steeringImpulseRelaxMax-m_steeringImpulseRelaxMin); float corr = -err * amount * m_PhysDyn.mass * deltaTime; pe_action_impulse imp; imp.iApplyTime = 0; imp.angImpulse = worldTM.GetColumn2() * corr; pPhysics->Action(&imp, THREAD_SAFE); } } m_action.bHandBrake = (m_movementAction.brake) ? 1 : 0; if (currGear > 0 && m_vehicleStatus.iCurGear < m_currentGear) { // when shifted down, disengage clutch immediately to avoid power/speed dropdown m_action.clutch = 1.f; } pPhysics->Action(&m_action, 1); if (Boosting()) ApplyBoost(speed, 1.2f*m_maxSpeed*GetWheelCondition()*damageMul, m_boostStrength, deltaTime); if (m_wheelContacts <= 1 && speed > 5.f) { ApplyAirDamp(DEG2RAD(20.f), DEG2RAD(10.f), deltaTime, THREAD_SAFE); UpdateGravity(-9.81f * 1.4f); } if (m_netActionSync.PublishActions( CNetworkMovementStdWheeled(this) )) CHANGED_NETWORK_STATE(m_pVehicle, eEA_GameClientDynamic ); }
bool operator() (const SSpectacularKillAnimation& killAnim) const { const SSpectacularKillCVars& skCVars = g_pGameCVars->g_spectacularKill; const CActor* pOwner = m_spectacularKill.m_pOwner; // 0. the anim shouldn't be redundant if (((gEnv->pTimer->GetFrameStartTime().GetSeconds() - s_lastKillInfo.timeStamp) <= skCVars.minTimeBetweenSameKills) && (killAnim.killerAnimation.compare(s_lastKillInfo.killerAnim))) { SK_DEBUG_LOG("GetValidAnim - %s is not valid: This animation was last played %.1f ago, a minimum time of %.1f is required", killAnim.killerAnimation.c_str(), (gEnv->pTimer->GetFrameStartTime().GetSeconds() - s_lastKillInfo.timeStamp), skCVars.minTimeBetweenSameKills); return true; } // 1. the killer needs to be within a certain distance from the target IEntity* pTargetEntity = m_pTarget->GetEntity(); IEntity* pKillerEntity = pOwner->GetEntity(); const QuatT& killerTransform = pOwner->GetAnimatedCharacter()->GetAnimLocation(); const QuatT& targetTransform = m_pTarget->GetAnimatedCharacter()->GetAnimLocation(); const Vec3& vKillerPos = killerTransform.t; const Vec3& vTargetPos = targetTransform.t; Vec2 vKillerToTarget = Vec2(vTargetPos) - Vec2(vKillerPos); float distance = vKillerToTarget.GetLength(); const float optimalDistance = killAnim.optimalDist; if ((optimalDistance > 0.0f) && (fabs_tpl(distance - optimalDistance) > skCVars.maxDistanceError)) { #ifndef _RELEASE if (g_pGameCVars->g_spectacularKill.debug > 1) { // visually shows why it failed IPersistantDebug* pPersistantDebug = m_spectacularKill.BeginPersistantDebug(); const float fConeHeight = killAnim.optimalDist + skCVars.maxDistanceError; pPersistantDebug->AddPlanarDisc(vTargetPos, killAnim.optimalDist - skCVars.maxDistanceError, killAnim.optimalDist + skCVars.maxDistanceError, Col_Coral, 6.0f); pPersistantDebug->AddLine(vKillerPos, vKillerPos + Vec3(0.f, 0.f, 5.0f), Col_Red, 6.f); } SK_DEBUG_LOG("GetValidAnim - %s is not valid: Distance between actors should be %.2f, is %.2f (max error is %f)", killAnim.killerAnimation.c_str(), optimalDistance, distance, skCVars.maxDistanceError); #endif return true; } // 2. The killer needs to be facing the target within cosLookToConeHalfAngleRadians angle Vec2 vKillerDir(killerTransform.GetColumn1()); // In decoupled catchup mode we need the animated character's orientation vKillerDir.Normalize(); if (vKillerToTarget.GetNormalizedSafe().Dot(vKillerDir) <= skCVars.minKillerToTargetDotProduct) { SK_DEBUG_LOG("GetValidAnim - %s is not valid: Killer is not looking within %.2f degrees towards the target", killAnim.killerAnimation.c_str(), RAD2DEG(acos_tpl(skCVars.minKillerToTargetDotProduct) * 2.0f)); return true; } // 3. If specified, the killer needs to be within a certain angle range from a given reference orientation from the target // e.g. Specifying referenceAngle 180 means using the back of the target as the center of the angle range // (imagine it as a cone) where the killer has to be for the kill to be valid if (killAnim.targetToKillerAngle >= 0.f) { const float referenceAngle = killAnim.targetToKillerAngle; // Find the reference vector which will be the center of the allowed angle range Vec2 vTargetDir(targetTransform.GetColumn1()); vTargetDir.Normalize(); // 2D rotation Vec2 vReferenceDir((vTargetDir.x * cosf(referenceAngle)) - (vTargetDir.y * sinf(referenceAngle)), (vTargetDir.y * cosf(referenceAngle)) + (vTargetDir.x * sinf(referenceAngle))); if (vKillerToTarget.GetNormalizedSafe().Dot(-vReferenceDir) <= killAnim.targetToKillerMinDot) { #ifndef _RELEASE if (g_pGameCVars->g_spectacularKill.debug > 1) { // visually shows why it failed IPersistantDebug* pPersistantDebug = m_spectacularKill.BeginPersistantDebug(); const float fConeHeight = killAnim.optimalDist + skCVars.maxDistanceError; pPersistantDebug->AddCone(vTargetPos + (vReferenceDir * fConeHeight), -vReferenceDir, killAnim.targetToKillerMinDot * fConeHeight * 2.0f, fConeHeight, Col_Coral, 6.f); pPersistantDebug->AddLine(vKillerPos, vKillerPos + Vec3(0.f, 0.f, 5.0f), Col_Red, 6.f); } float targetToKillerDot = vTargetDir.GetNormalizedSafe().Dot(-vKillerToTarget); SK_DEBUG_LOG("GetValidAnim - %s is not valid: Killer is not within a %.2f degrees cone centered on the target's %.2f degrees. Killer is at %.2f angles respect the target", killAnim.killerAnimation.c_str(), RAD2DEG(acos_tpl(killAnim.targetToKillerMinDot) * 2.0f), RAD2DEG(killAnim.targetToKillerAngle), RAD2DEG(acos_tpl(targetToKillerDot))); #endif return true; } } SK_DEBUG_LOG("GetValidAnim - %s is valid", killAnim.killerAnimation.c_str()); return false; }
void CMountedGunController::Update(EntityId mountedGunID, float frameTime) { CRY_ASSERT_MESSAGE(m_pControlledPlayer, "Controlled player not initialized"); CItem* pMountedGun = static_cast<CItem*>(gEnv->pGame->GetIGameFramework()->GetIItemSystem()->GetItem(mountedGunID)); bool canUpdateMountedGun = (pMountedGun != NULL) && (pMountedGun->GetStats().mounted); if (canUpdateMountedGun) { IMovementController * pMovementController = m_pControlledPlayer->GetMovementController(); assert(pMovementController); SMovementState info; pMovementController->GetMovementState(info); IEntity* pMountedGunEntity = pMountedGun->GetEntity(); const Matrix34& lastMountedGunWorldTM = pMountedGunEntity->GetWorldTM(); Vec3 desiredAimDirection = info.aimDirection.GetNormalized(); // AI can switch directions too fast, prevent snapping if(!m_pControlledPlayer->IsPlayer()) { const Vec3 currentDir = lastMountedGunWorldTM.GetColumn1(); const float dot = clamp(currentDir.Dot(desiredAimDirection), -1.0f, 1.0f); const float reqAngle = cry_acosf(dot); const float maxRotSpeed = 2.0f; const float maxAngle = frameTime * maxRotSpeed; if(fabs(reqAngle) > maxAngle) { const Vec3 axis = currentDir.Cross(desiredAimDirection); if(axis.GetLengthSquared() > 0.001f) // current dir and new dir are enough different { desiredAimDirection = currentDir.GetRotated(axis.GetNormalized(),sgn(reqAngle)*maxAngle); } } } bool isUserClient = m_pControlledPlayer->IsClient(); IEntity* pMountedGunParentEntity = pMountedGunEntity->GetParent(); IVehicle *pVehicle = NULL; if(pMountedGunParentEntity && m_pControlledPlayer) pVehicle = m_pControlledPlayer->GetLinkedVehicle(); CRecordingSystem* pRecordingSystem = g_pGame->GetRecordingSystem(); //For client update always, for others only when there is notable change if (!pVehicle && (isUserClient || (!desiredAimDirection.IsEquivalent(lastMountedGunWorldTM.GetColumn1(), 0.003f)))) { Quat rotation = Quat::CreateRotationVDir(desiredAimDirection, 0.0f); pMountedGunEntity->SetRotation(rotation); if (isUserClient && pRecordingSystem) { // Only record the gun position if you're using the gun. pRecordingSystem->OnMountedGunRotate(pMountedGunEntity, rotation); } } const Vec3 vInitialAimDirection = GetMountDirection(pMountedGun, pMountedGunParentEntity); assert( vInitialAimDirection.IsUnit() ); //Adjust gunner position and animations UpdateGunnerLocation(pMountedGun, pMountedGunParentEntity, vInitialAimDirection); const float aimrad = Ang3::CreateRadZ(Vec2(vInitialAimDirection),Vec2(-desiredAimDirection)); const float pitchLimit = sin_tpl(DEG2RAD(30.0f)); const float animHeight = fabs_tpl(clamp(desiredAimDirection.z * (float)__fres(pitchLimit), -1.0f, 1.0f)); const float aimUp = (float)__fsel(-desiredAimDirection.z, 0.0f, animHeight); const float aimDown = (float)__fsel(desiredAimDirection.z, 0.0f, animHeight); if (pRecordingSystem) { pRecordingSystem->OnMountedGunUpdate(m_pControlledPlayer, aimrad, aimUp, aimDown); } if(!m_pControlledPlayer->IsThirdPerson()) { UpdateFirstPersonAnimations(pMountedGun, desiredAimDirection); } if(m_pMovementAction) { const float aimUpParam = aimUp; const float aimDownParam = aimDown; const float aimMovementParam = CalculateAnimationTime(aimrad); m_pMovementAction->SetParam(MountedGunCRCs.aimUpParam, aimUpParam); m_pMovementAction->SetParam(MountedGunCRCs.aimDownParam, aimDownParam); m_pMovementAction->SetParam(MountedGunCRCs.aimMovementParam, aimMovementParam); } UpdateIKMounted(pMountedGun); } }
void CAnimatedGrabHandler::UpdatePosVelRot(float frameTime) { IEntity *pGrab = gEnv->pEntitySystem->GetEntity(m_grabStats.grabId); if ( !pGrab) return; IEntity *pEnt = m_pActor->GetEntity(); if (m_grabStats.grabDelay<0.001f) { Vec3 grabWPos (GetGrabBoneWorldTM ().t); // NOTE Aug 3, 2007: <pvl> the second part of this test means don't enable // the correction if animation/ik wasn't used for grabbing in the first place if (m_grabStats.readIkInaccuracyCorrection && m_grabStats.grabAnimGraphSignal[0]) { // NOTE Aug 2, 2007: <pvl> executed the first time this function is called // for a particular grabbing action m_grabStats.ikInaccuracyCorrection = grabWPos - (pGrab->GetWorldTM().GetTranslation() + m_grabStats.entityGrabSpot); m_grabStats.readIkInaccuracyCorrection = false; // FIXME Sep 13, 2007: <pvl> only putting it here because it's called just // once, at the instant when the object is grabbed - rename readIkInaccuracyCorrection // to make this clearer, or put this somewhere else DisableGrabbedAnimatedCharacter (true); } else { // NOTE Aug 2, 2007: <pvl> phase it out gradually m_grabStats.ikInaccuracyCorrection *= 0.9f; if (m_grabStats.ikInaccuracyCorrection.len2 () < 0.01f) m_grabStats.ikInaccuracyCorrection = Vec3 (0.0f, 0.0f, 0.0f); } // NOTE Sep 13, 2007: <pvl> this should prevent us from calling SetWPos() // later so that the IK "release" phase can take over m_grabStats.IKActive = false; Matrix34 tm(pGrab->GetWorldTM()); tm.SetTranslation(grabWPos - (m_grabStats.ikInaccuracyCorrection + pGrab->GetRotation() * m_grabStats.entityGrabSpot)); pGrab->SetWorldTM(tm,ENTITY_XFORM_USER); } //update IK for (int i=0;i<m_grabStats.limbNum;++i) { SIKLimb *pLimb = m_pActor->GetIKLimb(m_grabStats.limbId[i]); // NOTE Dez 14, 2006: <pvl> this class is always supposed to have // m_grabStats.usingAnimation == true if (m_grabStats.usingAnimation && m_grabStats.releaseIKTime>0.001f && m_grabStats.IKActive) { // NOTE Dez 15, 2006: <pvl> use IK to constantly offset the // animation so that the difference between where the animation // expects the object to be and where the object really is is taken // into account. Vec3 animPos = pEnt->GetSlotWorldTM(0) * pLimb->lAnimPos; Vec3 assumedGrabPos = pEnt->GetSlotWorldTM(0) * m_grabStats.grabbedObjOfs; Vec3 actualGrabPos = pGrab->GetWorldPos() + m_grabStats.entityGrabSpot; Vec3 adjustment = actualGrabPos - assumedGrabPos; pLimb->SetWPos(pEnt,animPos + adjustment,ZERO,0.5f,2.0f,1000); //gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere(pGrab->GetWorldPos() + m_grabStats.entityGrabSpot, 0.5f, ColorB(0,255,0,100)); } //if there are multiple limbs, only the first one sets the rotation of the object. if (m_grabStats.useIKRotation && i == 0 && m_grabStats.grabDelay<0.001f) { // NOTE Aug 8, 2007: <pvl> the idea here is to store current world // rotations of both the object being grabbed and the end bone of // a grabbing limb. Then track how the end bone rotates with respect // to the stored original rotation and rotate the grabbed object // the same way. That way, the grabbed object rotates the same as // the limb and appears to be "stabbed" by it. QuatT endBoneWorldRot = GetGrabBoneWorldTM (); endBoneWorldRot.q.Normalize(); // may not be necessary - just to be safe if ( ! m_grabStats.origRotationsValid) { m_grabStats.origRotation = pGrab->GetRotation(); m_grabStats.origRotation.Normalize(); // may not be necessary - just to be safe m_grabStats.origEndBoneWorldRot = endBoneWorldRot; m_grabStats.origRotationsValid = true; } Quat grabQuat( (endBoneWorldRot*m_grabStats.origEndBoneWorldRot.GetInverted()).q * m_grabStats.origRotation); grabQuat.Normalize(); // NOTE Dez 14, 2006: <pvl> this code sets up and look vectors for the grabbed // entity in case it's an Actor (the player, mostly) so that the player always // looks roughly at the grabber. The grabber is supposed to be the Hunter here // so this code is somewhat Hunter-specific. // UPDATE Aug 7, 2007: <pvl> do the above for the player only // UPDATE Sep 13, 2007: <pvl> don't do it for anybody ATM, it doesn't seem useful CActor *pGrabbedActor = (CActor *)g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(m_grabStats.grabId); if (false && pGrabbedActor && pGrabbedActor->IsClient() && pGrabbedActor->GetActorStats()) { Vec3 upVec(Quat(endBoneWorldRot.q * m_grabStats.additionalRotation).GetColumn2()); upVec.z = fabs_tpl(upVec.z) * 2.0f; upVec.NormalizeSafe(Vec3(0,0,1)); SActorStats *pAS = pGrabbedActor->GetActorStats(); if (pAS) { pAS->forceUpVector = upVec; pAS->forceLookVector = (pEnt->GetSlotWorldTM(0) * m_pActor->GetLocalEyePos(0)) - pGrabbedActor->GetEntity()->GetWorldPos(); float lookLen(pAS->forceLookVector.len()); pAS->forceLookVector *= (1.0f/lookLen)*0.33f; //pAS->forceLookVector = -Quat(boneRot * m_grabStats.additionalRotation).GetColumn1();//boneRot.GetColumn2(); } } else { pGrab->SetRotation(grabQuat,ENTITY_XFORM_USER); } } } if (m_grabStats.grabDelay<0.001f) { // NOTE Sep 16, 2007: <pvl> now that grabbed entity rotation coming from // a grabbing bone (if any) is computed, bone-space offset can be applied Matrix34 tm(pGrab->GetWorldTM()); tm.AddTranslation(GetGrabBoneWorldTM().q * m_grabStats.boneGrabOffset); pGrab->SetWorldTM(tm,ENTITY_XFORM_USER); /* { // debug draw for the grab bone QuatT grabBoneWorldTM = GetGrabBoneWorldTM(); Vec3 start = grabBoneWorldTM.t; Vec3 end = start + grabBoneWorldTM.q * Vec3 (1,0,0) * 3; gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (255,0,0), end, ColorB (0,0,255), 6.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere (start, 0.5f, ColorB (255,128,0)); } */ /* { // draw complete coord systems for both the end bone and the grabbed thing QuatT grabBoneWorldTM = GetGrabBoneWorldTM(); Vec3 start = grabBoneWorldTM.t; Vec3 end = start + grabBoneWorldTM.q * Vec3 (1,0,0) * 3; gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (128,0,0), end, ColorB (128,0,0), 6.0f); end = start + grabBoneWorldTM.q * Vec3 (0,1,0) * 3; gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (0,128,0), end, ColorB (0,128,0), 6.0f); end = start + grabBoneWorldTM.q * Vec3 (0,0,1) * 3; gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (0,0,128), end, ColorB (0,0,128), 6.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere (start, 0.2f, ColorB (255,255,255)); start = pGrab->GetWorldTM().GetTranslation(); end = start + pGrab->GetRotation() * Vec3 (1,0,0) * 3; gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (128,0,0), end, ColorB (128,0,0), 6.0f); end = start + pGrab->GetRotation() * Vec3 (0,1,0) * 3; gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (0,128,0), end, ColorB (0,128,0), 6.0f); end = start + pGrab->GetRotation() * Vec3 (0,0,1) * 3; gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (0,0,128), end, ColorB (0,0,128), 6.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere (start, 0.2f, ColorB (64,64,64)); } */ } /* { // debug draw for the grabbed object Vec3 start = pGrab->GetWorldTM().GetTranslation(); Vec3 end = start + pGrab->GetRotation() * Vec3 (0,0,1) * 3; gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine (start, ColorB (255,0,0), end, ColorB (0,0,255), 6.0f); gEnv->pRenderer->GetIRenderAuxGeom()->DrawSphere (start, 0.2f, ColorB (255,128,0)); } */ }
bool CPlayerStateSwim::OnPrePhysicsUpdate( CPlayer& player, const SActorFrameMovementParams& movement, float frameTime ) { const CPlayerStateSwim_WaterTestProxy& waterProxy = player.m_playerStateSwim_WaterTestProxy; CPlayerStateUtil::PhySetFly( player ); const SPlayerStats& stats = player.m_stats; #ifdef STATE_DEBUG const bool debug = (g_pGameCVars->cl_debugSwimming != 0); #endif const Vec3 entityPos = player.GetEntity()->GetWorldPos(); const Quat baseQuat = player.GetBaseQuat(); const Vec3 vRight(baseQuat.GetColumn0()); Vec3 velocity = player.GetActorPhysics().velocity; // Underwater timer, sounds update and surface wave speed. if (waterProxy.IsHeadUnderWater()) { m_headUnderWaterTimer += frameTime; if (m_headUnderWaterTimer <= -0.0f && !m_onSurface ) { player.PlaySound(CPlayer::ESound_DiveIn, true, "speed", velocity.len()); m_headUnderWaterTimer = 0.0f; } player.PlaySound(CPlayer::ESound_Underwater, true); } else { m_headUnderWaterTimer -= frameTime; if (m_headUnderWaterTimer >= 0.0f && (waterProxy.IsHeadComingOutOfWater() || m_onSurface)) { player.PlaySound(CPlayer::ESound_DiveOut, true, "speed", velocity.len()); m_headUnderWaterTimer = 0.0f; } player.PlaySound(CPlayer::ESound_Underwater, false); } m_headUnderWaterTimer = clamp_tpl( m_headUnderWaterTimer, -0.2f, 0.2f ); // Apply water flow velocity to the player Vec3 gravity; pe_params_buoyancy buoyancy[s_maxWaterVolumesToConsider]; if (int count = gEnv->pPhysicalWorld->CheckAreas(entityPos, gravity, &buoyancy[0], s_maxWaterVolumesToConsider)) { for(int i = 0; i < count && i < s_maxWaterVolumesToConsider; ++i) { // 0 is water if( buoyancy[i].iMedium == 0 ) { velocity += (buoyancy[i].waterFlow * frameTime); } } } // Calculate desired acceleration (user input) Vec3 desiredWorldVelocity(ZERO); Vec3 acceleration(ZERO); { Vec3 desiredLocalNormalizedVelocity(ZERO); Vec3 desiredLocalVelocity(ZERO); const Quat viewQuat = player.GetViewQuat(); const float backwardMultiplier = (float)__fsel(movement.desiredVelocity.y, 1.0f, g_pGameCVars->pl_swimBackSpeedMul); desiredLocalNormalizedVelocity.x = movement.desiredVelocity.x * g_pGameCVars->pl_swimSideSpeedMul; desiredLocalNormalizedVelocity.y = movement.desiredVelocity.y * backwardMultiplier; float sprintMultiplier = 1.0f; if ((player.IsSprinting()) && !player.IsCinematicFlagActive(SPlayerStats::eCinematicFlag_RestrictMovement)) { sprintMultiplier = GetSwimParams().m_swimSprintSpeedMul; // Higher speed multiplier when sprinting while looking up const float upFraction = clamp_tpl(viewQuat.GetFwdZ(), 0.0f, 1.0f); sprintMultiplier *= LERP(1.0f, GetSwimParams().m_swimUpSprintSpeedMul, upFraction); } const float baseSpeed = player.GetStanceMaxSpeed(STANCE_SWIM); desiredLocalVelocity.x = desiredLocalNormalizedVelocity.x * sprintMultiplier * baseSpeed; desiredLocalVelocity.y = desiredLocalNormalizedVelocity.y * sprintMultiplier * baseSpeed; desiredLocalVelocity.z = desiredLocalNormalizedVelocity.z * g_pGameCVars->pl_swimVertSpeedMul * baseSpeed; // The desired movement is applied in view-space, not in entity-space, since entity does not necessarily pitch while swimming. desiredWorldVelocity += viewQuat.GetColumn0() * desiredLocalVelocity.x; desiredWorldVelocity += viewQuat.GetColumn1() * desiredLocalVelocity.y; // though, apply up/down in world space. desiredWorldVelocity.z += desiredLocalVelocity.z; #ifdef STATE_DEBUG if (debug) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.8f), 1.5f, "BaseSpeed %1.3f", baseSpeed); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,1.0f), 1.5f, "SprintMul %1.2f", sprintMultiplier); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.6f), 1.5f, "MoveN[%1.3f, %1.3f, %1.3f]", desiredLocalNormalizedVelocity.x, desiredLocalNormalizedVelocity.y, desiredLocalNormalizedVelocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.5f), 1.5f, "VeloL[%1.3f, %1.3f, %1.3f]", desiredLocalVelocity.x, desiredLocalVelocity.y, desiredLocalVelocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.4f), 1.5f, "VeloW[%1.3f, %1.3f, %1.3f]", desiredWorldVelocity.x, desiredWorldVelocity.y, desiredWorldVelocity.z); } #endif //Remove a bit of control when entering the water const float userControlFraction = (float)__fsel(0.3f - waterProxy.GetSwimmingTimer(), 0.2f, 1.0f); acceleration += desiredWorldVelocity * userControlFraction; } // Apply acceleration (frame-rate independent) const float accelerateDelayInv = 3.333f; velocity += acceleration * (frameTime * accelerateDelayInv); #ifdef STATE_DEBUG if( debug ) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.2f), 1.5f, " Axx[%1.3f, %1.3f, %1.3f]", acceleration.x, acceleration.y, acceleration.z); } #endif //-------------------- const float relativeWaterLevel = waterProxy.GetRelativeWaterLevel() + 0.1f; const float surfaceDistanceFraction = clamp_tpl(fabsf(relativeWaterLevel), 0.0f, 1.0f); float surfaceProximityInfluence = 1.0f - surfaceDistanceFraction; const float verticalVelocityFraction = clamp_tpl((fabsf(desiredWorldVelocity.z) - 0.3f) * 2.5f, 0.0f, 1.0f); surfaceProximityInfluence = surfaceProximityInfluence * (1.0f - verticalVelocityFraction); // Apply velocity dampening (frame-rate independent) Vec3 damping(ZERO); const float zSpeedPreDamping = velocity.z; { damping.x = fabsf(velocity.x); damping.y = fabsf(velocity.y); // Vertical damping is special, to allow jumping out of water with higher speed, // and also not sink too deep when falling down ito the water after jump or such. float zDamp = 1.0f + (6.0f * clamp_tpl((-velocity.z - 1.0f) * 0.333f, 0.0f, 1.0f)); zDamp *= 1.0f - surfaceProximityInfluence; damping.z = fabsf(velocity.z) * zDamp; const float stopDelayInv = 3.333f; damping *= (frameTime * stopDelayInv); velocity.x = (float)__fsel((fabsf(velocity.x) - damping.x), (velocity.x - fsgnf(velocity.x) * damping.x), 0.0f); velocity.y = (float)__fsel((fabsf(velocity.y) - damping.y), (velocity.y - fsgnf(velocity.y) * damping.y), 0.0f); velocity.z = (float)__fsel((fabsf(velocity.z) - damping.z), (velocity.z - fsgnf(velocity.z) * damping.z), 0.0f); //Make sure you can not swim above the surface if ((relativeWaterLevel >= 0.0f) && (velocity.z > 0.0f)) { velocity.z = 0.0f; } } // Decide if we're on the surface and therefore need to be kept there.. if( relativeWaterLevel > -0.2f && relativeWaterLevel < 1.0f && fabs_tpl( zSpeedPreDamping ) < 0.5f ) { if( !waterProxy.IsHeadUnderWater() ) { m_onSurface = true; } } else { // we only leave the surface if the player moves, otherwise we try and keep the // player on the surface, even if they currently arent m_onSurface = false; } // Calculate and apply surface movement to the player. float speedDelta = 0.0f; if( m_onSurface ) { const float newWaterLevel = waterProxy.GetWaterLevel(); const float waterLevelDelta = clamp_tpl(newWaterLevel - m_lastWaterLevel, -1.0f, 1.0f ); const float newCheckedTime = waterProxy.GetWaterLevelTimeUpdated(); const float timeDelta = newCheckedTime - m_lastWaterLevelTime; if( timeDelta > FLT_EPSILON ) { speedDelta = waterLevelDelta/timeDelta; velocity.z += speedDelta; } m_lastWaterLevel = newWaterLevel; m_lastWaterLevelTime = newCheckedTime; } // Set request type and velocity player.GetMoveRequest().type = eCMT_Fly; player.GetMoveRequest().velocity = velocity; #ifdef STATE_DEBUG // DEBUG VELOCITY if (debug) { gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.0f), 1.5f, "Velo[%1.3f, %1.3f, %1.3f]", velocity.x, velocity.y, velocity.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.4f), 1.5f, "Damp[%1.3f, %1.3f, %1.3f]", damping.x, damping.y, damping.z); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f - Vec3(0,0,0.6f), 1.5f, "FrameTime %1.4f", frameTime); gEnv->pRenderer->DrawLabel(entityPos - vRight * 1.5f + Vec3(0,0,0.3f), 1.5f, "DeltaSpeed[%1.3f]", speedDelta ); //if (bNewSwimJumping) //gEnv->pRenderer->DrawLabel(entityPos - vRight * 0.15f + Vec3(0,0,0.6f), 2.0f, "JUMP"); } #endif return true; }
//------------------------------------------------------------------------ void CScriptControlledPhysics::OnPostStep(EventPhysPostStep *pPostStep) { pe_action_set_velocity av; const bool moving = m_moving; const bool rotating = m_rotating; const float deltaTime = pPostStep->dt; if (m_moving) { const Vec3 current = pPostStep->pos; const Vec3 target = m_moveTarget; const Vec3 delta = target - current; const float distanceSq = delta.len2(); if (distanceSq <= sqr(0.025f) || (delta.dot(m_lastVelocity) < 0.0f)) { m_speed = 0.0f; m_moving = false; m_lastVelocity.zero(); pPostStep->pos = target; av.v = ZERO; } else { float velocity = m_speed; float acceleration = m_acceleration; Vec3 direction = delta; const float distanceToEnd = direction.NormalizeSafe(); // Accelerate velocity = std::min(velocity + acceleration * deltaTime, m_maxSpeed); // Calculate acceleration and time needed to stop const float accelerationNeededToStop = (-(velocity*velocity) / distanceToEnd) / 2; if (fabsf(accelerationNeededToStop) > 0.0f) { const float timeNeededToStop = sqrtf((distanceToEnd / 0.5f) / fabsf(accelerationNeededToStop)); if (timeNeededToStop < m_stopTime) { acceleration = accelerationNeededToStop; } } // Prevent overshooting if ((velocity * deltaTime) > distanceToEnd) { const float multiplier = distanceToEnd / fabsf(velocity * deltaTime); velocity *= multiplier; acceleration *= multiplier; } m_acceleration = acceleration; m_speed = velocity; m_lastVelocity = direction * velocity; av.v = direction * velocity; } } if (m_rotating) { Quat current=pPostStep->q; Quat target=m_rotationTarget; Quat rotation=target*!current; float angle=acos_tpl(CLAMP(rotation.w, -1.0f, 1.0f))*2.0f; float original=angle; if (angle>gf_PI) angle=angle-gf_PI2; else if (angle<-gf_PI) angle=angle+gf_PI2; if (fabs_tpl(angle)<0.01f) { m_rotationSpeed=0.0f; m_rotating=false; pPostStep->q=m_rotationTarget; av.w=ZERO; } else { float a=m_rotationSpeed/m_rotationStopTime; float d=m_rotationSpeed*m_stopTime-0.5f*a*m_rotationStopTime*m_rotationStopTime; if (fabs_tpl(angle)<d+0.001f) m_rotationAcceleration=(angle-m_rotationSpeed*m_rotationStopTime)/(m_rotationStopTime*m_rotationStopTime); m_rotationSpeed=m_rotationSpeed+sgn(angle)*m_rotationAcceleration*deltaTime; if (fabs_tpl(m_rotationSpeed*deltaTime)>fabs_tpl(angle)) m_rotationSpeed=angle/deltaTime; else if (fabs_tpl(m_rotationSpeed)<0.001f) m_rotationSpeed=sgn(m_rotationSpeed)*0.001f; else if (fabs_tpl(m_rotationSpeed)>=m_rotationMaxSpeed) { m_rotationSpeed=sgn(m_rotationSpeed)*m_rotationMaxSpeed; m_rotationAcceleration=0.0f; } } if(fabs_tpl(angle)>=0.001f) av.w=(rotation.v/sin_tpl(original*0.5f)).normalized(); av.w*=m_rotationSpeed; } if (moving || rotating) { if (IPhysicalEntity *pPE=GetEntity()->GetPhysics()) pPE->Action(&av, 1); } /* if ((moving && !m_moving) || (rotating && !m_rotating)) GetEntity()->SetWorldTM(Matrix34::Create(GetEntity()->GetScale(), pPostStep->q, pPostStep->pos)); */ }
float GetDuration(int iA, int iB) { CTimeValue dt = m_stickHistoryTime[iB] - m_stickHistoryTime[iA]; return fabs_tpl(dt.GetSeconds()); }
void CTimeOfDayScheduler::Update() { static const float MIN_DT = 1.0f / 100.0f; float curTime = gEnv->p3DEngine->GetTimeOfDay()->GetTime(); float lastTime = m_lastTime; const float dt = curTime-lastTime; // only evaluate if at least some time passed if (m_bForceUpdate==false && fabs_tpl(dt) < MIN_DT) return; m_bForceUpdate = false; // execute all entries between lastTime and curTime // if curTime < lastTime, we wrapped around and have to process two intervals // [lastTime, 24.0] and [0, curTime] TEntries processingEntries; // no need to make member var, as allocation/execution currently is NOT too often SEntry entryForTime(0,lastTime,0,0); TEntries::iterator iter = std::lower_bound(m_entries.begin(), m_entries.end(), entryForTime); TEntries::iterator iterEnd = m_entries.end(); const bool bWrap = lastTime > curTime; const float maxTime = bWrap ? 24.0f : curTime; //CryLogAlways("CTOD: lastTime=%f curTime=%f", lastTime, curTime); // process interval [lastTime, curTime] or in case of wrap [lastTime, 24.0] while (iter != iterEnd) { const SEntry& entry = *iter; assert (entry.time >= lastTime); if (entry.time > maxTime) break; // CryLogAlways("Adding: %d time=%f", entry.id, entry.time); processingEntries.push_back(entry); ++iter; } if (bWrap) // process interval [0, curTime] { iter = m_entries.begin(); while (iter != iterEnd) { const SEntry& entry = *iter; if (entry.time > curTime) break; // CryLogAlways("Adding[wrap]: %d time=%f", entry.id, entry.time); processingEntries.push_back(entry); ++iter; } } iter = processingEntries.begin(); iterEnd = processingEntries.end(); while (iter != iterEnd) { const SEntry& entry = *iter; entry.callback(entry.id, entry.pUserData, curTime); ++iter; } m_lastTime = curTime; }