#include "common.h" #include "patcher.h" #include "Draw.h" #include "World.h" #include "Vehicle.h" #include "Ped.h" #include "Pad.h" #include "General.h" #include "CullZones.h" #include "SurfaceTable.h" #include "MBlur.h" #include "Camera.h" const float DefaultFOV = 80.0f; // actually 70.0f CCamera &TheCamera = *(CCamera*)0x6FACF8; bool &CCamera::m_bUseMouse3rdPerson = *(bool *)0x5F03D8; WRAPPER void CCamera::DrawBordersForWideScreen(void) { EAXJMP(0x46B430); } bool CCamera::IsSphereVisible(const CVector ¢er, float radius, const CMatrix *mat) { RwV3d c; c = *(RwV3d*)¢er; RwV3dTransformPoints(&c, &c, 1, &mat->m_matrix); if(c.y + radius < CDraw::GetNearClipZ()) return false; if(c.y - radius > CDraw::GetFarClipZ()) return false; if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > radius) return false; if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > radius) return false; if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > radius) return false; if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > radius) return false; return true; } bool CCamera::IsPointVisible(const CVector ¢er, const CMatrix *mat) { RwV3d c; c = *(RwV3d*)¢er; RwV3dTransformPoints(&c, &c, 1, &mat->m_matrix); if(c.y < CDraw::GetNearClipZ()) return false; if(c.y > CDraw::GetFarClipZ()) return false; if(c.x*m_vecFrustumNormals[0].x + c.y*m_vecFrustumNormals[0].y > 0.0f) return false; if(c.x*m_vecFrustumNormals[1].x + c.y*m_vecFrustumNormals[1].y > 0.0f) return false; if(c.y*m_vecFrustumNormals[2].y + c.z*m_vecFrustumNormals[2].z > 0.0f) return false; if(c.y*m_vecFrustumNormals[3].y + c.z*m_vecFrustumNormals[3].z > 0.0f) return false; return true; } bool CCamera::IsBoxVisible(RwV3d *box, const CMatrix *mat) { int i; int frustumTests[6] = { 0 }; RwV3dTransformPoints(box, box, 8, &mat->m_matrix); for(i = 0; i < 8; i++){ if(box[i].y < CDraw::GetNearClipZ()) frustumTests[0]++; if(box[i].y > CDraw::GetFarClipZ()) frustumTests[1]++; if(box[i].x*m_vecFrustumNormals[0].x + box[i].y*m_vecFrustumNormals[0].y > 0.0f) frustumTests[2]++; if(box[i].x*m_vecFrustumNormals[1].x + box[i].y*m_vecFrustumNormals[1].y > 0.0f) frustumTests[3]++; // Why not test z? // if(box[i].y*m_vecFrustumNormals[2].y + box[i].z*m_vecFrustumNormals[2].z > 0.0f) frustumTests[4]++; // if(box[i].y*m_vecFrustumNormals[3].y + box[i].z*m_vecFrustumNormals[3].z > 0.0f) frustumTests[5]++; } for(i = 0; i < 6; i++) if(frustumTests[i] == 8) return false; // Box is completely outside of one plane return true; } int CCamera::GetLookDirection(void) { if(Cams[ActiveCam].Mode == CCam::MODE_CAMONASTRING || Cams[ActiveCam].Mode == CCam::MODE_FIRSTPERSON || Cams[ActiveCam].Mode == CCam::MODE_BEHINDBOAT || Cams[ActiveCam].Mode == CCam::MODE_FOLLOWPED) return Cams[ActiveCam].DirectionWasLooking; return LOOKING_FORWARD;; } WRAPPER void CCamera::Fade(float timeout, int16 direction) { EAXJMP(0x46B3A0); } WRAPPER void CCamera::ProcessFade(void) { EAXJMP(0x46F080); } WRAPPER void CCamera::ProcessMusicFade(void) { EAXJMP(0x46F1E0); } int CCamera::GetScreenFadeStatus(void) { if(m_fFLOATingFade == 0.0f) return FADE_0; if(m_fFLOATingFade == 255.0f) return FADE_2; return FADE_1; } void CCamera::SetFadeColour(uint8 r, uint8 g, uint8 b) { m_FadeTargetIsSplashScreen = r == 0 && g == 0 && b == 0; CDraw::FadeRed = r; CDraw::FadeGreen = g; CDraw::FadeBlue = b; } void CCamera::SetMotionBlur(int r, int g, int b, int a, int type) { m_BlurRed = r; m_BlurGreen = g; m_BlurBlue = b; m_motionBlur = a; m_BlurType = type; } void CCamera::SetMotionBlurAlpha(int a) { m_imotionBlurAddAlpha = a; } void CCamera::RenderMotionBlur(void) { if(m_BlurType == 0) return; CMBlur::MotionBlurRender(m_pRwCamera, m_BlurRed, m_BlurGreen, m_BlurBlue, m_motionBlur, m_BlurType, m_imotionBlurAddAlpha); } /* * * CCam * */ // MaxSpeed is a limit of how fast the value is allowed to change. 1.0 = to Target in up to 1ms // Acceleration is how fast the speed will change to MaxSpeed. 1.0 = to MaxSpeed in 1ms void WellBufferMe(float Target, float *CurrentValue, float *CurrentSpeed, float MaxSpeed, float Acceleration, bool IsAngle) { float Delta = Target - *CurrentValue; if(IsAngle){ while(Delta >= PI) Delta -= 2*PI; while(Delta < -PI) Delta += 2*PI; } float TargetSpeed = Delta * MaxSpeed; // Add or subtract absolute depending on sign, genius! // if(TargetSpeed - *CurrentSpeed > 0.0f) // *CurrentSpeed += Acceleration * fabs(TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); // else // *CurrentSpeed -= Acceleration * fabs(TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); // this is simpler: *CurrentSpeed += Acceleration * (TargetSpeed - *CurrentSpeed) * CTimer::GetTimeStep(); // Clamp speed if we overshot if(TargetSpeed < 0.0f && *CurrentSpeed < TargetSpeed) *CurrentSpeed = TargetSpeed; else if(TargetSpeed > 0.0f && *CurrentSpeed > TargetSpeed) *CurrentSpeed = TargetSpeed; *CurrentValue += *CurrentSpeed * min(10.0f, CTimer::GetTimeStep()); } void CCam::GetVectorsReadyForRW(void) { CVector right; Up = CVector(0.0f, 0.0f, 1.0f); Front.Normalise(); if(Front.x == 0.0f && Front.y == 0.0f){ Front.x = 0.0001f; Front.y = 0.0001f; } right = CrossProduct(Front, Up); right.Normalise(); Up = CrossProduct(right, Front); } // This code is really bad. wtf R*? CVector CCam::DoAverageOnVector(const CVector &vec) { int i; CVector Average = { 0.0f, 0.0f, 0.0f }; if(ResetStatics){ m_iRunningVectorArrayPos = 0; m_iRunningVectorCounter = 1; } // TODO: make this work with NUMBER_OF_VECTORS_FOR_AVERAGE != 2 if(m_iRunningVectorCounter == 3){ m_arrPreviousVectors[0] = m_arrPreviousVectors[1]; m_arrPreviousVectors[1] = vec; }else m_arrPreviousVectors[m_iRunningVectorArrayPos] = vec; for(i = 0; i <= m_iRunningVectorArrayPos; i++) Average += m_arrPreviousVectors[i]; Average /= i; m_iRunningVectorArrayPos++; m_iRunningVectorCounter++; if(m_iRunningVectorArrayPos >= NUMBER_OF_VECTORS_FOR_AVERAGE) m_iRunningVectorArrayPos = NUMBER_OF_VECTORS_FOR_AVERAGE-1; if(m_iRunningVectorCounter > NUMBER_OF_VECTORS_FOR_AVERAGE+1) m_iRunningVectorCounter = NUMBER_OF_VECTORS_FOR_AVERAGE+1; return Average; } // Rotate Beta in direction opposite of BetaOffset in 5 deg. steps. // Return the first angle for which Beta + BetaOffset + Angle has a clear view. // i.e. BetaOffset is a safe zone so that Beta + Angle is really clear. // If BetaOffset == 0, try both directions. float CCam::GetPedBetaAngleForClearView(const CVector &Target, float Dist, float BetaOffset, bool checkBuildings, bool checkVehicles, bool checkPeds, bool checkObjects, bool checkDummies) { CColPoint point; CEntity *ent = nil; CVector ToSource; float a; // This would be so much nicer if we just got the step variable before the loop...R* for(a = 0.0f; a <= PI; a += DEGTORAD(5.0f)){ if(BetaOffset <= 0.0f){ ToSource = CVector(cos(Beta + BetaOffset + a), sin(Beta + BetaOffset + a), 0.0f)*Dist; if(!CWorld::ProcessLineOfSight(Target, Target + ToSource, point, ent, checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, true, true)) return a; } if(BetaOffset >= 0.0f){ ToSource = CVector(cos(Beta + BetaOffset - a), sin(Beta + BetaOffset - a), 0.0f)*Dist; if(!CWorld::ProcessLineOfSight(Target, Target + ToSource, point, ent, checkBuildings, checkVehicles, checkPeds, checkObjects, checkDummies, true, true)) return -a; } } return 0.0f; } static float DefaultAcceleration = 0.045f; static float DefaultMaxStep = 0.15f; void CCam::Process_FollowPed(const CVector &CameraTarget, float TargetOrientation, float, float) { const float GroundDist = 1.85f; CVector TargetCoors, Dist, IdealSource; float Length = 0.0f; float LateralLeft = 0.0f; float LateralRight = 0.0f; float Center = 0.0f; static bool PreviouslyObscured; static bool PickedASide; static float FixedTargetOrientation = 0.0f; float AngleToGoTo = 0.0f; float BetaOffsetAvoidBuildings = 0.45f; // ~25 deg float BetaOffsetGoingBehind = 0.45f; bool GoingBehind = false; bool Obscured = false; bool BuildingCheckObscured = false; bool HackPlayerOnStoppingTrain = false; static int TimeIndicatedWantedToGoDown = 0; static bool StartedCountingForGoDown = false; float DeltaBeta; m_bFixingBeta = false; bBelowMinDist = false; bBehindPlayerDesired = false; assert(CamTargetEntity->IsPed()); // CenterDist should be > LateralDist because we don't have an angle for safety in this case float CenterDist, LateralDist; float AngleToGoToSpeed; if(m_fCloseInPedHeightOffsetSpeed > 0.00001f){ LateralDist = 0.55f; CenterDist = 1.25f; BetaOffsetAvoidBuildings = 0.9f; // ~50 deg BetaOffsetGoingBehind = 0.9f; AngleToGoToSpeed = 0.88254666f; }else{ LateralDist = 0.8f; CenterDist = 1.35f; if(TheCamera.PedZoomIndicator == 1.0f || TheCamera.PedZoomIndicator == 4.0f){ LateralDist = 1.25f; CenterDist = 1.6f; } AngleToGoToSpeed = 0.43254671f; } FOV = DefaultFOV; if(ResetStatics){ Rotating = false; m_bCollisionChecksOn = true; FixedTargetOrientation = 0.0f; PreviouslyObscured = false; PickedASide = false; StartedCountingForGoDown = false; AngleToGoTo = 0.0f; // unused LastAngleWithNoPickedASide } TargetCoors = CameraTarget; IdealSource = Source; TargetCoors.z += m_fSyphonModeTargetZOffSet; CVector TempTargetCoors; TempTargetCoors = DoAverageOnVector(TargetCoors); TargetCoors = TempTargetCoors; // Add this unknown offset, but later it's removed again TargetCoors.z += m_fUnknownZOffSet; Dist.x = IdealSource.x - TargetCoors.x; Dist.y = IdealSource.y - TargetCoors.y; Length = Dist.Magnitude2D(); // Cam on a string. With a fixed distance. Zoom in/out is done later. if(Length != 0.0f) IdealSource = TargetCoors + CVector(Dist.x, Dist.y, 0.0f)/Length * GroundDist; else IdealSource = TargetCoors + CVector(1.0f, 1.0f, 0.0f); // TODO: what's transition beta? if(TheCamera.m_bUseTransitionBeta && ResetStatics){ CVector VecDistance; IdealSource.x = TargetCoors.x + GroundDist*cos(m_fTransitionBeta); IdealSource.y = TargetCoors.y + GroundDist*sin(m_fTransitionBeta); Beta = CGeneral::GetATanOfXY(IdealSource.x - TargetCoors.x, IdealSource.y - TargetCoors.y); }else Beta = CGeneral::GetATanOfXY(Source.x - TargetCoors.x, Source.y - TargetCoors.y); if(TheCamera.m_bCamDirectlyBehind){ m_bCollisionChecksOn = true; Beta = TargetOrientation + PI; } if(FindPlayerVehicle()) if(FindPlayerVehicle()->m_vehType == VEHICLE_TYPE_TRAIN) HackPlayerOnStoppingTrain = true; if(TheCamera.m_bCamDirectlyInFront){ m_bCollisionChecksOn = true; Beta = TargetOrientation; } while(Beta >= PI) Beta -= 2.0f * PI; while(Beta < -PI) Beta += 2.0f * PI; // BUG? is this ever used? // The values seem to be roughly m_fPedZoomValueSmooth + 1.85 if(ResetStatics){ if(TheCamera.PedZoomIndicator == 1.0) m_fRealGroundDist = 2.090556f; if(TheCamera.PedZoomIndicator == 2.0) m_fRealGroundDist = 3.34973f; if(TheCamera.PedZoomIndicator == 3.0) m_fRealGroundDist = 4.704914f; if(TheCamera.PedZoomIndicator == 4.0) m_fRealGroundDist = 2.090556f; } // And what is this? It's only used for collision and rotation it seems float RealGroundDist; if(TheCamera.PedZoomIndicator == 1.0) RealGroundDist = 2.090556f; if(TheCamera.PedZoomIndicator == 2.0) RealGroundDist = 3.34973f; if(TheCamera.PedZoomIndicator == 3.0) RealGroundDist = 4.704914f; if(TheCamera.PedZoomIndicator == 4.0) RealGroundDist = 2.090556f; if(m_fCloseInPedHeightOffset > 0.00001f) RealGroundDist = 1.7016; bool Shooting = false; CPed *ped = (CPed*)CamTargetEntity; if(ped->GetWeapon()->m_eWeaponType != WEAPONTYPE_UNARMED) if(CPad::GetPad(0)->GetWeapon()) Shooting = true; if(ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_DETONATOR || ped->GetWeapon()->m_eWeaponType == WEAPONTYPE_BASEBALLBAT) Shooting = false; if(m_fCloseInPedHeightOffset > 0.00001f) TargetCoors.z -= m_fUnknownZOffSet; // Figure out if and where we want to rotate if(CPad::GetPad(0)->ForceCameraBehindPlayer() || Shooting){ // Center cam behind player GoingBehind = true; m_bCollisionChecksOn = true; float OriginalBeta = Beta; // Set Beta behind player Beta = TargetOrientation + PI; TargetCoors.z -= 0.1f; AngleToGoTo = GetPedBetaAngleForClearView(TargetCoors, CenterDist * RealGroundDist, 0.0f, true, false, false, true, false); if(AngleToGoTo != 0.0f){ if(AngleToGoTo < 0.0f) AngleToGoTo -= AngleToGoToSpeed; else AngleToGoTo += AngleToGoToSpeed; }else{ float LateralLeft = GetPedBetaAngleForClearView(TargetCoors, LateralDist * RealGroundDist, BetaOffsetGoingBehind, true, false, false, true, false); float LateralRight = GetPedBetaAngleForClearView(TargetCoors, LateralDist * RealGroundDist, -BetaOffsetGoingBehind, true, false, false, true, false); if(LateralLeft == 0.0f && LateralRight != 0.0f) AngleToGoTo += LateralRight; else if(LateralLeft != 0.0f && LateralRight == 0.0f) AngleToGoTo += LateralLeft; } TargetCoors.z += 0.1f; Beta = OriginalBeta; if(PickedASide){ if(AngleToGoTo == 0.0f) FixedTargetOrientation = TargetOrientation + PI; Rotating = true; }else{ FixedTargetOrientation = TargetOrientation + PI + AngleToGoTo; Rotating = true; PickedASide = true; } }else{ // Rotate cam to avoid clipping into buildings TargetCoors.z -= 0.1f; Center = GetPedBetaAngleForClearView(TargetCoors, CenterDist * RealGroundDist, 0.0f, true, false, false, true, false); if(m_bCollisionChecksOn || PreviouslyObscured || Center != 0.0f || m_fCloseInPedHeightOffset > 0.00001f){ if(Center != 0.0f){ AngleToGoTo = Center; }else{ LateralLeft = GetPedBetaAngleForClearView(TargetCoors, LateralDist * RealGroundDist, BetaOffsetAvoidBuildings, true, false, false, true, false); LateralRight = GetPedBetaAngleForClearView(TargetCoors, LateralDist * RealGroundDist, -BetaOffsetAvoidBuildings, true, false, false, true, false); if(LateralLeft == 0.0f && LateralRight != 0.0f){ AngleToGoTo += LateralRight; if(m_fCloseInPedHeightOffset > 0.0f) RwCameraSetNearClipPlane(Scene.camera, 0.7f); }else if(LateralLeft != 0.0f && LateralRight == 0.0f){ AngleToGoTo += LateralLeft; if(m_fCloseInPedHeightOffset > 0.0f) RwCameraSetNearClipPlane(Scene.camera, 0.7f); } } if(LateralLeft != 0.0f || LateralRight != 0.0f || Center != 0.0f) BuildingCheckObscured = true; } TargetCoors.z += 0.1f; } if(m_fCloseInPedHeightOffset > 0.00001f) TargetCoors.z += m_fUnknownZOffSet; // Have to fix to avoid collision if(AngleToGoTo != 0.0f){ Obscured = true; Rotating = true; if(CPad::GetPad(0)->ForceCameraBehindPlayer() || Shooting){ if(!PickedASide) FixedTargetOrientation = Beta + AngleToGoTo; // can this even happen? }else FixedTargetOrientation = Beta + AngleToGoTo; // This calculation is only really used to figure out how fast to rotate out of collision m_fAmountFractionObscured = 1.0f; CVector PlayerPos = FindPlayerPed()->GetPosition(); float RotationDist = (AngleToGoTo == Center ? CenterDist : LateralDist) * RealGroundDist; // What's going on here? - AngleToGoTo? CVector RotatedSource = PlayerPos + CVector(cos(Beta - AngleToGoTo), sin(Beta - AngleToGoTo), 0.0f) * RotationDist; CColPoint colpoint; CEntity *entity; if(CWorld::ProcessLineOfSight(PlayerPos, RotatedSource, colpoint, entity, true, false, false, true, false, false, false)){ if((PlayerPos - RotatedSource).Magnitude() != 0.0f) m_fAmountFractionObscured = (PlayerPos - colpoint.point).Magnitude() / (PlayerPos - RotatedSource).Magnitude(); else m_fAmountFractionObscured = 1.0f; } } if(m_fAmountFractionObscured < 0.0f) m_fAmountFractionObscured = 0.0f; if(m_fAmountFractionObscured > 1.0f) m_fAmountFractionObscured = 1.0f; // Figure out speed values for Beta rotation float Acceleration, MaxSpeed; static float AccelerationMult = 0.35f; static float MaxSpeedMult = 0.85f; static float AccelerationMultClose = 0.7f; static float MaxSpeedMultClose = 1.6f; float BaseAcceleration = 0.025f; float BaseMaxSpeed = 0.09f; if(m_fCloseInPedHeightOffset > 0.00001f){ if(AngleToGoTo == 0.0f){ BaseAcceleration = 0.022f; BaseMaxSpeed = 0.04f; }else{ BaseAcceleration = DefaultAcceleration; BaseMaxSpeed = DefaultMaxStep; } } if(AngleToGoTo == 0.0f){ Acceleration = BaseAcceleration; MaxSpeed = BaseMaxSpeed; }else if(CPad::GetPad(0)->ForceCameraBehindPlayer() && !Shooting){ Acceleration = 0.051f; MaxSpeed = 0.18f; }else if(m_fCloseInPedHeightOffset > 0.00001f){ Acceleration = BaseAcceleration + AccelerationMultClose*sq(m_fAmountFractionObscured - 1.05f); MaxSpeed = BaseMaxSpeed + MaxSpeedMultClose*sq(m_fAmountFractionObscured - 1.05f); }else{ Acceleration = DefaultAcceleration + AccelerationMult*sq(m_fAmountFractionObscured - 1.05f); MaxSpeed = DefaultMaxStep + MaxSpeedMult*sq(m_fAmountFractionObscured - 1.05f); } static float AccelerationLimit = 0.3f; static float MaxSpeedLimit = 0.65f; if(Acceleration > AccelerationLimit) Acceleration = AccelerationLimit; if(MaxSpeed > MaxSpeedLimit) MaxSpeed = MaxSpeedLimit; int MoveState = ((CPed*)CamTargetEntity)->m_nMoveState; if(MoveState != PEDMOVE_NONE && MoveState != PEDMOVE_STILL && !CPad::GetPad(0)->ForceCameraBehindPlayer() && !Obscured && !Shooting){ Rotating = false; BetaSpeed = 0.0f; } // Now do the Beta rotation float Distance = (IdealSource - TargetCoors).Magnitude2D(); m_fDistanceBeforeChanges = Distance; if(Rotating){ m_bFixingBeta = true; while(FixedTargetOrientation >= PI) FixedTargetOrientation -= 2*PI; while(FixedTargetOrientation < -PI) FixedTargetOrientation += 2*PI; while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; /* // This is inlined WellBufferMe DeltaBeta = FixedTargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; float ReqSpeed = DeltaBeta * MaxSpeed; // Add or subtract absolute depending on sign, genius! if(ReqSpeed - BetaSpeed > 0.0f) BetaSpeed += SpeedStep * fabs(ReqSpeed - BetaSpeed) * CTimer::GetTimeStep(); else BetaSpeed -= SpeedStep * fabs(ReqSpeed - BetaSpeed) * CTimer::GetTimeStep(); // this would be simpler: // BetaSpeed += SpeedStep * (ReqSpeed - BetaSpeed) * CTimer::ms_fTimeStep; if(ReqSpeed < 0.0f && BetaSpeed < ReqSpeed) BetaSpeed = ReqSpeed; else if(ReqSpeed > 0.0f && BetaSpeed > ReqSpeed) BetaSpeed = ReqSpeed; Beta += BetaSpeed * min(10.0f, CTimer::GetTimeStep()); */ WellBufferMe(FixedTargetOrientation, &Beta, &BetaSpeed, MaxSpeed, Acceleration, true); if(ResetStatics){ Beta = FixedTargetOrientation; BetaSpeed = 0.0f; } Source.x = TargetCoors.x + Distance * cos(Beta); Source.y = TargetCoors.y + Distance * sin(Beta); // Check if we can stop rotating DeltaBeta = FixedTargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; if(fabs(DeltaBeta) < DEGTORAD(1.0f) && !bBehindPlayerDesired){ // Stop rotation PickedASide = false; Rotating = false; BetaSpeed = 0.0f; } } if(TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront || HackPlayerOnStoppingTrain || Rotating){ if(TheCamera.m_bCamDirectlyBehind){ Beta = TargetOrientation + PI; Source.x = TargetCoors.x + Distance * cos(Beta); Source.y = TargetCoors.y + Distance * sin(Beta); } if(TheCamera.m_bCamDirectlyInFront){ Beta = TargetOrientation; Source.x = TargetCoors.x + Distance * cos(Beta); Source.y = TargetCoors.y + Distance * sin(Beta); } if(HackPlayerOnStoppingTrain){ Beta = TargetOrientation + PI; Source.x = TargetCoors.x + Distance * cos(Beta); Source.y = TargetCoors.y + Distance * sin(Beta); m_fDimensionOfHighestNearCar = 0.0f; m_fCamBufferedHeight = 0.0f; m_fCamBufferedHeightSpeed = 0.0f; } // Beta and Source already set in the rotation code }else{ Source = IdealSource; BetaSpeed = 0.0f; } // Subtract m_fUnknownZOffSet from both? TargetCoors.z -= m_fUnknownZOffSet; Source.z = IdealSource.z - m_fUnknownZOffSet; // Apply zoom now // m_fPedZoomValueSmooth makes the cam go down the further out it is // 0.25 -> 0.20 for nearest dist // 1.50 -> -0.05 for mid dist // 2.90 -> -0.33 for far dist Source.z += (2.5f - TheCamera.m_fPedZoomValueSmooth)*0.2f - 0.25f; // Zoom out camera Front = TargetCoors - Source; Front.Normalise(); Source -= Front * TheCamera.m_fPedZoomValueSmooth; // and then we move up again // -0.375 // 0.25 // 0.95 Source.z += (TheCamera.m_fPedZoomValueSmooth - 1.0f)*0.5f + m_fCloseInPedHeightOffset; // Process height offset to avoid peds and cars float TargetZOffSet = m_fUnknownZOffSet + m_fDimensionOfHighestNearCar; TargetZOffSet = max(TargetZOffSet, m_fPedBetweenCameraHeightOffset); float TargetHeight = CameraTarget.z + TargetZOffSet - Source.z; if(TargetHeight > m_fCamBufferedHeight){ // Have to go up if(TargetZOffSet == m_fPedBetweenCameraHeightOffset && TargetZOffSet > m_fCamBufferedHeight) WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.04f, false); else if(TargetZOffSet == m_fUnknownZOffSet && TargetZOffSet > m_fCamBufferedHeight){ // TODO: figure this out bool foo = false; switch(((CPhysical*)CamTargetEntity)->m_nLastCollType) case 2: case 3: case 5: case 11: case 23: case 26: foo = true; if(foo) WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.4f, 0.05f, false); else WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.025f, false); }else WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.025f, false); StartedCountingForGoDown = false; }else{ // Have to go down if(StartedCountingForGoDown){ if(CTimer::GetTimeInMilliseconds() != TimeIndicatedWantedToGoDown){ if(TargetHeight > 0.0f) WellBufferMe(TargetHeight, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.01f, false); else WellBufferMe(0.0f, &m_fCamBufferedHeight, &m_fCamBufferedHeightSpeed, 0.2f, 0.01f, false); } }else{ StartedCountingForGoDown = true; TimeIndicatedWantedToGoDown = CTimer::GetTimeInMilliseconds(); } } Source.z += m_fCamBufferedHeight; // Clip Source if necessary bool ClipSource = m_fCloseInPedHeightOffset > 0.00001f && m_fCamBufferedHeight > 0.001f; if(GoingBehind || ResetStatics || ClipSource){ CColPoint colpoint; CEntity *entity; if(CWorld::ProcessLineOfSight(TargetCoors, Source, colpoint, entity, true, false, false, true, false, true, true)){ Source = colpoint.point; if((TargetCoors - Source).Magnitude2D() < 1.0f) RwCameraSetNearClipPlane(Scene.camera, 0.05f); } } TargetCoors.z += min(1.0f, m_fCamBufferedHeight/2.0f); m_cvecTargetCoorsForFudgeInter = TargetCoors; Front = TargetCoors - Source; m_fRealGroundDist = Front.Magnitude2D(); m_fMinDistAwayFromCamWhenInterPolating = m_fRealGroundDist; Front.Normalise(); GetVectorsReadyForRW(); TheCamera.m_bCamDirectlyBehind = false; TheCamera.m_bCamDirectlyInFront = false; PreviouslyObscured = BuildingCheckObscured; ResetStatics = false; } void CCam::Process_BehindCar(const CVector &CameraTarget, float TargetOrientation, float, float) { FOV = DefaultFOV; if(!CamTargetEntity->IsVehicle()) return; CVector TargetCoors = CameraTarget; TargetCoors.z -= 0.2f; CA_MAX_DISTANCE = 9.95f; CA_MIN_DISTANCE = 8.5f; CVector Dist = Source - TargetCoors; float Length = Dist.Magnitude2D(); m_fDistanceBeforeChanges = Length; if(Length < 0.002f) Length = 0.002f; Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y); if(Length > CA_MAX_DISTANCE){ Source.x = TargetCoors.x + Dist.x/Length * CA_MAX_DISTANCE; Source.y = TargetCoors.y + Dist.y/Length * CA_MAX_DISTANCE; }else if(Length < CA_MIN_DISTANCE){ Source.x = TargetCoors.x + Dist.x/Length * CA_MIN_DISTANCE; Source.y = TargetCoors.y + Dist.y/Length * CA_MIN_DISTANCE; } TargetCoors.z += 0.8f; WorkOutCamHeightWeeCar(TargetCoors, TargetOrientation); RotCamIfInFrontCar(TargetCoors, TargetOrientation); FixCamIfObscured(TargetCoors, 1.2f, TargetOrientation); Front = TargetCoors - Source; m_cvecTargetCoorsForFudgeInter = TargetCoors; ResetStatics = false; GetVectorsReadyForRW(); } void CCam::WorkOutCamHeightWeeCar(CVector &TargetCoors, float TargetOrientation) { CColPoint colpoint; CEntity *ent; float TargetZOffSet = 0.0f; static bool PreviouslyFailedRoadHeightCheck = false; static float RoadHeightFix = 0.0f; static float RoadHeightFixSpeed = 0.0f; if(ResetStatics){ RoadHeightFix = 0.0f; RoadHeightFixSpeed = 0.0f; Alpha = DEGTORAD(25.0f); AlphaSpeed = 0.0f; } float AlphaTarget = DEGTORAD(25.0f); if(CCullZones::CamNoRain() || CCullZones::PlayerNoRain()) AlphaTarget = DEGTORAD(14.0f); WellBufferMe(AlphaTarget, &Alpha, &AlphaSpeed, 0.1f, 0.05f, true); Source.z = TargetCoors.z + CA_MAX_DISTANCE*sin(Alpha); if(FindPlayerVehicle()){ m_fUnknownZOffSet = 0.0f; bool FoundRoad = false; bool FoundRoof = false; float RoadZ = 0.0f; float RoofZ = 0.0f; if(CWorld::ProcessVerticalLine(Source, -1000.0f, colpoint, ent, true, false, false, false, false, false, false) && ent->IsBuilding()){ FoundRoad = true; RoadZ = colpoint.point.z; } if(FoundRoad){ if(Source.z - RoadZ < 0.9f){ PreviouslyFailedRoadHeightCheck = true; TargetZOffSet = RoadZ + 0.9f - Source.z; }else{ if(m_bCollisionChecksOn) PreviouslyFailedRoadHeightCheck = false; else TargetZOffSet = 0.0f; } }else{ if(CWorld::ProcessVerticalLine(Source, 1000.0f, colpoint, ent, true, false, false, false, false, false, false) && ent->IsBuilding()){ FoundRoof = true; RoofZ = colpoint.point.z; } if(FoundRoof){ if(Source.z - RoofZ < 0.9f){ PreviouslyFailedRoadHeightCheck = true; TargetZOffSet = RoofZ + 0.9f - Source.z; }else{ if(m_bCollisionChecksOn) PreviouslyFailedRoadHeightCheck = false; else TargetZOffSet = 0.0f; } } } } if(TargetZOffSet > RoadHeightFix) RoadHeightFix = TargetZOffSet; else WellBufferMe(TargetZOffSet, &RoadHeightFix, &RoadHeightFixSpeed, 0.27f, 0.1f, false); if((colpoint.surfaceB == SURFACE_DEFAULT || colpoint.surfaceB >= SURFACE_METAL6) && colpoint.surfaceB != SURFACE_STEEL && colpoint.surfaceB != SURFACE_STONE && RoadHeightFix > 1.4f) RoadHeightFix = 1.4f; Source.z += RoadHeightFix; } void CCam::WorkOutCamHeight(const CVector &TargetCoors, float TargetOrientation, float TargetHeight) { static float LastTargetAlphaWithCollisionOn = 0.0f; static float LastTopAlphaSpeed = 0.0f; static float LastAlphaSpeedStep = 0.0f; static bool PreviousNearCheckNearClipSmall = false; bool CamClear = true; float ModeAlpha = 0.0f; if(ResetStatics){ LastTargetAlphaWithCollisionOn = 0.0f; LastTopAlphaSpeed = 0.0f; LastAlphaSpeedStep = 0.0f; PreviousNearCheckNearClipSmall = false; } float TopAlphaSpeed = 0.15f; float AlphaSpeedStep = 0.015f; float zoomvalue = TheCamera.CarZoomValueSmooth; if(zoomvalue < 0.1f) zoomvalue = 0.1f; if(TheCamera.CarZoomIndicator == 1.0f) ModeAlpha = CGeneral::GetATanOfXY(23.0f, zoomvalue); // near else if(TheCamera.CarZoomIndicator == 2.0f) ModeAlpha = CGeneral::GetATanOfXY(10.8f, zoomvalue); // mid else if(TheCamera.CarZoomIndicator == 3.0f) ModeAlpha = CGeneral::GetATanOfXY(7.0f, zoomvalue); // far float Length = (Source - TargetCoors).Magnitude2D(); if(m_bCollisionChecksOn){ // there's another variable (on PC) but it's uninitialised CVector Forward = CamTargetEntity->GetForward(); float CarAlpha = CGeneral::GetATanOfXY(Forward.Magnitude2D(), Forward.z); // this shouldn't be necessary.... while(CarAlpha >= PI) CarAlpha -= 2*PI; while(CarAlpha < -PI) CarAlpha += 2*PI; while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; float deltaBeta = Beta - TargetOrientation; while(deltaBeta >= PI) deltaBeta -= 2*PI; while(deltaBeta < -PI) deltaBeta += 2*PI; float BehindCarNess = cos(deltaBeta); // 1 if behind car, 0 if side, -1 if in front CarAlpha = -CarAlpha * BehindCarNess; if(CarAlpha < -0.01f) CarAlpha = -0.01f; float DeltaAlpha = CarAlpha - Alpha; while(DeltaAlpha >= PI) DeltaAlpha -= 2*PI; while(DeltaAlpha < -PI) DeltaAlpha += 2*PI; // What's this?? wouldn't it make more sense to clamp? float AngleLimit = DEGTORAD(1.8f); if(DeltaAlpha < -AngleLimit) DeltaAlpha += AngleLimit; else if(DeltaAlpha > AngleLimit) DeltaAlpha -= AngleLimit; else DeltaAlpha = 0.0f; // Now the collision float TargetAlpha = 0.0f; bool FoundRoofCenter = false; bool FoundRoofSide1 = false; bool FoundRoofSide2 = false; bool FoundCamRoof = false; bool FoundCamGround = false; float CamRoof = 0.0f; float CarBottom = TargetCoors.z - TargetHeight/2.0f; // Check car center float CarRoof = CWorld::FindRoofZFor3DCoord(TargetCoors.x, TargetCoors.y, CarBottom, &FoundRoofCenter); // Check sides of the car Forward = CamTargetEntity->GetForward(); // we actually still have that... Forward.Normalise(); // shouldn't be necessary float CarSideAngle = CGeneral::GetATanOfXY(Forward.x, Forward.y) + PI/2.0f; float SideX = 2.5f * cos(CarSideAngle); float SideY = 2.5f * sin(CarSideAngle); CWorld::FindRoofZFor3DCoord(TargetCoors.x + SideX, TargetCoors.y + SideY, CarBottom, &FoundRoofSide1); CWorld::FindRoofZFor3DCoord(TargetCoors.x - SideX, TargetCoors.y - SideY, CarBottom, &FoundRoofSide2); // Now find out at what height we'd like to place the camera float CamGround = CWorld::FindGroundZFor3DCoord(Source.x, Source.y, TargetCoors.z + Length*sin(Alpha + ModeAlpha) + m_fCloseInCarHeightOffset, &FoundCamGround); float CamTargetZ = 0.0f; if(FoundCamGround){ // This is the normal case CamRoof = CWorld::FindRoofZFor3DCoord(Source.x, Source.y, CamGround + TargetHeight, &FoundCamRoof); CamTargetZ = CamGround + TargetHeight*1.5f + 0.1f; }else{ FoundCamRoof = false; CamTargetZ = TargetCoors.z; } if(FoundRoofCenter && !FoundCamRoof && (FoundRoofSide1 || FoundRoofSide2)){ // Car is under something but camera isn't // This seems weird... TargetAlpha = CGeneral::GetATanOfXY(CA_MAX_DISTANCE, CarRoof - CamTargetZ - 1.5f); CamClear = false; } if(FoundCamRoof){ // Camera is under something float roof = FoundRoofCenter ? min(CamRoof, CarRoof) : CamRoof; // Same weirdness again? TargetAlpha = CGeneral::GetATanOfXY(CA_MAX_DISTANCE, roof - CamTargetZ - 1.5f); CamClear = false; } while(TargetAlpha >= PI) TargetAlpha -= 2*PI; while(TargetAlpha < -PI) TargetAlpha += 2*PI; if(TargetAlpha < DEGTORAD(-7.0f)) TargetAlpha = DEGTORAD(-7.0f); // huh? if(TargetAlpha > ModeAlpha) CamClear = true; // Camera is contrained by collision in some way PreviousNearCheckNearClipSmall = false; if(!CamClear){ PreviousNearCheckNearClipSmall = true; RwCameraSetNearClipPlane(Scene.camera, 0.9f); DeltaAlpha = TargetAlpha - (Alpha + ModeAlpha); while(DeltaAlpha >= PI) DeltaAlpha -= 2*PI; while(DeltaAlpha < -PI) DeltaAlpha += 2*PI; TopAlphaSpeed = 0.3f; AlphaSpeedStep = 0.03f; } // Now do things if CamClear...but what is that anyway? float CamZ = TargetCoors.z + Length*sin(Alpha + DeltaAlpha + ModeAlpha) + m_fCloseInCarHeightOffset; bool FoundGround, FoundRoof; float CamGround2 = CWorld::FindGroundZFor3DCoord(Source.x, Source.y, CamZ, &FoundGround); if(FoundGround){ if(CamClear) if(CamZ - CamGround2 < 1.5f){ PreviousNearCheckNearClipSmall = true; RwCameraSetNearClipPlane(Scene.camera, 0.9f); float a; if(Length == 0.0f || CamGround2 + 1.5f - TargetCoors.z == 0.0f) a = Alpha; else a = CGeneral::GetATanOfXY(Length, CamGround2 + 1.5f - TargetCoors.z); while(a > PI) a -= 2*PI; while(a < -PI) a += 2*PI; DeltaAlpha = a - Alpha; } }else{ if(CamClear){ float CamRoof2 = CWorld::FindRoofZFor3DCoord(Source.x, Source.y, CamZ, &FoundRoof); if(FoundRoof && CamZ - CamRoof2 < 1.5f){ PreviousNearCheckNearClipSmall = true; RwCameraSetNearClipPlane(Scene.camera, 0.9f); if(CamRoof2 > TargetCoors.z + 3.5f) CamRoof2 = TargetCoors.z + 3.5f; float a; if(Length == 0.0f || CamRoof2 + 1.5f - TargetCoors.z == 0.0f) a = Alpha; else a = CGeneral::GetATanOfXY(Length, CamRoof2 + 1.5f - TargetCoors.z); while(a > PI) a -= 2*PI; while(a < -PI) a += 2*PI; DeltaAlpha = a - Alpha; } } } LastTargetAlphaWithCollisionOn = DeltaAlpha + Alpha; LastTopAlphaSpeed = TopAlphaSpeed; LastAlphaSpeedStep = AlphaSpeedStep; }else{ if(PreviousNearCheckNearClipSmall) RwCameraSetNearClipPlane(Scene.camera, 0.9f); } WellBufferMe(LastTargetAlphaWithCollisionOn, &Alpha, &AlphaSpeed, LastTopAlphaSpeed, LastAlphaSpeedStep, true); Source.z = TargetCoors.z + sin(Alpha + ModeAlpha)*Length + m_fCloseInCarHeightOffset; } // Rotate cam behind the car when the car is moving forward bool CCam::RotCamIfInFrontCar(CVector &TargetCoors, float TargetOrientation) { bool MovingForward = false; CPhysical *phys = (CPhysical*)CamTargetEntity; float ForwardSpeed = DotProduct(phys->GetForward(), phys->GetSpeed(CVector(0.0f, 0.0f, 0.0f))); if(ForwardSpeed > 0.02f) MovingForward = true; float Dist = (Source - TargetCoors).Magnitude2D(); float DeltaBeta = TargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; if(fabs(DeltaBeta) > DEGTORAD(20.0f) && MovingForward && TheCamera.m_uiTransitionState == 0) m_bFixingBeta = true; CPad *pad = CPad::GetPad(0); if(!(pad->GetLookBehindForCar() || pad->GetLookBehindForPed() || pad->GetLookLeft() || pad->GetLookRight())) if(DirectionWasLooking != LOOKING_FORWARD) TheCamera.m_bCamDirectlyBehind = true; if(!m_bFixingBeta && !TheCamera.m_bUseTransitionBeta && !TheCamera.m_bCamDirectlyBehind && !TheCamera.m_bCamDirectlyInFront) return false; bool SetBeta = false; if(TheCamera.m_bCamDirectlyBehind || TheCamera.m_bCamDirectlyInFront || TheCamera.m_bUseTransitionBeta) if(&TheCamera.Cams[TheCamera.ActiveCam] == this) SetBeta = true; if(m_bFixingBeta || SetBeta){ WellBufferMe(TargetOrientation, &Beta, &BetaSpeed, 0.15f, 0.007f, true); if(TheCamera.m_bCamDirectlyBehind && &TheCamera.Cams[TheCamera.ActiveCam] == this) Beta = TargetOrientation; if(TheCamera.m_bCamDirectlyInFront && &TheCamera.Cams[TheCamera.ActiveCam] == this) Beta = TargetOrientation + PI; if(TheCamera.m_bUseTransitionBeta && &TheCamera.Cams[TheCamera.ActiveCam] == this) Beta = m_fTransitionBeta; Source.x = TargetCoors.x - cos(Beta)*Dist; Source.y = TargetCoors.y - sin(Beta)*Dist; // Check if we're done DeltaBeta = TargetOrientation - Beta; while(DeltaBeta >= PI) DeltaBeta -= 2*PI; while(DeltaBeta < -PI) DeltaBeta += 2*PI; if(fabs(DeltaBeta) < DEGTORAD(2.0f)) m_bFixingBeta = false; } TheCamera.m_bCamDirectlyBehind = false; TheCamera.m_bCamDirectlyInFront = false; return true; } // Move the cam to avoid clipping through buildings bool CCam::FixCamIfObscured(CVector &TargetCoors, float TargetHeight, float TargetOrientation) { CVector Target = TargetCoors; bool UseEntityPos = false; CVector EntityPos; static CColPoint colPoint; static bool LastObscured = false; if(Mode == MODE_BEHINDCAR) Target.z += TargetHeight/2.0f; if(Mode == MODE_CAMONASTRING){ UseEntityPos = true; Target.z += TargetHeight/2.0f; EntityPos = CamTargetEntity->GetPosition(); } CVector TempSource = Source; bool Obscured1 = false; bool Obscured2 = false; bool Fix1 = false; float Dist1 = 0.0f; float Dist2 = 0.0f; CEntity *ent; if(m_bCollisionChecksOn || LastObscured){ Obscured1 = CWorld::ProcessLineOfSight(Target, TempSource, colPoint, ent, true, false, false, true, false, true, true); if(Obscured1){ Dist1 = (Target - colPoint.point).Magnitude2D(); Fix1 = true; if(UseEntityPos) Obscured1 = CWorld::ProcessLineOfSight(EntityPos, TempSource, colPoint, ent, true, false, false, true, false, true, true); }else if(m_bFixingBeta){ float d = (TempSource - Target).Magnitude(); TempSource.x = Target.x - d*cos(TargetOrientation); TempSource.y = Target.y - d*sin(TargetOrientation); // same check again Obscured2 = CWorld::ProcessLineOfSight(Target, TempSource, colPoint, ent, true, false, false, true, false, true, true); if(Obscured2){ Dist2 = (Target - colPoint.point).Magnitude2D(); if(UseEntityPos) Obscured2 = CWorld::ProcessLineOfSight(EntityPos, TempSource, colPoint, ent, true, false, false, true, false, true, true); } } LastObscured = Obscured1 || Obscured2; } // nothing to do if(!LastObscured) return false; if(Fix1){ Source.x = Target.x - cos(Beta)*Dist1; Source.y = Target.y - sin(Beta)*Dist1; if(Mode == MODE_BEHINDCAR) Source = colPoint.point; }else{ WellBufferMe(Dist2, &m_fDistanceBeforeChanges, &DistanceSpeed, 0.2f, 0.025f, false); Source.x = Target.x - cos(Beta)*m_fDistanceBeforeChanges; Source.y = Target.y - sin(Beta)*m_fDistanceBeforeChanges; } if(ResetStatics){ m_fDistanceBeforeChanges = (Source - Target).Magnitude2D(); DistanceSpeed = 0.0f; Source.x = colPoint.point.x; Source.y = colPoint.point.y; } return true; } void CCam::Process_Cam_On_A_String(const CVector &CameraTarget, float TargetOrientation, float, float) { if(!CamTargetEntity->IsVehicle()) return; FOV = DefaultFOV; if(ResetStatics){ AlphaSpeed = 0.0f; if(TheCamera.m_bIdleOn) TheCamera.m_uiTimeWeEnteredIdle = CTimer::GetTimeInMilliseconds(); } CBaseModelInfo *mi = CModelInfo::GetModelInfo(CamTargetEntity->GetModelIndex()); CVector Dimensions = mi->GetColModel()->boundingBox.max - mi->GetColModel()->boundingBox.min; float BaseDist = Dimensions.Magnitude2D(); CVector TargetCoors = CameraTarget; TargetCoors.z += Dimensions.z - 0.1f; // final Beta = CGeneral::GetATanOfXY(TargetCoors.x - Source.x, TargetCoors.y - Source.y); while(Alpha >= PI) Alpha -= 2*PI; while(Alpha < -PI) Alpha += 2*PI; while(Beta >= PI) Beta -= 2*PI; while(Beta < -PI) Beta += 2*PI; m_fDistanceBeforeChanges = (Source - TargetCoors).Magnitude2D(); Cam_On_A_String_Unobscured(TargetCoors, BaseDist); WorkOutCamHeight(TargetCoors, TargetOrientation, Dimensions.z); RotCamIfInFrontCar(TargetCoors, TargetOrientation); FixCamIfObscured(TargetCoors, Dimensions.z, TargetOrientation); FixCamWhenObscuredByVehicle(TargetCoors); m_cvecTargetCoorsForFudgeInter = TargetCoors; Front = TargetCoors - Source; Front.Normalise(); GetVectorsReadyForRW(); ResetStatics = false; } // Basic Cam on a string algorithm void CCam::Cam_On_A_String_Unobscured(const CVector &TargetCoors, float BaseDist) { CA_MAX_DISTANCE = BaseDist + 0.1f + TheCamera.CarZoomValueSmooth; CA_MIN_DISTANCE = min(BaseDist*0.6f, 3.5f); CVector Dist = Source - TargetCoors; if(ResetStatics) Source = TargetCoors + Dist*(CA_MAX_DISTANCE + 1.0f); float Length = Dist.Magnitude2D(); if(Length < 0.001f){ // This probably shouldn't happen. reset view CVector Forward = CamTargetEntity->GetForward(); Forward.z = 0.0f; Forward.Normalise(); Source = TargetCoors - Forward*CA_MAX_DISTANCE; Dist = Source - TargetCoors; Length = Dist.Magnitude2D(); } if(Length > CA_MAX_DISTANCE){ Source.x = TargetCoors.x + Dist.x/Length * CA_MAX_DISTANCE; Source.y = TargetCoors.y + Dist.y/Length * CA_MAX_DISTANCE; }else if(Length < CA_MIN_DISTANCE){ Source.x = TargetCoors.x + Dist.x/Length * CA_MIN_DISTANCE; Source.y = TargetCoors.y + Dist.y/Length * CA_MIN_DISTANCE; } } void CCam::FixCamWhenObscuredByVehicle(const CVector &TargetCoors) { // BUG? is this never reset static float HeightFixerCarsObscuring = 0.0f; static float HeightFixerCarsObscuringSpeed = 0.0f; CColPoint colPoint; CEntity *entity; float HeightTarget = 0.0f; if(CWorld::ProcessLineOfSight(TargetCoors, Source, colPoint, entity, false, true, false, false, false, false, false)){ CBaseModelInfo *mi = CModelInfo::GetModelInfo(entity->GetModelIndex()); HeightTarget = mi->GetColModel()->boundingBox.max.z + 1.0f + TargetCoors.z - Source.z; if(HeightTarget < 0.0f) HeightTarget = 0.0f; } WellBufferMe(HeightTarget, &HeightFixerCarsObscuring, &HeightFixerCarsObscuringSpeed, 0.2f, 0.025f, false); Source.z += HeightFixerCarsObscuring; } bool CCam::Using3rdPersonMouseCam() { return CCamera::m_bUseMouse3rdPerson && (Mode == MODE_FOLLOWPED || TheCamera.m_bPlayerIsInGarage && FindPlayerPed() && FindPlayerPed()->m_nPedState != PED_DRIVING && Mode != MODE_TOPDOWN1 && this->CamTargetEntity == FindPlayerPed()); } STARTPATCHES InjectHook(0x42C760, &CCamera::IsSphereVisible, PATCH_JUMP); InjectHook(0x46FD00, &CCamera::SetFadeColour, PATCH_JUMP); InjectHook(0x46FD40, &CCamera::SetMotionBlur, PATCH_JUMP); InjectHook(0x46FD80, &CCamera::SetMotionBlurAlpha, PATCH_JUMP); InjectHook(0x46F940, &CCamera::RenderMotionBlur, PATCH_JUMP); InjectHook(0x456F40, WellBufferMe, PATCH_JUMP); InjectHook(0x4582F0, &CCam::GetVectorsReadyForRW, PATCH_JUMP); InjectHook(0x457710, &CCam::DoAverageOnVector, PATCH_JUMP); InjectHook(0x458060, &CCam::GetPedBetaAngleForClearView, PATCH_JUMP); InjectHook(0x457210, &CCam::Cam_On_A_String_Unobscured, PATCH_JUMP); InjectHook(0x457A80, &CCam::FixCamWhenObscuredByVehicle, PATCH_JUMP); InjectHook(0x457B90, &CCam::FixCamIfObscured, PATCH_JUMP); InjectHook(0x465DA0, &CCam::RotCamIfInFrontCar, PATCH_JUMP); InjectHook(0x4662D0, &CCam::WorkOutCamHeightWeeCar, PATCH_JUMP); InjectHook(0x466650, &CCam::WorkOutCamHeight, PATCH_JUMP); InjectHook(0x45E3A0, &CCam::Process_FollowPed, PATCH_JUMP); InjectHook(0x45BE60, &CCam::Process_BehindCar, PATCH_JUMP); InjectHook(0x45C090, &CCam::Process_Cam_On_A_String, PATCH_JUMP); ENDPATCHES