Пример #1
0
//----------------------------------------------------------------------
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);
}
Пример #2
0
// 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;
}
Пример #4
0
//------------------------------------------------------------------------
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));
}
Пример #6
0
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);
}
Пример #7
0
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);
			}
		}
	}
}
Пример #8
0
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);
}
Пример #9
0
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;
}