//---------------------------------------------------------------------- void CVehicleMountedWeapon::PerformRipOff(CActor* pOwner) { IActionMapManager* pMapManager = gEnv->pGame->GetIGameFramework()->GetIActionMapManager(); assert(pMapManager); pMapManager->EnableActionMap("vehicle_general", false); IVehicle *pVehicle = gEnv->pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(m_vehicleId); if(pVehicle) { SVehicleEventParams params; params.entityId = GetEntityId(); pVehicle->BroadcastVehicleEvent(eVE_WeaponRemoved, params); if(pOwner) { if(gEnv->bMultiplayer) { const IEntity* pVehicleEnt = pVehicle->GetEntity(); IEntity* pEntity = pOwner->GetEntity(); const Matrix34& vehWMat = pVehicleEnt->GetWorldTM(); m_previousWSpaceOffsetPosition = pEntity->GetWorldPos(); m_localRipUserOffset = vehWMat.GetInverted().TransformPoint(m_previousWSpaceOffsetPosition); } pOwner->LinkToVehicle(0); } m_previousVehicleRotation = pVehicle->GetEntity()->GetWorldRotation(); } CHeavyMountedWeapon::PerformRipOff(pOwner); }
// message from server received on clients void CVTOLVehicleManager::OnSingleEntityRMI(CGameRules::SModuleRMIEntityParams params) { switch(params.m_data) { case eRMITypeSingleEntity_vtol_destroyed: { CryLog("CVTOLVehicleManager::OnSingleEntityRMI() eRMITypeSingleEntity_vtol_destroyed"); IVehicle* pVehicle = m_pVehicleSystem->GetVehicle( params.m_entityId ); CRY_ASSERT_MESSAGE(pVehicle, "have received destroyed VTOL RMI, but cannot find the vehicle for specified entity id"); if (pVehicle) { DestroyVTOL(pVehicle->GetEntity(), m_vtolList[params.m_entityId]); } break; } case eRMITypeSingleEntity_vtol_hidden: // for late joining clients only { CryLog("CVTOLVehicleManager::OnSingleEntityRMI() eRMITypeSingleEntity_vtol_hidden"); IVehicle* pVehicle = m_pVehicleSystem->GetVehicle(params.m_entityId); CRY_ASSERT_MESSAGE(pVehicle, "have received hidden VTOL RMI, but cannot find the vehicle for specified entity id"); if (pVehicle) { //Hide existing vehicle IEntity* pVehicleEntity = pVehicle->GetEntity(); pVehicleEntity->SetPos(Vec3(0.f,0.f,0.f)); // TODO - get Gary's fix for this if any pVehicleEntity->Hide(true); SVTOLInfo& info = m_vtolList[params.m_entityId]; info.state = EVS_Invisible; info.stateTime = 0.f; // this may allow clients to do their own respawn handling, stopping the need for respawned RMI below SHUDEvent hudEvent(eHUDEvent_RemoveEntity); hudEvent.AddData((int)params.m_entityId); CHUDEventDispatcher::CallEvent(hudEvent); } break; } case eRMITypeSingleEntity_vtol_respawned: { CryLog("CVTOLVehicleManager::OnSingleEntityRMI() eRMITypeSingleEntity_vtol_respawned"); IVehicle* pVehicle = m_pVehicleSystem->GetVehicle(params.m_entityId); CRY_ASSERT_MESSAGE(pVehicle, "have received respawned VTOL RMI, but cannot find the vehicle for specified entity id"); if (pVehicle) { RespawnVTOL(pVehicle, m_vtolList[params.m_entityId]); } break; } default: CRY_ASSERT_MESSAGE(0, string().Format("unhandled RMI data %d", params.m_data)); break; } }
//------------------------------------------------------------------------ float CGameRulesCommonDamageHandling::GetVehicleForeignCollisionMultiplier( const IVehicle& vehicle, const SCollisionEntityInfo& colliderInfo, const CGameRules::SCollisionHitInfo& colHitInfo ) const { float result = 1.0f; //Vehicle to vehicle collision if (colliderInfo.pEntityVehicle) { const float vehicleMass = vehicle.GetMass(); const float vehicleColliderMass = colliderInfo.pEntityVehicle->GetMass(); const float targetSpeedSqr = colHitInfo.target_velocity.len2(); if ((vehicleMass > vehicleColliderMass * 1.5f) && (targetSpeedSqr > 0.01f)) { //Reduce damage for collisions with large mass ratios, to avoid instant-killing const float ratio = 1.0f + (0.35f * min(10.0f, vehicleMass * __fres(vehicleColliderMass))) * min(1.0f, targetSpeedSqr * 0.31623f); result = __fres(ratio); if (DebugCollisions()) { CryLog("Vehicle/Vehicle (%s <- %s), collision mult: %.2f", vehicle.GetEntity()->GetName(), colliderInfo.pEntity->GetName(), result); } } } return result; }
//------------------------------------------------------------------------ void CVehicleMountedWeapon::CorrectRipperEntityPosition(float timeStep) { IVehicle *pVehicle = gEnv->pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(m_vehicleId); if(pVehicle) { const IEntity* pVehicleEnt = pVehicle->GetEntity(); Vec3 posDiff(ZERO); IActor* pOwner = GetOwnerActor(); if (pOwner && pOwner->IsPlayer()) { CPlayer* pPlayer = static_cast<CPlayer*>(pOwner); const Matrix34& wMat = pVehicleEnt->GetWorldTM(); Vec3 vehiclePos = wMat.GetTranslation(); Vec3 currWSpaceRipUserOffset = wMat.TransformPoint(m_localRipUserOffset); posDiff = currWSpaceRipUserOffset - m_previousWSpaceOffsetPosition; // Don't want to overwrite anyone else changes with an absolute 'set' pOwner->GetEntity()->SetPos(pOwner->GetEntity()->GetWorldPos() + posDiff); m_previousWSpaceOffsetPosition = currWSpaceRipUserOffset; //Update view limit direction based on change in vehicle rotation if(pPlayer->IsClient()) { SViewLimitParams &viewLimits = pPlayer->GetActorParams().viewLimits; if(viewLimits.GetViewLimitRangeH()) //Don't do this unless we are currently horizontally constrained { Quat vehicleRotation(wMat); Quat rotationChange = vehicleRotation * m_previousVehicleRotation.GetInverted(); Vec3 viewLimitDir = rotationChange * viewLimits.GetViewLimitDir(); viewLimitDir.z = 0.f; viewLimitDir.Normalize(); viewLimits.SetViewLimit(viewLimitDir, 0.01f, 0.01f, 0.f, 0.f, SViewLimitParams::eVLS_Item); m_previousVehicleRotation = vehicleRotation; } //Reset the pitch/roll view angles over time Quat viewDirFinal = pPlayer->GetViewQuatFinal(); Ang3 viewAngles(viewDirFinal); float xAdjustment = (float)__fsel(viewAngles.x, max(-viewAngles.x, -0.5f * timeStep), min(-viewAngles.x, 0.5f * timeStep)); float yAdjustment = (float)__fsel(viewAngles.y, max(-viewAngles.y, -0.5f * timeStep), min(-viewAngles.y, 0.5f * timeStep)); if(xAdjustment || yAdjustment) { pPlayer->AddViewAngles(Ang3(xAdjustment, yAdjustment, 0.f)); } } } } }
//------------------------------------------------------------------------ int CScriptBind_Vehicle::IsInsideRadius(IFunctionHandler *pH, Vec3 pos, float radius) { IVehicle* vehicle = GetVehicle(pH); if (!vehicle) return pH->EndFunction(); AABB boundingBox; IEntity* vehicleEntity = vehicle->GetEntity(); vehicleEntity->GetWorldBounds(boundingBox); Sphere sphere(pos, radius); return pH->EndFunction(Overlap::Sphere_AABB(sphere, boundingBox)); }
void CVehicleWeaponPulseC::Update(SEntityUpdateContext& ctx, int update) { if(!m_vehicleId && GetEntity()->GetParent()) { m_vehicleId = GetEntity()->GetParent()->GetId(); CRY_ASSERT(gEnv->pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(m_vehicleId) && "Using VehicleWeapons on non-vehicles may lead to unexpected behavior."); } IVehicle* pVehicle = GetVehicle(); if(pVehicle) { IVehiclePart* pPart = pVehicle->GetWeaponParentPart(GetEntityId()); if(pPart) { const Matrix34& partWorldTM = pPart->GetWorldTM(); const Vec3 partDirection = partWorldTM.GetColumn1(); const Vec3 partPosition = partWorldTM.GetTranslation(); //ColorB col(255, 0, 0); //gEnv->pRenderer->GetIRenderAuxGeom()->DrawLine(partPosition, col, partPosition + 100.0f * partDirection, col, 2.0f); //ok, ok, i'll optimise this later Matrix34 mat = pVehicle->GetEntity()->GetWorldTM(); Matrix33 matO(mat); matO.Invert(); const Vec3 diff = (m_TargetPos - partPosition).GetNormalized(); m_destination.SetLerp(partDirection, diff, ctx.fFrameTime); Quat quat1; //quat1.SetRotationVDir(diff, 0.0f); quat1.SetRotationVDir(m_destination.GetNormalized(), 0.0f); Matrix33 mat2(quat1); mat2 = matO * mat2; m_destination = m_destination * 10000.0f + partPosition; m_targetPosition = m_destination; m_aimPosition = m_destination; pPart->SetLocalTM(Matrix34(mat2, pPart->GetLocalTM(true, true).GetTranslation())); } } Base::Update(ctx, update); }
void CVTOLVehicleManager::Update(float frameTime) { // Update logic goes here if(CGameRules* pGameRules = g_pGame->GetGameRules()) { CGameRules::TPlayers players; pGameRules->GetPlayers(players); for (TVTOLList::iterator iter=m_vtolList.begin(), end=m_vtolList.end(); iter!=end; ++iter) { SVTOLInfo& info = iter->second; IVehicle* pVehicle = m_pVehicleSystem->GetVehicle( info.entityId ); if(pVehicle) { info.stateTime += frameTime; // Find difference in rotation Quat newRotation = pVehicle->GetEntity()->GetWorldRotation(); newRotation.Normalize(); Quat rotationDifference = newRotation*info.location.q.GetInverted(); rotationDifference.Normalize(); // Store new rotation + position info.location.q = newRotation; info.location.t = pVehicle->GetEntity()->GetWorldPos(); if(info.state==EVS_Normal) { pVehicle->NeedsUpdate(IVehicle::eVUF_AwakePhysics); if(CVehicleMovementMPVTOL* pMovement = static_cast<CVehicleMovementMPVTOL*>(pVehicle->GetMovement())) { const uint8 pathComplete = pMovement->GetPathComplete(); if(pathComplete==1) { if(CMPPathFollowingManager* pMPPathFollowingManager = g_pGame->GetGameRules()->GetMPPathFollowingManager()) { pMPPathFollowingManager->NotifyListenersOfPathCompletion(info.entityId); } pMovement->SetPathCompleteNotified(); } } } // Check status if(info.state==EVS_Normal && pVehicle->GetDamageRatio() >= 1.f) { if(gEnv->bServer) { DestructionDamageRatioReached(pVehicle, info, frameTime); } } else if(info.state == EVS_Invisible) { if(info.stateTime > g_pGameCVars->g_VTOLRespawnTime) { RespawnVTOL(pVehicle, info); } } //Process players TPlayerList& currentPlayerList = info.playersInside; TPlayerList oldPlayerList = currentPlayerList; currentPlayerList.clear(); CGameRules::TPlayers::iterator iterPlayer = players.begin(); const CGameRules::TPlayers::const_iterator endPlayer = players.end(); while(iterPlayer != endPlayer) { // Adding safeguard to protect against cases where user alt-f4 quits program. UpdateEntityInVTOL(info, *iterPlayer); ++iterPlayer; } //Find out who has been inside since the last update, who has just entered, and who has left TPlayerStatusList playerStatusList; int currentId; for(unsigned int prev = 0; prev < currentPlayerList.size(); ++prev) { currentId = currentPlayerList[prev]; bool found = false; TPlayerList::iterator oldIter = oldPlayerList.begin(); TPlayerList::iterator oldEnd = oldPlayerList.end(); while(oldIter != oldEnd) { if(currentId == *oldIter) //In both lists so still inside { found = true; playerStatusList.push_back( TPlayerStatus(currentId, E_PEVS_StillInside) ); oldPlayerList.erase(oldIter); break; } ++oldIter; } if(!found) //Only in current list so entered { playerStatusList.push_back( TPlayerStatus(currentId, E_PEVS_Entered) ); } } //Those remaining in old list have exited for(unsigned int old = 0; old < oldPlayerList.size(); ++old) { playerStatusList.push_back( TPlayerStatus(oldPlayerList[old], E_PEVS_Exited) ); } //Act based on current state TPlayerStatusList::iterator statusIter = playerStatusList.begin(); TPlayerStatusList::iterator statusEnd = playerStatusList.end(); while(statusIter != statusEnd) { switch(statusIter->second) { case E_PEVS_Entered: { OnPlayerEntered(statusIter->first); } break; case E_PEVS_Exited: { OnPlayerExited(statusIter->first); } break; } ++statusIter; } UpdateRotationOfInternalPlayers(info, playerStatusList, rotationDifference); } } } }
void CVehicleWeaponControlled::Update(SEntityUpdateContext& ctx, int update) { IVehicle *pVehicle = m_vehicleId ? gEnv->pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(m_vehicleId) : NULL; if (!m_vehicleId && GetEntity()->GetParent()) { IEntity *entity = GetEntity(); if (entity) { IEntity *parent = entity->GetParent(); if (parent) { m_vehicleId = parent->GetId(); pVehicle = gEnv->pGame->GetIGameFramework()->GetIVehicleSystem()->GetVehicle(parent->GetId()); } } } if (pVehicle) { IVehiclePart *pPart = pVehicle->GetWeaponParentPart(GetEntityId()); if(pPart) { if(IVehiclePart *pParentPart = pPart->GetParent()) { CRY_ASSERT(pVehicle->GetEntity()); if(ICharacterInstance *characterInst = pVehicle->GetEntity()->GetCharacter(pParentPart->GetSlot())) { if(ISkeletonPose* pose = characterInst->GetISkeletonPose()) { IDefaultSkeleton& rIDefaultSkeleton = characterInst->GetIDefaultSkeleton(); int16 joint = rIDefaultSkeleton.GetJointIDByName(pPart->GetName()); const QuatT &jQuat = pose->GetAbsJointByID(joint); Matrix34 localT(jQuat); localT.SetTranslation(jQuat.t/* - Vec3(0.0f, 0.75f, 0.0f)*/); Matrix34 vehicleWorldTm = pVehicle->GetEntity()->GetWorldTM(); Matrix34 mat = vehicleWorldTm * localT; Vec3 vehicleSide2 = pPart->GetParent()->GetLocalTM(true, true).GetTranslation(); CPlayer *pl = this->GetOwnerPlayer(); Matrix33 mat2; if (!m_destination.IsEquivalent(ZERO)) { Vec3 diff = GetDestination() - mat.GetTranslation(); //pPart->GetWorldTM().GetTranslation(); diff.Normalize(); Matrix33 loc(mat); loc.Invert(); Vec3 diffLocal = loc.TransformVector(diff); Matrix33 desMat; desMat.SetRotationVDir(diffLocal, 0.0f); Vec3 test = GetEntity()->GetLocalTM().GetColumn0(); Ang3 testTM(desMat); float za = testTM.x - m_Angles.x; za = (za < 0.0f) ? -gf_PI : gf_PI; za *= 0.05f * ctx.fFrameTime; m_Angles.x += za; Limit(m_Angles.x, -gf_PI * 0.33f, gf_PI * 0.33f); if (testTM.z > m_Angles.z + 0.05f) { m_Angles.z += gf_PI * factor1 * ctx.fFrameTime; } else if (testTM.z < m_Angles.z - 0.05f) { m_Angles.z -= gf_PI * factor1 * ctx.fFrameTime; } else { m_Angles.z = testTM.z; } Limit(m_Angles.z, -gf_PI * 0.33f, gf_PI * 0.33f); mat2.SetRotationXYZ(m_Angles); } else { if (!m_FireBlocked) { m_Angles.x = m_Angles.x - ctx.fFrameTime * factor2 * m_Angles.x; m_Angles.z = m_Angles.z - ctx.fFrameTime * factor2 * m_Angles.z; } mat2.SetRotationXYZ(m_Angles); } mat = mat * mat2; GetEntity()->SetWorldTM(mat); if (pl) { Matrix34 worldGunMat = vehicleWorldTm * localT; if (!pl->IsDead()) { Vec3 trans = worldGunMat.GetTranslation() - worldGunMat.GetColumn2() * 0.7f; worldGunMat.SetTranslation(trans); pl->GetEntity()->SetWorldTM(worldGunMat); float dot = mat.GetColumn1().dot(worldGunMat.GetColumn0()); Update3PAnim(pl, 0.5f - dot * 0.5f, ctx.fFrameTime, mat); } else { ICharacterInstance* pCharacter = pl->GetEntity()->GetCharacter(0); int boneId = pCharacter ? pCharacter->GetIDefaultSkeleton().GetJointIDByName("Spine03") : 7; pl->LinkToMountedWeapon(0); if (IVehicleSeat* seat = pVehicle->GetSeatForPassenger(pl->GetEntityId())) { seat->Exit(false, true); } Matrix33 rot(worldGunMat); Vec3 offset(0.0f, 0.0f, 0.70f); Vec3 transformedOff = rot.TransformVector(offset); Vec3 trans = worldGunMat.GetTranslation(); trans -= transformedOff; worldGunMat.SetTranslation(trans); pl->GetEntity()->SetWorldTM(worldGunMat); pl->GetEntity()->SetPos(worldGunMat.GetTranslation()); //worldGunMat.GetTranslation()); pl->RagDollize(true); if (boneId > -1) { IPhysicalEntity *physEnt = pl->GetEntity()->GetPhysics(); if (physEnt) { pe_simulation_params simulationParams; physEnt->GetParams(&simulationParams); pe_params_pos pos; pos.pos = GetEntity()->GetPos(); physEnt->SetParams(&pos); pe_action_impulse impulse; impulse.ipart = boneId; impulse.angImpulse = Vec3(0.0f, 0.0f, 1.0f); impulse.impulse = worldGunMat.GetColumn1() * -1.5f * simulationParams.mass; physEnt->Action(&impulse); } } StopUse(GetOwnerId()); SetOwnerId(0); StopFire(); m_FireBlocked = true; } // IsDead } // pl } // pose } // characterInst } // pParentPart } // pPart } // pVehicle Base::Update(ctx, update); RequireUpdate(eIUS_General); }
void CPlayerView::ViewSpectatorTarget(SViewParams &viewParams) { CActor* pTarget = (CActor*)g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(m_in.stats_spectatorTarget); if(!pTarget) return; IVehicle* pVehicle = pTarget->GetLinkedVehicle(); static float defaultOffset = 0.3f; static float viewHeight = 1.8f; Matrix34 worldTM = pTarget->GetEntity()->GetWorldTM(); Vec3 worldPos = worldTM.GetTranslation(); if(!pVehicle) { const SStanceInfo* stanceInfo = pTarget->GetStanceInfo(pTarget->GetStance()); if(stanceInfo) { Interpolate(viewHeight, stanceInfo->viewOffset.z, 5.0f, viewParams.frameTime); worldPos.z += viewHeight + defaultOffset; } else { worldPos.z += 1.8f; } } else { // use vehicle pos/ori worldTM = pVehicle->GetEntity()->GetWorldTM(); worldPos = pVehicle->GetEntity()->GetWorldPos(); worldPos.z += 1.5f; } Ang3 worldAngles = Ang3::GetAnglesXYZ(Matrix33(worldTM)); float distance = 3; // if freelook allowed, get orientation and distance from player entity if(g_pGameCVars->g_spectate_FixedOrientation == 0) { CPlayer* pThisPlayer = static_cast<CPlayer*>(g_pGame->GetIGameFramework()->GetIActorSystem()->GetActor(m_in.entityId)); if(!pThisPlayer) return; Matrix34 ownOrientation = pThisPlayer->GetEntity()->GetWorldTM(); worldAngles += Ang3::GetAnglesXYZ(Matrix33(ownOrientation)); distance = pThisPlayer->GetSpectatorZoom(); } if(pVehicle) { distance *= 4.0f; // air vehicles need bigger distance if(pVehicle->GetMovement() && pVehicle->GetMovement()->GetMovementType() == IVehicleMovement::eVMT_Air) distance *= 2.0f; } Vec3 goal; goal.x = distance * cos(worldAngles.z + gf_PI*1.5f) + worldPos.x; goal.y = distance * sin(worldAngles.z - gf_PI/2.0f) + worldPos.y; AABB targetBounds; pTarget->GetEntity()->GetLocalBounds(targetBounds); goal.z = targetBounds.max.z; float offset = defaultOffset; if(pVehicle) { if(pVehicle->GetMovement() && pVehicle->GetMovement()->GetMovementType() == IVehicleMovement::eVMT_Air) offset = 3.0f; else offset = 1.0f; } goal.z += pTarget->GetEntity()->GetWorldPos().z + offset; // store / interpolate the offset, not the world pos (reduces percieved jitter in vehicles) static Vec3 viewOffset(goal-worldPos); static Vec3 camPos(goal); static Vec3 entPos(worldPos); static EntityId lastSpectatorTarget(m_in.stats_spectatorTarget); // do a ray cast to check for camera intersection static ray_hit hit; IPhysicalEntity* pSkipEntities[10]; int nSkip = 0; if(pVehicle) { // vehicle drivers don't seem to have current items, so need to add the vehicle itself here nSkip = pVehicle->GetSkipEntities(pSkipEntities, 10); } else { IItem* pItem = pTarget->GetCurrentItem(); if (pItem) { CWeapon* pWeapon = (CWeapon*)pItem->GetIWeapon(); if (pWeapon) nSkip = CSingle::GetSkipEntities(pWeapon, pSkipEntities, 10); } } static float minDist = 0.4f; // how close we're allowed to get to the target static float wallSafeDistance = 0.3f; // how far to keep camera from walls Vec3 dir = goal - worldPos; primitives::sphere sphere; sphere.center = worldPos; sphere.r = wallSafeDistance; geom_contact *pContact = 0; float hitDist = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, dir, ent_static|ent_terrain|ent_rigid|ent_sleeping_rigid, &pContact, 0, (geom_colltype_player<<rwi_colltype_bit) | rwi_stop_at_pierceable, 0, 0, 0, pSkipEntities, nSkip); // even when we have contact, keep the camera the same height above the target float minHeightDiff = dir.z; if(hitDist > 0 && pContact) { goal = worldPos + (hitDist * dir.GetNormalizedSafe()); if(goal.z - worldPos.z < minHeightDiff) { // can't move the camera far enough away from the player in this direction. Try moving it directly up a bit int numHits = 0; sphere.center = goal; // (move back just slightly to avoid colliding with the wall we've already found...) sphere.center -= dir.GetNormalizedSafe() * 0.05f; float newHitDist = gEnv->pPhysicalWorld->PrimitiveWorldIntersection(sphere.type, &sphere, Vec3(0,0,minHeightDiff), ent_static|ent_terrain|ent_rigid|ent_sleeping_rigid, &pContact, 0, (geom_colltype_player<<rwi_colltype_bit) | rwi_stop_at_pierceable, 0, 0, 0, pSkipEntities, nSkip); float raiseDist = minHeightDiff - (goal.z - worldPos.z) - wallSafeDistance; if(newHitDist != 0) { raiseDist = MIN(minHeightDiff, newHitDist); } raiseDist = MAX(0.0f, raiseDist); goal.z += raiseDist; worldPos.z += raiseDist*0.8f; } } int thisFrameId = gEnv->pRenderer->GetFrameID(); static int frameNo(thisFrameId); if(thisFrameId - frameNo > 5) { // reset positions viewOffset = goal - worldPos; entPos = worldPos; camPos = goal; } if(lastSpectatorTarget != m_in.stats_spectatorTarget) { viewOffset = goal - worldPos; entPos = worldPos; camPos = goal; lastSpectatorTarget = m_in.stats_spectatorTarget; } frameNo = thisFrameId; static float interpSpeed = 5.0f; static float interpSpeed2 = 5.0f; static float interpSpeed3 = 8.0f; if(pVehicle) { Interpolate(viewOffset, goal-worldPos, interpSpeed, viewParams.frameTime); entPos = worldPos; viewParams.position = worldPos + viewOffset; camPos = viewParams.position; } else { Vec3 camPosChange = goal - camPos; Vec3 entPosChange = worldPos - entPos; if(camPosChange.GetLengthSquared() > 100.0f) camPos = goal; if(entPosChange.GetLengthSquared() > 100.0f) entPos = worldPos; Interpolate(camPos, goal, interpSpeed2, viewParams.frameTime); Interpolate(entPos, worldPos, interpSpeed3, viewParams.frameTime); viewParams.position = camPos; } Matrix33 rotation = Matrix33::CreateRotationVDir((entPos - viewParams.position).GetNormalizedSafe()); viewParams.rotation = GetQuatFromMat33(rotation); m_io.bUsePivot = true; m_io.stats_bobCycle = 0.0; }