// // camera/cinematic/CinematicDirector.cpp // // Copyright (C) 1999-2011 Rockstar Games. All Rights Reserved. // #include "camera/cinematic/CinematicDirector.h" #include "fwsys/timer.h" #include "camera/CamInterface.h" #include "camera/cinematic/camera/animated/CinematicAnimatedCamera.h" #include "camera/cinematic/camera/tracking/CinematicCamManCamera.h" #include "camera/cinematic/camera/tracking/CinematicHeliChaseCamera.h" #include "camera/cinematic/camera/tracking/CinematicIdleCamera.h" #include "camera/cinematic/camera/tracking/CinematicFirstPersonIdleCamera.h" #include "camera/cinematic/camera/mounted/CinematicMountedCamera.h" #include "camera/cinematic/camera/mounted/CinematicMountedPartCamera.h" #include "camera/cinematic/camera/tracking/CinematicPedCloseUpCamera.h" #include "camera/cinematic/camera/tracking/CinematicPositionCamera.h" #include "camera/cinematic/camera/tracking/CinematicStuntCamera.h" #include "camera/cinematic/camera/tracking/CinematicTrainTrackCamera.h" #include "camera/cinematic/camera/tracking/CinematicWaterCrashCamera.h" #include "camera/cinematic/camera/orbit/CinematicVehicleOrbitCamera.h" #include "camera/cinematic/context/CinematicInVehicleContext.h" #include "camera/cinematic/context/CinematicInVehicleWantedContext.h" #include "camera/cinematic/context/CinematicInVehicleOverriddenFirstPersonContext.h" #include "camera/cinematic/context/CinematicInVehicleFirstPersonContext.h" #include "camera/cinematic/context/CinematicOnFootIdleContext.h" #include "camera/cinematic/context/CinematicWaterCrashContext.h" #include "camera/cinematic/shot/CinematicInVehicleShot.h" #include "camera/gameplay/GameplayDirector.h" #include "camera/gameplay/aim/ThirdPersonPedAssistedAimCamera.h" #include "camera/gameplay/follow/FollowVehicleCamera.h" #include "camera/helpers/ControlHelper.h" #include "camera/helpers/BaseFrameShaker.h" #include "camera/replay/ReplayDirector.h" #include "camera/scripted/ScriptDirector.h" #include "camera/system/CameraFactory.h" #include "camera/system/CameraMetadata.h" #include "frontend/MobilePhone.h" #include "game/ModelIndices.h" #include "network/NetworkInterface.h" #include "peds/PedIntelligence.h" #include "peds/QueriableInterface.h" #include "scene/playerswitch/PlayerSwitchInterface.h" #include "system/Control.h" #include "task/Animation/TaskAnims.h" #include "task/Animation/TaskScriptedAnimation.h" #include "task/Weapons/Gun/TaskGun.h" #include "task/combat/TaskCombatMelee.h" #include "task/combat/Subtasks/TaskVehicleCombat.h" #include "task/movement/TaskParachute.h" #include "Task/Vehicle/TaskVehicleWeapon.h" #include "tools/SectorTools.h" #include "vehicles/Metadata/VehicleSeatInfo.h" #include "vehicles/vehicle.h" #include "vehicles/train.h" CAMERA_OPTIMISATIONS() INSTANTIATE_RTTI_CLASS(camCinematicDirector,0xD9B9A56E) //NOTE: We wish to avoid overriding the camera metadata in the title update, if at all possible. So rather than change //camCinematicDirectorMetadata::m_DurationBeforeTriggeringNetworkCinematicVehicleCam, we are applying a scaling factor to //the minimum idle duration in code. const u32 g_DurationScalingForNetworkCinematicVehicleIdleMode = 2; #if __BANK #define MAX_NUMBER_BONNET_CAMERAS (35) const char* BonnetCameraTypes[MAX_NUMBER_BONNET_CAMERAS] = {"Use Metadata Defined Camera", "DEFAULT_VEHICLE_BONNET_CAMERA", "VEHICLE_BONNET_CAMERA_HIGH", "VEHICLE_BONNET_CAMERA_IGNUS", "VEHICLE_BONNET_CAMERA_LOW", "VEHICLE_BONNET_CAMERA_LOW_LOW", "VEHICLE_BONNET_CAMERA_LOW_CALICO", "VEHICLE_BONNET_CAMERA_MID", "VEHICLE_BONNET_CAMERA_MID_EXTRA_HIGH", "VEHICLE_BONNET_CAMERA_MID_HIGH", "VEHICLE_BONNET_CAMERA_MID_NEAR", "VEHICLE_BONNET_CAMERA_MONROE", "VEHICLE_BONNET_CAMERA_NEAR", "VEHICLE_BONNET_CAMERA_NEAR_EXTRA_HIGH", "VEHICLE_BONNET_CAMERA_NEAR_HIGH", "VEHICLE_BONNET_CAMERA_NEAR_LOW", "VEHICLE_BONNET_CAMERA_RAPIDGT", "VEHICLE_BONNET_CAMERA_STANDARD", "VEHICLE_BONNET_CAMERA_STANDARD_HIGH", "VEHICLE_BONNET_CAMERA_STANDARD_LONG", "VEHICLE_BONNET_CAMERA_STANDARD_LONG_EXTRA_LOW", "VEHICLE_BONNET_CAMERA_STANDARD_LONG_NO_PITCH", "VEHICLE_BONNET_CAMERA_STANDARD_LONG_REBLA", "VEHICLE_BONNET_CAMERA_STANDARD_LONG_DEVIANT", "VEHICLE_BONNET_CAMERA_STOCKADE", "VEHICLE_BONNET_CAMERA_TANK", "VEHICLE_BONNET_CAMERA_SCARAB", "VEHICLE_BONNET_CAMERA_SPEEDO4", "VEHICLE_BONNET_CAMERA_ZR3803", "VEHICLE_BONNET_CAMERA_FORMULA", "VEHICLE_BONNET_CAMERA_FORMULA2", "VEHICLE_BONNET_CAMERA_OPENWHEEL1", "BOAT_BONNET_CAMERA", "BOAT_LONGFIN_BONNET_CAMERA", "VEHICLE_SUB_KOSATKA_BONNET_CAMERA", }; #endif bool camCinematicDirector::IsSpectatingCopsAndCrooksEscape() { TUNE_GROUP_BOOL(CAMERA_CINEMATIC, bForceSpectatingCopsAndCroocksEscape, false); return bForceSpectatingCopsAndCroocksEscape || (NetworkInterface::IsInSpectatorMode() && CNetwork::GetNetworkGameModeState() == GameModeState_CnC_Escape); } bool camCinematicDirector::ms_UseBonnetCamera = false; PARAM(onFootCinematic, "[camCinematicDirector] Allow on foot cinematic"); camCinematicDirector::camCinematicDirector(const camBaseObjectMetadata& metadata) : camBaseDirector(metadata) , m_Metadata(static_cast(metadata)) , m_SlowMoEnvelope(NULL) , m_TargetEntity(NULL) , m_LastLookAtEntity(NULL) , m_FollowPedPositionOnPreviousUpdate(VEC3_ZERO) , m_PreviousKillShotStartTime(fwTimer::GetTimeInMilliseconds()) , m_PreviousReactionShotStartTime(fwTimer::GetTimeInMilliseconds()) , m_TimeCinematicButtonIsDown(0) , m_currentContext(-1) , m_TimeCinematicButtonInitalPressed(0) , m_InputSelectedShotPreference(SELECTION_INPUT_NONE) , m_TimeCinematicButtonWasHeldPressed(0) , m_SlowMotionBlendOutDurationOnShotTermination(0) , m_VehiclePovCameraWasTerminatedForWaterClippingTime(0) #if __BANK , m_DebugBonnetCameraIndex(0) #endif , m_SlowMotionScaling(1.0f) , m_desiredSlowMotionScaling(1.0f) , m_CachedHeadingForVehiclePovCameraDuringWaterClipping(0.0f) , m_CachedPitchForVehiclePovCameraDuringWaterClipping(0.0f) , m_IsCinematicButtonDisabledByScript(false) , m_IsFirstPersonInVehicleDisabledThisUpdate(false) , m_IsVehicleIdleModeDisabledThisUpdate(false) , m_HasCinematicContextChangedThisUpdate(false) , m_CanActivateMeleeCam(true) , m_ShouldSlowMotion(false) , m_HasControlStickBeenReset(false) , m_IsCinematicCameraModeLocked(false) , m_ShouldUsePreferedShot(false) , m_ShouldChangeShot(false) , m_IsSlowMoActive(false) , m_IsCinematicInputActive(false) , m_HaveOverriddenSlowMoShot(false) , m_LatchedState(false) , m_IsCinematicButtonDown(false) , m_HaveCancelledOverridenShot(false) , m_IsUsingQuickToggleThisFrame(false) , m_HaveJustChangedToToggleContext(false) , m_IsRenderedCamInVehicle(false) , m_IsRenderedCamPovTurret(false) , m_IsRenderedCamFirstPerson(false) , m_ShouldRespectTheLineOfAction(false) , m_ShouldScriptOverrideCinematicInputThisUpdate(false) , m_IsCinematicInputActiveForScript(false) , m_CanActivateNewsChannelContextThisUpdate(false) , m_CanActivateMissionCreatorFailContext(false) , m_WasCinematicButtonAciveAfterScriptStoppedBlocking(false) , m_HaveStoredPressedTime(false) , m_CanBlendOutSlowMotionOnShotTermination(false) , m_IsScriptDisablingSlowMoThisUpdate(false) , m_HaveUpdatedACinematicCamera(false) , m_DisableBonnetCameraForScriptedAnimTask(false) , m_IsMenuPreferenceForBonnetCameraIgnoredThisUpdate(false) , m_IsScriptDisablingWaterClippingTestThisUpdate(false) , m_VehiclePovCameraWasTerminatedForWaterClipping(false) #if __BANK ,m_WasCinematicInputBlockedByScriptForDebugOutput(false) #endif { m_Random.SetFullSeed((u64)sysTimer::GetTicks()); const int numContexts = m_Metadata.m_CinematicContexts.GetCount(); for(int i=0; i(*m_Metadata.m_CinematicContexts[i]); if(cameraVerifyf(pContext, "Failed to create a context (name: %s, hash: %u)", SAFE_CSTRING(m_Metadata.m_CinematicContexts[i]->m_Name.GetCStr()), m_Metadata.m_CinematicContexts[i]->m_Name.GetHash())) { m_Contexts.Grow() = pContext; } } } const camEnvelopeMetadata* envelopeMetadata = camFactory::FindObjectMetadata(m_Metadata.m_SlowMoEnvelopeRef.GetHash()); if(envelopeMetadata) { m_SlowMoEnvelope = camFactory::CreateObject(*envelopeMetadata); cameraAssertf(m_SlowMoEnvelope, "A cinematic context (name: %s, hash: %u) failed to create an envelope (name: %s, hash: %u)", GetName(), GetNameHash(), SAFE_CSTRING(envelopeMetadata->m_Name.GetCStr()), envelopeMetadata->m_Name.GetHash()); } } camCinematicDirector::~camCinematicDirector() { CleanUpCinematicContext(); } void camCinematicDirector::RegisterVehicleDamage(const CVehicle* pVehicle, const Vector3& impactDirection, float damage) { if (!m_Metadata.m_VehicleImpactHeadingShakeRef || !m_Metadata.m_VehicleImpactPitchShakeRef) { return; } if (camInterface::IsFadedOut() || camInterface::GetDominantRenderedDirector() != this) { return; } if(!m_UpdatedCamera) { return; } if (!m_UpdatedCamera->GetIsClassId(camCinematicMountedCamera::GetStaticClassId())) { return; } const camCinematicMountedCamera* cinematicMountedCamera = static_cast(m_UpdatedCamera.Get()); if (!cinematicMountedCamera->ShouldShakeOnImpact()) { return; } //compute the damage relative to the size of the vehicle const float relativeDamage = damage * InvertSafe(pVehicle->pHandling->m_fMass, pVehicle->GetInvMass()); if (pVehicle && (!pVehicle->InheritsFromPlane() || !const_cast(pVehicle)->IsInAir()) && camInterface::GetGameplayDirector().GetFollowVehicle() == pVehicle && relativeDamage > m_Metadata.m_VehicleImpactShakeMinDamage) { //Apply amplitude scaling based on the relative damage done const float amplitude = Min(relativeDamage * (m_Metadata.m_VehicleImpactShakeMaxAmplitude / m_Metadata.m_VehicleImpactShakeMaxDamage), m_Metadata.m_VehicleImpactShakeMaxAmplitude); Vector3 relativeImpactDirection; const Matrix34& worldMatrix = m_UpdatedCamera->GetFrame().GetWorldMatrix(); worldMatrix.UnTransform3x3(impactDirection, relativeImpactDirection); const float heading = camFrame::ComputeHeadingFromFront(relativeImpactDirection); const float headingAmplitude = amplitude * -sin(heading); const float pitchAmplitude = amplitude * cos(heading); #if __DEV && 0 Errorf("Vehicle shake heading amplitude is %f and pitch amplitude is %f for damage %f", headingAmplitude, pitchAmplitude, relativeDamage); #endif // __DEV m_UpdatedCamera->Shake(m_Metadata.m_VehicleImpactHeadingShakeRef, headingAmplitude, 2); m_UpdatedCamera->Shake(m_Metadata.m_VehicleImpactPitchShakeRef, pitchAmplitude, 2); } } //This is for Alex (and decals for rendering bullet holes and stuff on glass.) bool camCinematicDirector::IsRenderedCameraInsideVehicle() const { return m_IsRenderedCamInVehicle; } bool camCinematicDirector::IsRenderedCameraVehicleTurret() const { return m_IsRenderedCamPovTurret; } bool camCinematicDirector::IsRenderedCameraFirstPerson() const { return m_IsRenderedCamFirstPerson; } bool camCinematicDirector::IsRenderedCameraForcingMotionBlur() const { return m_UpdatedCamera && ( (m_UpdatedCamera->GetIsClassId(camCinematicMountedCamera::GetStaticClassId()) && static_cast(m_UpdatedCamera.Get())->IsCameraForcingMotionBlur()) || (m_UpdatedCamera->GetIsClassId(camCinematicMountedPartCamera::GetStaticClassId()) && static_cast(m_UpdatedCamera.Get())->IsCameraForcingMotionBlur()) ); } void camCinematicDirector::UpdateRenderedCameraVehicleFlags() { m_IsRenderedCamInVehicle = false; m_IsRenderedCamPovTurret = false; m_IsRenderedCamFirstPerson = false; if( m_UpdatedCamera && m_UpdatedCamera->GetIsClassId(camCinematicMountedCamera::GetStaticClassId()) ) { const camCinematicMountedCamera* pMountedCamera = static_cast(m_UpdatedCamera.Get()); m_IsRenderedCamInVehicle = pMountedCamera->IsCameraInsideVehicle(); m_IsRenderedCamPovTurret = pMountedCamera->IsVehicleTurretCamera(); m_IsRenderedCamFirstPerson = pMountedCamera->IsFirstPersonCamera(); } } bool camCinematicDirector::ShouldDisplayReticule() const { bool shouldDisplayReticule = false; if( m_RenderedCamera && m_RenderedCamera->GetIsClassId(camCinematicMountedCamera::GetStaticClassId())) { shouldDisplayReticule = static_cast(m_RenderedCamera.Get())->ShouldDisplayReticule(); } return shouldDisplayReticule; } void camCinematicDirector::InvalidateCinematicVehicleIdleMode() { //Invalidate the idle mode of the in-vehicle cinematic context. for(int i = 0; i < m_Contexts.GetCount(); i++) { camBaseCinematicContext* context = m_Contexts[i]; if(context && context->GetIsClassId(camCinematicInVehicleContext::GetStaticClassId())) { static_cast(context)->InvalidateIdleMode(); } } } void camCinematicDirector::InvalidateIdleCamera() { //Invalidate any idle contexts. for(int i = 0; i < m_Contexts.GetCount(); i++) { camBaseCinematicContext* context = m_Contexts[i]; if(context && context->GetIsClassId(camCinematicOnFootIdleContext::GetStaticClassId())) { static_cast(context)->Invalidate(); } } } u32 camCinematicDirector::GetTimeBeforeCinematicVehicleIdleCam() const { const u32 duration = (g_DurationScalingForNetworkCinematicVehicleIdleMode * m_Metadata.m_DurationBeforeTriggeringNetworkCinematicVehicleCam); return duration; } bool camCinematicDirector::IsRenderingCinematicMountedCamera(bool shouldTestValidity) const { if(IsAnyCinematicContextActive()) { if(m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleFirstPersonContext::GetStaticClassId() || m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleOverriddenFirstPersonContext::GetStaticClassId() ) { if(!shouldTestValidity || m_Contexts[m_currentContext]->IsValid()) { const camBaseCamera* renderedCamera = camInterface::GetDominantRenderedCamera(); if(renderedCamera && (renderedCamera->GetClassId() == camCinematicMountedCamera::GetStaticClassId())) { return true; } } } } return false; } bool camCinematicDirector::IsRenderingCinematicPointOfViewCamera(bool shouldTestValidity) const { if(IsAnyCinematicContextActive()) { if(m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleFirstPersonContext::GetStaticClassId() || m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleOverriddenFirstPersonContext::GetStaticClassId() ) { if(!shouldTestValidity || m_Contexts[m_currentContext]->IsValid()) { const camBaseCamera* renderedCamera = camInterface::GetDominantRenderedCamera(); if(renderedCamera && (renderedCamera->GetClassId() == camCinematicMountedCamera::GetStaticClassId())) { const bool c_IsFirstPersonCamera = static_cast(renderedCamera)->IsFirstPersonCamera(); return c_IsFirstPersonCamera; } } } } return false; } bool camCinematicDirector::IsRenderingCinematicCameraInsideVehicle(bool shouldTestValidity) const { if(IsAnyCinematicContextActive()) { if(m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleFirstPersonContext::GetStaticClassId() || m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleOverriddenFirstPersonContext::GetStaticClassId() ) { if(!shouldTestValidity || m_Contexts[m_currentContext]->IsValid()) { const camBaseCamera* renderedCamera = camInterface::GetDominantRenderedCamera(); if(renderedCamera && (renderedCamera->GetClassId() == camCinematicMountedCamera::GetStaticClassId())) { const camCinematicMountedCamera* mountedCamera = static_cast(renderedCamera); if(mountedCamera->IsCameraInsideVehicle()) { return true; } } } } } return false; } float camCinematicDirector::GetVehicleImpactShakeMinDamage() const { return m_Metadata.m_VehicleImpactShakeMinDamage; } camCinematicMountedCamera* camCinematicDirector::GetFirstPersonVehicleCamera() const { if(m_UpdatedCamera && m_UpdatedCamera->GetIsClassId(camCinematicMountedCamera::GetStaticClassId())) { camCinematicMountedCamera* firstPersonVehicleCamera = static_cast(m_UpdatedCamera.Get()); if(firstPersonVehicleCamera->IsFirstPersonCamera()) { return firstPersonVehicleCamera; } } return NULL; } camCinematicMountedCamera* camCinematicDirector::GetBonnetVehicleCamera() const { if(camCinematicDirector::ms_UseBonnetCamera || camInterface::GetGameplayDirector().IsForcingFirstPersonBonnet()) { if(m_UpdatedCamera && m_UpdatedCamera->GetIsClassId(camCinematicMountedCamera::GetStaticClassId())) { camCinematicMountedCamera* bonnetVehicleCamera = static_cast(m_UpdatedCamera.Get()); return bonnetVehicleCamera; } } return nullptr; } bool camCinematicDirector::IsRenderingCinematicIdleCamera(bool shouldTestValidity) const { if(IsAnyCinematicContextActive()) { if(m_Contexts[m_currentContext]->GetClassId()== camCinematicOnFootIdleContext::GetStaticClassId()) { if(!shouldTestValidity || m_Contexts[m_currentContext]->IsValid()) { const camBaseCamera* renderedCamera = camInterface::GetDominantRenderedCamera(); if(renderedCamera) { if(renderedCamera->GetClassId() == camCinematicIdleCamera::GetStaticClassId()) { return true; } if(renderedCamera->GetClassId() == camCinematicFirstPersonIdleCamera::GetStaticClassId()) { return true; } } } } } return false; } bool camCinematicDirector::IsRenderingCinematicWaterCrash(bool shouldTestValidity) const { if(IsAnyCinematicContextActive()) { if(m_Contexts[m_currentContext]->GetClassId()== camCinematicWaterCrashContext::GetStaticClassId()) { if(!shouldTestValidity || m_Contexts[m_currentContext]->IsValid()) { const camBaseCamera* renderedCamera = camInterface::GetDominantRenderedCamera(); if(renderedCamera && (renderedCamera->GetClassId() == camCinematicWaterCrashCamera::GetStaticClassId())) { return true; } } } } return false; } const camBaseCinematicContext* camCinematicDirector::GetCurrentContext() const { if(m_currentContext >= 0 && m_currentContext < m_Contexts.GetCount()) { return m_Contexts[m_currentContext]; } return NULL; } camCinematicDirector::eSideOfTheActionLine camCinematicDirector::GetSideOfActionLine(const CEntity* pNewLookAtEntity) const { if(m_ShouldRespectTheLineOfAction) { if(m_LastLookAtEntity == NULL || pNewLookAtEntity == NULL || pNewLookAtEntity !=m_LastLookAtEntity ) { return ACTION_LINE_ANY; } else { Vector3 LookAtForward = VEC3V_TO_VECTOR3(m_LastLookAtEntity->GetTransform().GetForward()); //compute the vector from the camera pos to parent Vector3 vLastToLookAt = VEC3V_TO_VECTOR3(m_LastLookAtEntity->GetTransform().GetPosition()) - m_LastCameraPosition; vLastToLookAt.NormalizeSafe(); //compute the last angle float LastAngleToLookAtTarget = vLastToLookAt.AngleZ(LookAtForward); if(Abs(LastAngleToLookAtTarget) < (m_Metadata.m_MinAngleToRespectTheLineOfAction *DtoR) || Abs(LastAngleToLookAtTarget) > (PI - (m_Metadata.m_MinAngleToRespectTheLineOfAction*DtoR)) ) { return ACTION_LINE_ANY; } else if(LastAngleToLookAtTarget > 0.0f) { return ACTION_LINE_LEFT; } else { return ACTION_LINE_RIGHT; } } } else { return ACTION_LINE_ANY; } } bool camCinematicDirector::IsCameraPositionValidWithRespectToTheLine( const CEntity* pNewLookAtEntity, const Vector3& desiredCamPosition) const { if(GetSideOfActionLine(pNewLookAtEntity) == ACTION_LINE_ANY) { return true; } if(pNewLookAtEntity) { Vector3 LookAtForward = VEC3V_TO_VECTOR3(pNewLookAtEntity->GetTransform().GetForward()); //compute the vector from the camera pos to parent Vector3 vLastToLookAt = VEC3V_TO_VECTOR3(pNewLookAtEntity->GetTransform().GetPosition()) - m_LastCameraPosition; vLastToLookAt.NormalizeSafe(); //compute the last angle float LastAngleToLookAtTarget = vLastToLookAt.AngleZ(LookAtForward); if(Abs(LastAngleToLookAtTarget) < (m_Metadata.m_MinAngleToRespectTheLineOfAction *DtoR) || Abs(LastAngleToLookAtTarget) > (PI - (m_Metadata.m_MinAngleToRespectTheLineOfAction*DtoR)) ) { return true; } //compute the new desired heading Vector3 vDesiredLookAt = VEC3V_TO_VECTOR3(pNewLookAtEntity->GetTransform().GetPosition()) - desiredCamPosition; vDesiredLookAt.NormalizeSafe(); float DesiredAngleToLookAtTarget = vDesiredLookAt.AngleZ(LookAtForward); if(Abs(DesiredAngleToLookAtTarget) < (m_Metadata.m_MinAngleToRespectTheLineOfAction *DtoR) || Abs(DesiredAngleToLookAtTarget) > (PI - (m_Metadata.m_MinAngleToRespectTheLineOfAction*DtoR)) ) { return true; } if (Sign(LastAngleToLookAtTarget) == Sign(DesiredAngleToLookAtTarget)) { return true; } else { return false; } } return true; } bool camCinematicDirector::Update() { bool hasSucceeded = false; m_ActiveRenderState = RS_NOT_RENDERING; ms_UseBonnetCamera = (CPauseMenu::GetMenuPreference( PREF_HOOD_CAMERA ) == TRUE) && !m_IsMenuPreferenceForBonnetCameraIgnoredThisUpdate; // HACK: cases where bonnet/hood camera needs to be disabled, using pov camera instead. const CPed* followPed = camInterface::FindFollowPed(); if(followPed && followPed->GetPlayerInfo()) { CVehicle* pVehicle = followPed->GetVehiclePedInside(); if (pVehicle) { u32 iSeatIndex = followPed->GetAttachCarSeatIndex(); const CVehicleModelInfo* pModelInfo = pVehicle->GetVehicleModelInfo(); const CVehicleSeatInfo* pSeatInfo = pVehicle->IsSeatIndexValid(iSeatIndex) ? pVehicle->GetSeatInfo(iSeatIndex) : NULL; const bool bIsFrontSeat = pSeatInfo ? pSeatInfo->GetIsFrontSeat() : false; const bool bHasRearActivities = pModelInfo ? pModelInfo->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_HAS_REAR_SEAT_ACTIVITIES) : false; const bool bShouldForceBonnetCamera = pModelInfo ? pModelInfo->GetVehicleFlag(CVehicleModelInfoFlags::FLAG_FORCE_BONNET_CAMERA_INSTEAD_OF_POV) : false; if (bShouldForceBonnetCamera) { ms_UseBonnetCamera = true; } if (!bIsFrontSeat && bHasRearActivities) { ms_UseBonnetCamera = false; } // No bonnet camera for turret seat of DUNE3 if (MI_CAR_DUNE3.IsValid() && pVehicle->GetModelIndex() == MI_CAR_DUNE3 && iSeatIndex == 1) { ms_UseBonnetCamera = false; } // Same with THRUSTER else if (MI_JETPACK_THRUSTER.IsValid() && pVehicle->GetModelIndex() == MI_JETPACK_THRUSTER) { ms_UseBonnetCamera = false; } // Same with KHANJALI (force POV so we can have individually-attached cameras per seat) else if (MI_TANK_KHANJALI.IsValid() && pVehicle->GetModelIndex() == MI_TANK_KHANJALI) { ms_UseBonnetCamera = false; } // Disable based on applied vehicle mods flagged in metadata // 13th June 2019 - Only doing this for certain vehicle models as this flag has never been hooked up since launch, so don't want to consider old (potentially invalid) data if (MI_CAR_HELLION.IsValid() && pVehicle->GetModelIndex() == MI_CAR_HELLION) { const CVehicleVariationInstance& vehVariation = pVehicle->GetVariationInstance(); u8 uVehModIndex = vehVariation.GetModIndex(VMT_EXHAUST); if (uVehModIndex != INVALID_MOD) { if (const CVehicleKit* pVehModKit = vehVariation.GetKit()) { const CVehicleModVisible& vehMod = pVehModKit->GetVisibleMods()[uVehModIndex]; if (vehMod.IsBonnetCameraDisabled()) { ms_UseBonnetCamera = false; } } } } } const bool isRenderingMobilePhoneCamera = CPhoneMgr::CamGetState(); if(pVehicle && CVehicle::IsTaxiModelId(pVehicle->GetModelId())) { // Copied from naEnvironment::AreWeAPassengerInATaxi() // see if the player has a car drive task, otherwise they are just a passenger if(followPed->GetAttachCarSeatIndex() > 1 && !followPed->GetPedResetFlag(CPED_RESET_FLAG_IsEnteringOrExitingVehicle)) { // No bonnet camera for player if he is a taxi passenger. ms_UseBonnetCamera = false; } } if(followPed->GetPedIntelligence()) { const bool bWasBonnetCameraDisabledForScriptedAnimTaskLastUpdate = m_DisableBonnetCameraForScriptedAnimTask; m_DisableBonnetCameraForScriptedAnimTask = false; CTaskScriptedAnimation* pTaskScriptedAnim = static_cast(followPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_SCRIPTED_ANIMATION)); CTaskSynchronizedScene* pTaskSyncScene = static_cast(followPed->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_SYNCHRONIZED_SCENE)); if( pTaskScriptedAnim || pTaskSyncScene ) { // No bonnet camera when running scripted animation task or synchonized scene task. // Forces camera inside for prostitute interaction and luxe passenger activities. m_DisableBonnetCameraForScriptedAnimTask = true; ms_UseBonnetCamera = false; } else if(bWasBonnetCameraDisabledForScriptedAnimTaskLastUpdate) { // HACK: Disable for 1 extra frame as it take a frame for prositiute interaction to switch from sync scene to scripted anim task. ms_UseBonnetCamera = false; } } if(isRenderingMobilePhoneCamera) { ms_UseBonnetCamera = false; } } UpdateContext(); if(m_UpdatedCamera) { m_LastLookAtEntity = m_UpdatedCamera->GetLookAtTarget(); m_LastCameraPosition = m_UpdatedCamera->GetFrame().GetPosition(); if(m_UpdatedCamera->GetIsClassId(camCinematicMountedPartCamera::GetStaticClassId())) { m_ShouldRespectTheLineOfAction = false; } else { m_ShouldRespectTheLineOfAction = true; } m_ActiveRenderState = RS_FULLY_RENDERING; UpdateRenderedCameraVehicleFlags(); m_HaveUpdatedACinematicCamera = true; } else { m_ShouldRespectTheLineOfAction = false; m_HaveUpdatedACinematicCamera = false; } UpdateCinematicViewModeSlowMotionControl(); if(m_VehiclePovCameraWasTerminatedForWaterClipping) { TUNE_GROUP_INT(VEHICLE_POV_CAMERA, iMinTimeAfterCachingVehiclePovWaterClipping, 500, 0, 60000, 1); TUNE_GROUP_INT(VEHICLE_POV_CAMERA, iMaxTimeAfterCachingVehiclePovWaterClipping, 5000, 0, 60000, 1); camCinematicMountedCamera* pVehicleCamera = GetFirstPersonVehicleCamera(); if(pVehicleCamera) { const u32 deltaTime = fwTimer::GetCamTimeInMilliseconds() - m_VehiclePovCameraWasTerminatedForWaterClippingTime; if(deltaTime > (u32)iMinTimeAfterCachingVehiclePovWaterClipping) { if(deltaTime <= (u32)iMaxTimeAfterCachingVehiclePovWaterClipping) { pVehicleCamera->SetUseScriptHeading(m_CachedHeadingForVehiclePovCameraDuringWaterClipping); pVehicleCamera->SetUseScriptPitch(m_CachedPitchForVehiclePovCameraDuringWaterClipping); } m_VehiclePovCameraWasTerminatedForWaterClipping = false; } } } //reset single frame flags m_IsFirstPersonInVehicleDisabledThisUpdate = false; m_IsVehicleIdleModeDisabledThisUpdate = false; m_ShouldScriptOverrideCinematicInputThisUpdate = false; m_CanActivateNewsChannelContextThisUpdate = false; m_CanActivateMissionCreatorFailContext = false; m_IsScriptDisablingSlowMoThisUpdate = false; m_IsMenuPreferenceForBonnetCameraIgnoredThisUpdate = false; m_IsScriptDisablingWaterClippingTestThisUpdate = false; return hasSucceeded; } void camCinematicDirector::PostUpdate() { camBaseDirector::PostUpdate(); //If we're rendering a bonnet camera or a vehicle part camera, apply any explosion shakes that are being managed by the gameplay director. const bool isRenderingCinematicMountedCamera = IsRenderingCinematicMountedCamera(); const bool isRenderingCinematicVehiclePartCamera = m_RenderedCamera && m_RenderedCamera->GetIsClassId(camCinematicMountedPartCamera::GetStaticClassId()); if(!isRenderingCinematicMountedCamera && !isRenderingCinematicVehiclePartCamera) { return; } DoSecondUpdateForTurretCamera(m_RenderedCamera.Get(), true); const atArray& explosionSettingsList = camInterface::GetGameplayDirector().GetExplosionSettings(); const s32 numExplosions = explosionSettingsList.GetCount(); for(s32 explosionIndex=0; explosionIndexShakeFrame(m_PostEffectFrame); } } } void camCinematicDirector::DoSecondUpdateForTurretCamera(camBaseCamera* pCamera, bool bDoPostUpdate) { if(pCamera && pCamera->GetIsClassId(camCinematicMountedCamera::GetStaticClassId())) { camCinematicMountedCamera* povTurretCamera = (camCinematicMountedCamera*)pCamera; if(povTurretCamera->IsVehicleTurretCamera() && povTurretCamera->GetAttachParent() && povTurretCamera->GetAttachParent()->GetIsTypeVehicle()) { CVehicle* pFollowVehicle = const_cast((const CVehicle*)povTurretCamera->GetAttachParent()); camControlHelper* pControlHelper = const_cast(povTurretCamera->GetControlHelper()); const CPed* followPed = camInterface::FindFollowPed(); bool isPlayerPed = followPed && followPed->GetPlayerInfo(); CPed* followPedNonConst = (isPlayerPed) ? const_cast(followPed) : NULL; int seatIndex = -1; if(pFollowVehicle && followPedNonConst && pControlHelper) { // Get the vehicle's seat manager const CSeatManager* seatManager = pFollowVehicle->GetSeatManager(); if(seatManager) { // Get the ped's seat index seatIndex = seatManager->GetPedsSeatIndex(followPedNonConst); } // Update the vehicle turret, its skeleton, then update the camera a second time. if(seatIndex >= 0 && pFollowVehicle->GetVehicleWeaponMgr()) { CTurret* turret = pFollowVehicle->GetVehicleWeaponMgr()->GetFirstTurretForSeat(seatIndex); if(turret && turret->IsPhysicalTurret()) { CTaskMotionInTurret* taskMotionInTurret = NULL; CTaskVehicleMountedWeapon* taskVehicleMountedWeapon = NULL; if(followPedNonConst->GetPedIntelligence()) { taskMotionInTurret = static_cast(followPedNonConst->GetPedIntelligence()->FindTaskActiveMotionByType(CTaskTypes::TASK_MOTION_IN_TURRET)); taskVehicleMountedWeapon = static_cast(followPedNonConst->GetPedIntelligence()->FindTaskActiveByType(CTaskTypes::TASK_VEHICLE_MOUNTED_WEAPON)); } ////followPedNonConst->ProcessPostCamera(); if(taskVehicleMountedWeapon) { taskVehicleMountedWeapon->ProcessPostCamera(); } if(taskMotionInTurret) { taskMotionInTurret->ProcessPostCamera(); } CTurretPhysical* turretPhysical = static_cast(turret); turretPhysical->ProcessPreRender( pFollowVehicle ); if(followPed->IsLocalPlayer()) { ////followPedNonConst->ProcessControl(); followPedNonConst->ProcessControl_Turret(); } if(pFollowVehicle->GetSkeleton()) { pFollowVehicle->GetSkeleton()->Update(); } pControlHelper->SkipControlHelperUpdateThisUpdate(); // disable input, already applied in first update // Second camera update { povTurretCamera->BaseUpdate(m_Frame); if(bDoPostUpdate) { camBaseDirector::PostUpdate(); } } } } } } } } void camCinematicDirector::CacheHeadingAndPitchForVehiclePovCameraDuringWaterClip(const float heading, const float pitch) { //make sure we only cache it once at the beginning of the water clipping, this needs to be checked because the //pov camera will persist for few frames after the water clipping has been detected. if(!m_VehiclePovCameraWasTerminatedForWaterClipping) { m_CachedHeadingForVehiclePovCameraDuringWaterClipping = heading; m_CachedPitchForVehiclePovCameraDuringWaterClipping = pitch; m_VehiclePovCameraWasTerminatedForWaterClipping = true; m_VehiclePovCameraWasTerminatedForWaterClippingTime = fwTimer::GetCamTimeInMilliseconds(); } } bool camCinematicDirector::IsRenderingAnyInVehicleCinematicCamera() const { if(IsAnyCinematicContextActive()) { if(m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleContext::GetStaticClassId() || m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleWantedContext::GetStaticClassId() ) { if(m_UpdatedCamera) { return true; } } } return false; } bool camCinematicDirector::IsRenderingAnyInVehicleFirstPersonCinematicCamera() const { if(IsAnyCinematicContextActive()) { if( m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleFirstPersonContext::GetStaticClassId() || m_Contexts[m_currentContext]->GetClassId()== camCinematicInVehicleOverriddenFirstPersonContext::GetStaticClassId() ) { if(m_UpdatedCamera) { return true; } } } return false; } camBaseCinematicContext* camCinematicDirector::FindContext(u32 classId) { for(int i=0; i < m_Contexts.GetCount(); i++) { if(m_Contexts[i]->GetClassId() == classId) { return m_Contexts[i]; } } return NULL; } void camCinematicDirector::UpdateCinematicViewModeSlowMotionControl() { CControl* control = camInterface::GetGameplayDirector().GetActiveControl(); if(IsAnyCinematicContextActive() && m_UpdatedCamera && !NetworkInterface::IsGameInProgress() && m_Contexts[m_currentContext]->m_Metadata.m_CanSelectSlowMotionFromControlInput && !m_IsScriptDisablingSlowMoThisUpdate) { m_CurrentSlowMotionBlendOutDurationOnShotTermination = 0; float shotSlowMo = m_Contexts[m_currentContext]->GetShotInitalSlowValue(); m_CanBlendOutSlowMotionOnShotTermination = m_Contexts[m_currentContext]->CanBlendOutSlowMotionOnShotTermination(); m_SlowMotionBlendOutDurationOnShotTermination = m_Contexts[m_currentContext]->GetShotSlowMotionBlendOutDuration(); float envelopeLevel = 1.0f; float stickOffsetY = 0.0f; if(control) { stickOffsetY = -control->GetVehicleSlowMoUpDown().GetNorm(); } //run if in stunt jump if(m_Contexts[m_currentContext]->IsInitialSlowMoValueValid(shotSlowMo) && !m_IsSlowMoActive && !m_HaveOverriddenSlowMoShot && !m_HaveCancelledOverridenShot) { m_desiredSlowMotionScaling = shotSlowMo; //set the desired slow mo to the initial m_IsSlowMoActive = true; m_HaveOverriddenSlowMoShot = true; //overriding the cinematic directors initial slow mo value. } //from stunt jump slow mo to a new shot reset the slow mo if(!m_Contexts[m_currentContext]->IsInitialSlowMoValueValid(shotSlowMo)) { if(m_HaveOverriddenSlowMoShot) { m_IsSlowMoActive = false; } m_HaveOverriddenSlowMoShot = false; m_HaveCancelledOverridenShot = false; } if(control && control->GetCinematicSlowMotion().IsReleased()) { m_HaveCancelledOverridenShot = true; if(m_IsSlowMoActive) { m_IsSlowMoActive = false; } else { m_desiredSlowMotionScaling = m_Metadata.m_DefaultSlowMotionScaling; m_IsSlowMoActive = true; } } if(m_SlowMoEnvelope) { m_SlowMoEnvelope->AutoStartStop(m_IsSlowMoActive); envelopeLevel = m_SlowMoEnvelope->Update(); } else { envelopeLevel = m_IsSlowMoActive ? 1.0f : 0.0f; } //listen to the control input to scale slow mo if(envelopeLevel >= 1.0f - SMALL_FLOAT) { if(m_IsSlowMoActive) { m_desiredSlowMotionScaling = m_desiredSlowMotionScaling + (stickOffsetY *m_Metadata.m_SlowMotionScalingDelta); m_desiredSlowMotionScaling = Clamp(m_desiredSlowMotionScaling, 0.0f, 1.0f); } } m_SlowMotionScaling = RampValueSafe(envelopeLevel, 0.0f, 1.0f, 1.0f, m_desiredSlowMotionScaling); m_SlowMotionScaling = Clamp( m_SlowMotionScaling, m_Metadata.m_MinSlowMotionScaling, 1.0f); m_previousSlowMoValue = m_SlowMotionScaling; } else { m_desiredSlowMotionScaling = 1.0f; m_IsSlowMoActive = false; m_HaveOverriddenSlowMoShot = false; m_HaveCancelledOverridenShot = false; if(m_SlowMoEnvelope) { m_SlowMoEnvelope->Stop(); } if(m_CanBlendOutSlowMotionOnShotTermination && m_SlowMotionBlendOutDurationOnShotTermination > 0) { m_CurrentSlowMotionBlendOutDurationOnShotTermination += fwTimer::GetCamTimeStepInMilliseconds(); float LerpValue = (float)m_CurrentSlowMotionBlendOutDurationOnShotTermination / (float)m_SlowMotionBlendOutDurationOnShotTermination; LerpValue = Clamp(LerpValue, 0.0f, 1.0f); m_SlowMotionScaling = Lerp(LerpValue ,m_previousSlowMoValue,1.0f); if(AreNearlyEqual(m_SlowMotionScaling, 1.0f, SMALL_FLOAT)) { m_SlowMotionScaling = 1.0f; m_CanBlendOutSlowMotionOnShotTermination = false; m_CurrentSlowMotionBlendOutDurationOnShotTermination = 0; } } else { m_SlowMotionScaling = 1.0f; } } const float currentSlowMotionScaling = m_Frame.GetCameraTimeScaleThisUpdate(); const float compoundSlowMotionScaling = currentSlowMotionScaling * m_SlowMotionScaling; m_Frame.SetCameraTimeScaleThisUpdate(compoundSlowMotionScaling); //Make exclusive use of INPUT_VEH_SLOWMO_UD whenever slow motion is active. if(m_IsSlowMoActive && control) { control->SetInputExclusive(INPUT_VEH_SLOWMO_UD); } } void camCinematicDirector::UpdateInputForQuickToggleActivationMode() { bool WasCinematicDownOnPreviousUpdate = m_IsCinematicButtonDown; m_ShouldHoldCinematicShot = false; m_IsCinematicButtonDown = false; CControl* control = camInterface::GetGameplayDirector().GetActiveControl(); if(IsCinematicButtonDisabledByScriptState()) { m_WasCinematicButtonAciveAfterScriptStoppedBlocking = true; m_HaveStoredPressedTime = false; m_TimeCinematicButtonWasHeldPressed = 0; #if __BANK if(!m_WasCinematicInputBlockedByScriptForDebugOutput) { cameraDebugf3("CinematicDirector: Script is blocking Cinematic Input"); m_WasCinematicInputBlockedByScriptForDebugOutput = true; } #endif } else if(control && !control->GetVehicleCinematicCam().IsDown()) { #if __BANK if(m_WasCinematicInputBlockedByScriptForDebugOutput) { cameraDebugf3("CinematicDirector: Script is not blocking Cinematic Input"); m_WasCinematicInputBlockedByScriptForDebugOutput = false; } #endif m_WasCinematicButtonAciveAfterScriptStoppedBlocking = false; m_HaveStoredPressedTime = false; } if(fwTimer::GetCamTimeInMilliseconds() - m_TimeCinematicButtonWasHeldPressed < m_Metadata.m_HoldDurationOfCinematicShotForEarlyTermination) { m_ShouldHoldCinematicShot = true; } if(m_IsUsingQuickToggleThisFrame && !IsCinematicButtonDisabledByScriptState()) { CControl* control = camInterface::GetGameplayDirector().GetActiveControl(); if(control) { if(control->GetVehicleCinematicCam().IsPressed()) { if(!m_WasCinematicButtonAciveAfterScriptStoppedBlocking) { m_TimeCinematicButtonInitalPressed = fwTimer::GetCamTimeInMilliseconds(); } } if(control->GetVehicleCinematicCam().IsReleased()) { if(!WasCinematicDownOnPreviousUpdate) { if(!m_WasCinematicButtonAciveAfterScriptStoppedBlocking) { m_LatchedState = !m_LatchedState; m_TimeCinematicButtonInitalPressed = 0; m_HaveJustChangedToToggleContext = false; #if __BANK if(m_LatchedState) { cameraDebugf3("CinematicDirector: Cinematc mode quick toggled on"); } else { cameraDebugf3("CinematicDirector: Cinematc mode quick toggled off"); } #endif } } } if(control->GetVehicleCinematicCam().IsDown()) { if(fwTimer::GetCamTimeInMilliseconds() - m_TimeCinematicButtonInitalPressed > m_Metadata.m_QuickToggleTimeThreshold ) { if(!m_HaveStoredPressedTime) { //not coming from a latch state if(!m_LatchedState) { m_TimeCinematicButtonWasHeldPressed = fwTimer::GetCamTimeInMilliseconds(); } else { //else previously latched m_TimeCinematicButtonWasHeldPressed = 0; } m_HaveStoredPressedTime = true; cameraDebugf3("CinematicDirector: Cinematc button held down"); } if(!m_WasCinematicButtonAciveAfterScriptStoppedBlocking) { m_IsCinematicButtonDown = true; m_LatchedState = false; m_HaveJustChangedToToggleContext = false; } } } else { m_HaveJustChangedToToggleContext = false; } const bool hasReleasedViewModeInput = control->GetNextCameraMode().IsEnabled() && control->GetNextCameraMode().IsReleased(); if(hasReleasedViewModeInput) { #if __BANK if(m_LatchedState) { cameraDebugf3("CinematicDirector: Cinematc toggled off because a new mode has been selected"); } #endif m_LatchedState = false; m_HaveJustChangedToToggleContext = false; } } } else { //reset the latch state if being blocked by script or the current context is invalid but not invalidated by aiming. if(IsCinematicButtonDisabledByScriptState() || (m_currentContext == -1 && !camInterface::GetGameplayDirector().IsAiming())) { m_LatchedState = false; } m_HaveJustChangedToToggleContext = false; } } void camCinematicDirector::UpdateCinematicViewModeControl() { float stickOffsetX = 0.0f; float stickOffsetY = 0.0f; CControl* control = camInterface::GetGameplayDirector().GetActiveControl(); if(!m_ShouldScriptOverrideCinematicInputThisUpdate) { m_IsCinematicInputActiveForScript = false; //Right stick normally switches the cinematic camera mode to previous or next. if(control) { UpdateInputForQuickToggleActivationMode(); #if __BANK bool WasCinematicModeActive = m_IsCinematicInputActive; #endif if(m_IsUsingQuickToggleThisFrame && !m_HaveJustChangedToToggleContext) { m_IsCinematicInputActive = m_LatchedState || m_IsCinematicButtonDown || m_ShouldHoldCinematicShot; } else { m_IsCinematicInputActive = control->GetVehicleCinematicCam().IsDown() || m_ShouldHoldCinematicShot; } #if __BANK if(WasCinematicModeActive != m_IsCinematicInputActive) { if(m_IsCinematicInputActive) { cameraDebugf3("CinematicDirector: Cinematic Mode Active"); } else { cameraDebugf3("CinematicDirector: Cinematic Mode Not Active"); } } #endif } } else { m_IsCinematicInputActiveForScript = true; } if(control) { //TODO: Make these control inputs explicit for cinematic camera control. stickOffsetX = control->GetVehicleCinematicLeftRight().GetNorm() * 128.0f; // 128.0f to convert to old input range. stickOffsetY = -control->GetVehicleCinematicUpDown().GetNorm() * 128.0f; } if(!m_IsSlowMoActive) { if((Abs(stickOffsetX) < m_Metadata.m_ControlStickDeadZone) && (Abs(stickOffsetY) < m_Metadata.m_ControlStickDeadZone)) { m_HasControlStickBeenReset = true; m_InputSelectedShotPreference = SELECTION_INPUT_NONE; } if(m_HasControlStickBeenReset) { //up Y if(stickOffsetY > m_Metadata.m_ControlStickTriggerThreshold) { m_HasControlStickBeenReset = false; m_ShouldChangeShot = true; m_InputSelectedShotPreference = SELECTION_INPUT_UP; } else if(stickOffsetY < -m_Metadata.m_ControlStickTriggerThreshold) { m_HasControlStickBeenReset = false; m_ShouldChangeShot = true; m_InputSelectedShotPreference = SELECTION_INPUT_DOWN; } else if(stickOffsetX > m_Metadata.m_ControlStickTriggerThreshold) { m_HasControlStickBeenReset = false; m_ShouldChangeShot = true; m_InputSelectedShotPreference = SELECTION_INPUT_RIGHT; } else if (stickOffsetX < -m_Metadata.m_ControlStickTriggerThreshold) { m_HasControlStickBeenReset = false; m_ShouldChangeShot = true; m_InputSelectedShotPreference = SELECTION_INPUT_LEFT; } } } } bool camCinematicDirector::CreateHigherPriorityContext() { if(IsAnyCinematicContextActive()) { for(int i = 0; i < m_currentContext; i++) { if(m_Contexts[i]->CanAbortOtherContexts() ) { u32 CurrentShotDuration = m_Contexts[m_currentContext]->GetCurrentShotDuration(); if(!m_Contexts[m_currentContext]->GetCurrentShot() || CurrentShotDuration > (u32)(m_Contexts[m_currentContext]->GetCurrentShot()->GetMinimumShotDuration() * m_Contexts[i]->GetMinShotScalar())) { if(m_Contexts[i]->Update()) { if(m_Contexts[i]->GetCamera()) { m_Contexts[m_currentContext]->ClearContext(); m_currentContext = i; m_UpdatedCamera = m_Contexts[m_currentContext]->GetCamera(); return true; } } } } } } return false; } void camCinematicDirector::PreUpdateContext() { for(int i = 0; i < m_Contexts.GetCount(); i++) { m_Contexts[i]->PreUpdate(); } } void camCinematicDirector::DoesCurrentContextSupportQuickToggle() { if(!m_ShouldScriptOverrideCinematicInputThisUpdate) { m_IsUsingQuickToggleThisFrame = false; for(int i = 0; i < m_Contexts.GetCount(); i++) { if(m_Contexts[i]->CanActivateUsingQuickToggle() && m_Contexts[i]->IsValid(false)) { if(m_currentContext != -1) { if(!m_Contexts[m_currentContext]->CanActivateUsingQuickToggle()) { CControl* control = camInterface::GetGameplayDirector().GetActiveControl(); if(control && control->GetVehicleCinematicCam().IsDown()) { m_HaveJustChangedToToggleContext = true; } } } m_IsUsingQuickToggleThisFrame = true; return; } } } } void camCinematicDirector::UpdateScannedEntitesForWantedClonedPlayer() { const CPed* followPed = camInterface::FindFollowPed(); if(followPed && followPed->IsNetworkClone()) { if(followPed->GetPlayerWanted() && followPed->GetPlayerWanted()->GetWantedLevel() > WANTED_CLEAN) { CPed* nonConstPed = const_cast(followPed); followPed->GetPedIntelligence()->GetPedScanner()->ScanForEntitiesInRange(*nonConstPed, false); } } } bool camCinematicDirector::UpdateContext() { bool hasSucceeded = true; bool applyAllUpdates = true; m_UpdatedCamera = NULL; //Block all cinematic contexts if the user is Switching characters or triggering a gameplay hint or in replay mode. const bool isSwitchActive = g_PlayerSwitch.IsActive(); const bool isGameplayHintActive = camInterface::GetGameplayDirector().IsHintActive(); const bool isReplayActive = camInterface::IsRenderingDirector(camInterface::GetReplayDirector()); if(isSwitchActive || isGameplayHintActive || isReplayActive) { //Set that the cinematic button has to be released if being held. m_WasCinematicButtonAciveAfterScriptStoppedBlocking = true; if(m_currentContext >= 0 && m_currentContext < m_Contexts.GetCount()) { m_Contexts[m_currentContext]->ClearContext(); } m_currentContext = -1; hasSucceeded = false; } else { DoesCurrentContextSupportQuickToggle(); //cloned peds do not run the entity scanner as part of their AI update so do it here UpdateScannedEntitesForWantedClonedPlayer(); PreUpdateContext(); UpdateCinematicViewModeControl(); if(m_currentContext >= 0 && m_currentContext < m_Contexts.GetCount()) { if(!CreateHigherPriorityContext()) { if(m_Contexts[m_currentContext]->IsValid() && m_Contexts[m_currentContext]->CanUpdate() != CSS_MUST_CHANGE_CAMERA) { if(m_Contexts[m_currentContext]->Update()) { if(!m_Contexts[m_currentContext]->IsUsingGameCameraAsFallBack()) { m_UpdatedCamera = m_Contexts[m_currentContext]->GetCamera(); if(m_UpdatedCamera) { //dont run a base update if this is first time round its already been done if(m_UpdatedCamera->WasUpdated()) { if(m_UpdatedCamera->IsValid()) { applyAllUpdates = false; } else { m_Contexts[m_currentContext]->ClearContext(); m_currentContext = -1; } } else { if(m_UpdatedCamera->BaseUpdate(m_Frame)) { applyAllUpdates = false; } else { m_Contexts[m_currentContext]->ClearContext(); m_currentContext = -1; } } } } else { applyAllUpdates = false; } } } else { m_Contexts[m_currentContext]->ClearContext(); m_currentContext = -1; } } else { applyAllUpdates = false; } } if(applyAllUpdates) { for(int i = 0; i < m_Contexts.GetCount(); i++) { if(m_Contexts[i]) { if(m_Contexts[i]->Update()) { m_currentContext = i; m_UpdatedCamera = m_Contexts[i]->GetCamera(); break; } } } } } //reset here as only valid for one frame of a context m_ShouldChangeShot = false; return hasSucceeded; } void camCinematicDirector::CleanUpCinematicContext() { //Clean-up any existing camera. if(m_UpdatedCamera) { delete m_UpdatedCamera; } //Clean-up any slow motion. m_SlowMotionScaling = 1.0f; } #if FPS_MODE_SUPPORTED void camCinematicDirector::RegisterWeaponFireRemote(const CWeapon& weapon, const CEntity& firingEntity) { if(IsRenderingAnyInVehicleFirstPersonCinematicCamera()) { const CWeaponInfo* weaponInfo = weapon.GetWeaponInfo(); if(weaponInfo && weaponInfo->GetIsTurret() && firingEntity.GetIsTypePed()) { const CPed* localPed = CPedFactory::GetFactory()->GetLocalPlayer(); const CPed* firingPed = static_cast(&firingEntity); const CVehicle* firingPedVehicle = static_cast(firingPed->GetVehiclePedInside()); // Don't shake if we're the ped firing the turret (as this would cause a duplicate shake), check that we're in the same vehicle if(firingPed != localPed && localPed->GetVehiclePedInside() == firingPedVehicle) { const u32 recoilShakeHash = weaponInfo->GetRecoilShakeHash(); camBaseCamera* renderedCamera = (camBaseCamera*)GetRenderedCamera(); if(recoilShakeHash && renderedCamera) { float distanceSquared = (renderedCamera->GetFrame().GetWorldMatrix().d - VEC3V_TO_VECTOR3(firingPed->GetMatrix().d())).Mag2(); distanceSquared = distanceSquared > 0 ? distanceSquared : SMALL_FLOAT; const float intensity = g_TurretShakeWeaponPower / (PI * distanceSquared * 4); const float shakeScaling = g_TurretShakeFirstPersonScaling; const float shakeAmplitude = Clamp(shakeScaling * intensity, 0.0f, g_TurretShakeMaxAmplitude); renderedCamera->Shake(recoilShakeHash, shakeAmplitude); } } } } } void camCinematicDirector::RegisterWeaponFireLocal(const CWeapon& weapon, const CEntity& firingEntity) { if( IsRenderingAnyInVehicleFirstPersonCinematicCamera() ) { //Only trigger a recoil shake if the follow-ped is firing. const CPed* followPed = camInterface::GetGameplayDirector().GetFollowPed(); if(followPed == &firingEntity) { bool bTurretCamera = false; if(m_UpdatedCamera && m_UpdatedCamera->GetIsClassId(camCinematicMountedCamera::GetStaticClassId())) { const camCinematicMountedCamera* pMountedCamera = (const camCinematicMountedCamera*)m_UpdatedCamera.Get(); bTurretCamera = pMountedCamera->IsVehicleTurretCamera(); } const CWeaponInfo* weaponInfo = weapon.GetWeaponInfo(); if( weaponInfo && (weaponInfo->GetUseFPSAimIK() || bTurretCamera) ) { //Kick-off a new recoil shake if specified in the weapon metadata. u32 recoilShakeHash = weaponInfo->GetRecoilShakeHash(); // TODO: should we use first person recoil shake, third person or a custom one? using first person for now. if( weapon.GetFirstPersonRecoilShakeHash() != 0) recoilShakeHash = weapon.GetFirstPersonRecoilShakeHash(); const camShakeMetadata* recoilShakeMetadata = camFactory::FindObjectMetadata(recoilShakeHash); if(recoilShakeMetadata) { //Apply any weapon-specific amplitude scaling to the shakes. float amplitude = weapon.GetRecoilShakeAmplitude(); const u32 minTimeBetweenRecoilShakes = weaponInfo->GetMinTimeBetweenRecoilShakes(); m_UpdatedCamera->Shake(*recoilShakeMetadata, amplitude, 1, minTimeBetweenRecoilShakes); } //Kick-off or auto-start the accuracy offset shake if specified in the weapon metadata. ////const u32 accuracyOffsetShakeHash = weaponInfo->GetAccuracyOffsetShakeHash(); ////const camShakeMetadata* accuracyOffsetShakeMetadata = camFactory::FindObjectMetadata(accuracyOffsetShakeHash); ////if(accuracyOffsetShakeMetadata) ////{ //// camBaseFrameShaker* accuracyOffsetShake = m_UpdatedCamera->FindFrameShaker(accuracyOffsetShakeHash); //// if(accuracyOffsetShake) //// { //// //Auto-start the shake, ensuring that we receive the full hold duration from this point on. //// accuracyOffsetShake->AutoStart(); //// } //// else //// { //// //Start with zero amplitude, as this will be updated to an appropriate level in UpdateAimAccuracyOffsetShake(). //// m_UpdatedCamera->Shake(*accuracyOffsetShakeMetadata, 0.0f); //// } ////} } } } } #endif #if __BANK void camCinematicDirector::AddWidgetsForInstance() { if(m_WidgetGroup == NULL) { bkBank* bank = BANKMGR.FindBank("Camera"); if(bank != NULL) { m_WidgetGroup = bank->PushGroup("Cinematic director", false); { bank->AddCombo( "bonnet camera", &m_DebugBonnetCameraIndex, MAX_NUMBER_BONNET_CAMERAS, BonnetCameraTypes); bank->AddToggle("Use bonnet camera [read-only]", &ms_UseBonnetCamera ); } bank->PopGroup(); //Cutscene director. } } } u32 camCinematicDirector::GetOverriddenBonnetCamera() { if(m_DebugBonnetCameraIndex == 0) { return 0; } else { return atStringHash(BonnetCameraTypes[m_DebugBonnetCameraIndex]); } } #endif // __BANK