diff --git a/Client/game_sa/CCarEnterExitSA.cpp b/Client/game_sa/CCarEnterExitSA.cpp index de9687fed7d..94f3219f4b4 100644 --- a/Client/game_sa/CCarEnterExitSA.cpp +++ b/Client/game_sa/CCarEnterExitSA.cpp @@ -183,3 +183,16 @@ bool CCarEnterExitSA::IsRoomForPedToLeaveCar(CVehicle* pVehicle, int iDoor, CVec return bRet; } + +void CCarEnterExitSA::GetPositionToOpenCarDoor(CVector& vecPosition, CVehicle* pVehicle, unsigned int uiDoor) +{ + CVehicleSA* pVehicleSA = dynamic_cast(pVehicle); + + if (pVehicleSA) + { + CVehicleSAInterface* pVehicleInterface = pVehicleSA->GetVehicleInterface(); + + auto CCarEnterExit_GetPositionToOpenCarDoor = (void(__cdecl*)(CVector&, CVehicleSAInterface*, int))FUNC_GetPositionToOpenCarDoor; + CCarEnterExit_GetPositionToOpenCarDoor(vecPosition, pVehicleInterface, uiDoor); + } +} diff --git a/Client/game_sa/CCarEnterExitSA.h b/Client/game_sa/CCarEnterExitSA.h index ef9a57bf5e8..ba81fdfb943 100644 --- a/Client/game_sa/CCarEnterExitSA.h +++ b/Client/game_sa/CCarEnterExitSA.h @@ -19,6 +19,7 @@ #define FUNC_GetNearestCarPassengerDoor 0x650BB0 #define FUNC_ComputeTargetDoorToExit 0x64F110 #define FUNC_IsRoomForPedToLeaveCar 0x6504C0 +#define FUNC_GetPositionToOpenCarDoor 0x64E740 class CCarEnterExitSA : public CCarEnterExit { @@ -27,4 +28,5 @@ class CCarEnterExitSA : public CCarEnterExit bool GetNearestCarPassengerDoor(CPed* pPed, CVehicle* pVehicle, CVector* pVector, int* pDoor, bool bUnknown, bool bUnknown2, bool bCheckIfRoomToGetIn); int ComputeTargetDoorToExit(CPed* pPed, CVehicle* pVehicle); bool IsRoomForPedToLeaveCar(CVehicle* pVehicle, int iDoor, CVector* pUnknown = 0); + void GetPositionToOpenCarDoor(CVector& vecPosition, CVehicle* pVehicle, unsigned int uiDoor); }; diff --git a/Client/mods/deathmatch/logic/CClientExplosionManager.cpp b/Client/mods/deathmatch/logic/CClientExplosionManager.cpp index 428d759af7f..70692d59d7c 100644 --- a/Client/mods/deathmatch/logic/CClientExplosionManager.cpp +++ b/Client/mods/deathmatch/logic/CClientExplosionManager.cpp @@ -109,6 +109,28 @@ bool CClientExplosionManager::Hook_ExplosionCreation(CEntity* pGameExplodingEnti if (!bIsLocalPlayer && !bIsLocalPlayerVehicle && !bIsUnoccupiedVehicleSynced) return false; + if (bIsLocalPlayer || bIsLocalPlayerVehicle) + { + CClientEntity* pOriginSource = NULL; + + // Check if the "weapon" used was a rocket or a tank grenade. + if (explosionWeaponType == WEAPONTYPE_ROCKET || explosionWeaponType == WEAPONTYPE_TANK_GRENADE) + { + CClientVehicle* pOccupiedVehicle = pLocalPlayer->GetOccupiedVehicle(); + + // Is the player in a vehicle? + if (pOccupiedVehicle) + { + // Is the vehicle only available locally? + if (pOccupiedVehicle->IsLocalEntity()) + { + // It is pretty safe to asume this was a projectile from a client-side vehicle driven by the player. Abort. + return true; + } + } + } + } + CClientEntity* pOriginSource = nullptr; // Is this an exploding vehicle? diff --git a/Client/mods/deathmatch/logic/CClientGame.cpp b/Client/mods/deathmatch/logic/CClientGame.cpp index b58cb2ce188..be049f71ce5 100644 --- a/Client/mods/deathmatch/logic/CClientGame.cpp +++ b/Client/mods/deathmatch/logic/CClientGame.cpp @@ -1559,63 +1559,90 @@ void CClientGame::UpdateVehicleInOut() if (m_bIsGettingOutOfVehicle) { // If we aren't working on leaving the car (he's eiter finished or cancelled/failed leaving) - if (!m_pLocalPlayer->IsLeavingVehicle()) + if (!m_pLocalPlayer->IsLeavingVehicle() && !m_pLocalPlayer->IsGettingJacked()) { // Are we outside the car? CClientVehicle* pVehicle = m_pLocalPlayer->GetRealOccupiedVehicle(); + if (!pVehicle) { - // Tell the server that we successfully left the car - NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - if (pBitStream) + // Check if vehicle isn't local. + if (!pInOutVehicle->IsLocalEntity()) { - // Write the car id and the action id (enter complete) - pBitStream->Write(m_VehicleInOutID); - unsigned char ucAction = VEHICLE_NOTIFY_OUT; - pBitStream->WriteBits(&ucAction, 4); + // Tell the server that we successfully left the car + NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); + if (pBitStream) + { + // Write the car id and the action id (enter complete) + pBitStream->Write(m_VehicleInOutID); + unsigned char ucAction = VEHICLE_NOTIFY_OUT; + pBitStream->WriteBits(&ucAction, 4); - // Send it and destroy the packet - g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); - g_pNet->DeallocateNetBitStream(pBitStream); - } + // Send it and destroy the packet + g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(pBitStream); + } - // Warp ourself out (so we're sure the records are correct) - m_pLocalPlayer->RemoveFromVehicle(); + // Warp ourself out (so we're sure the records are correct) + m_pLocalPlayer->RemoveFromVehicle(); - /* - // Make it undamagable if we're not syncing it, and damagable if we're syncing it - if ( pInOutVehicle ) - { - if ( pInOutVehicle->IsSyncing () ) + /* + // Make it undamagable if we're not syncing it, and damagable if we're syncing it + if ( pInOutVehicle ) { - pInOutVehicle->SetCanBeDamaged ( true ); - pInOutVehicle->SetTyresCanBurst ( true ); - } - else - { - pInOutVehicle->SetCanBeDamaged ( false ); - pInOutVehicle->SetTyresCanBurst ( false ); - } - }*/ + if ( pInOutVehicle->IsSyncing () ) + { + pInOutVehicle->SetCanBeDamaged ( true ); + pInOutVehicle->SetTyresCanBurst ( true ); + } + else + { + pInOutVehicle->SetCanBeDamaged ( false ); + pInOutVehicle->SetTyresCanBurst ( false ); + } + }*/ + } + if (pInOutVehicle) { pInOutVehicle->CalcAndUpdateCanBeDamagedFlag(); pInOutVehicle->CalcAndUpdateTyresCanBurstFlag(); } + unsigned char ucSeat = m_ucVehicleInOutSeat; + // Reset the vehicle in out stuff so we're ready for another car entry/leave. // Don't allow a new entry/leave until we've gotten the notify return packet ElementID ReasonVehicleID = m_VehicleInOutID; - g_pClientGame->ResetVehicleInOut(); - m_bNoNewVehicleTask = true; - m_NoNewVehicleTaskReasonID = ReasonVehicleID; + ResetVehicleInOut(); -#ifdef MTA_DEBUG - g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_out"); -#endif + if (!pInOutVehicle->IsLocalEntity()) + { + m_bNoNewVehicleTask = true; + m_NoNewVehicleTaskReasonID = ReasonVehicleID; + + #ifdef MTA_DEBUG + g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_out"); + #endif + } + else + { + CClientVehicle::UnpairPedAndVehicle(m_pLocalPlayer, pInOutVehicle); + + CLuaArguments Arguments; + Arguments.PushElement(m_pLocalPlayer); + Arguments.PushNumber(ucSeat); + pInOutVehicle->CallEvent("onClientVehicleExit", Arguments, true); + + Arguments.DeleteArguments(); + Arguments.PushElement(pInOutVehicle); + Arguments.PushNumber(ucSeat); + + m_pLocalPlayer->CallEvent("onClientPlayerVehicleExit", Arguments, true); + } } // Are we still inside the car? - else + else if(!pVehicle->IsLocalEntity()) { // Warp us out now to keep in sync with the server m_pLocalPlayer->RemoveFromVehicle(); @@ -1633,33 +1660,37 @@ void CClientGame::UpdateVehicleInOut() CClientVehicle* pVehicle = m_pLocalPlayer->GetRealOccupiedVehicle(); if (pVehicle) { - // Tell the server that we successfully entered the car - NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - if (pBitStream) + // Check if vehicle isn't local. + if (!pVehicle->IsLocalEntity()) { - // Write the car id and the action id (enter complete) - pBitStream->Write(m_VehicleInOutID); - unsigned char ucAction; - - if (m_bIsJackingVehicle) + // Tell the server that we successfully entered the car + NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); + if (pBitStream) { - ucAction = static_cast(VEHICLE_NOTIFY_JACK); + // Write the car id and the action id (enter complete) + pBitStream->Write(m_VehicleInOutID); + unsigned char ucAction; + + if (m_bIsJackingVehicle) + { + ucAction = static_cast(VEHICLE_NOTIFY_JACK); #ifdef MTA_DEBUG - g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_jack"); + g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_jack"); #endif - } - else - { - ucAction = static_cast(VEHICLE_NOTIFY_IN); + } + else + { + ucAction = static_cast(VEHICLE_NOTIFY_IN); #ifdef MTA_DEBUG - g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_in"); + g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_in"); #endif - } - pBitStream->WriteBits(&ucAction, 4); + } + pBitStream->WriteBits(&ucAction, 4); - // Send it and destroy the packet - g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); - g_pNet->DeallocateNetBitStream(pBitStream); + // Send it and destroy the packet + g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(pBitStream); + } } // Warp ourself in (so we're sure the records are correct) @@ -1682,66 +1713,70 @@ void CClientGame::UpdateVehicleInOut() } else { - // Tell the server that we aborted entered the car - NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - if (pBitStream) + // Check if vehicle isn't local. + if (!pInOutVehicle->IsLocalEntity()) { - // Write the car id and the action id (enter complete) - pBitStream->Write(m_VehicleInOutID); - unsigned char ucAction; - if (m_bIsJackingVehicle) + // Tell the server that we aborted entered the car + NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); + if (pBitStream) { - ucAction = static_cast(VEHICLE_NOTIFY_JACK_ABORT); - pBitStream->WriteBits(&ucAction, 4); - - // Did we start jacking them? - bool bAlreadyStartedJacking = false; - CClientVehicle* pVehicle = DynamicCast(CElementIDs::GetElement(m_VehicleInOutID)); - if (pVehicle) + // Write the car id and the action id (enter complete) + pBitStream->Write(m_VehicleInOutID); + unsigned char ucAction; + if (m_bIsJackingVehicle) { - CClientPed* pJackedPlayer = pVehicle->GetOccupant(); - if (pJackedPlayer) + ucAction = static_cast(VEHICLE_NOTIFY_JACK_ABORT); + pBitStream->WriteBits(&ucAction, 4); + + // Did we start jacking them? + bool bAlreadyStartedJacking = false; + CClientVehicle* pVehicle = DynamicCast(CElementIDs::GetElement(m_VehicleInOutID)); + if (pVehicle) { - // Jax: have we already started to jack the other player? - if (pJackedPlayer->IsGettingJacked()) + CClientPed* pJackedPlayer = pVehicle->GetOccupant(); + if (pJackedPlayer) { - bAlreadyStartedJacking = true; + // Jax: have we already started to jack the other player? + if (pJackedPlayer->IsGettingJacked()) + { + bAlreadyStartedJacking = true; + } } + unsigned char ucDoor = m_pLocalPlayer->m_ucEnteringDoor - 2; + pBitStream->WriteBits(&ucDoor, 3); + SDoorOpenRatioSync door; + door.data.fRatio = pVehicle->GetDoorOpenRatio(m_pLocalPlayer->m_ucEnteringDoor); + pBitStream->Write(&door); } - unsigned char ucDoor = m_pLocalPlayer->m_ucEnteringDoor - 2; - pBitStream->WriteBits(&ucDoor, 3); - SDoorOpenRatioSync door; - door.data.fRatio = pVehicle->GetDoorOpenRatio(m_pLocalPlayer->m_ucEnteringDoor); - pBitStream->Write(&door); - } - pBitStream->WriteBit(bAlreadyStartedJacking); + pBitStream->WriteBit(bAlreadyStartedJacking); #ifdef MTA_DEBUG - g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_jack_abort"); + g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_jack_abort"); #endif - } - else - { - ucAction = static_cast(VEHICLE_NOTIFY_IN_ABORT); - pBitStream->WriteBits(&ucAction, 4); - CClientVehicle* pVehicle = DynamicCast(CElementIDs::GetElement(m_VehicleInOutID)); - if (pVehicle) - { - unsigned char ucDoor = m_pLocalPlayer->m_ucEnteringDoor - 2; - pBitStream->WriteBits(&ucDoor, 3); - SDoorOpenRatioSync door; - door.data.fRatio = pVehicle->GetDoorOpenRatio(m_pLocalPlayer->m_ucEnteringDoor); - pBitStream->Write(&door); } + else + { + ucAction = static_cast(VEHICLE_NOTIFY_IN_ABORT); + pBitStream->WriteBits(&ucAction, 4); + CClientVehicle* pVehicle = DynamicCast(CElementIDs::GetElement(m_VehicleInOutID)); + if (pVehicle) + { + unsigned char ucDoor = m_pLocalPlayer->m_ucEnteringDoor - 2; + pBitStream->WriteBits(&ucDoor, 3); + SDoorOpenRatioSync door; + door.data.fRatio = pVehicle->GetDoorOpenRatio(m_pLocalPlayer->m_ucEnteringDoor); + pBitStream->Write(&door); + } #ifdef MTA_DEBUG - g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_in_abort"); + g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_in_abort"); #endif - } + } - // Send it and destroy the packet - g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); - g_pNet->DeallocateNetBitStream(pBitStream); + // Send it and destroy the packet + g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(pBitStream); + } } // Warp ourself out again (so we're sure the records are correct) @@ -1770,12 +1805,30 @@ void CClientGame::UpdateVehicleInOut() } } + unsigned char ucSeat = m_ucVehicleInOutSeat; + // Reset // Don't allow a new entry/leave until we've gotten the notify return packet ElementID ReasonID = m_VehicleInOutID; ResetVehicleInOut(); - m_bNoNewVehicleTask = true; - m_NoNewVehicleTaskReasonID = ReasonID; + + if (!pInOutVehicle->IsLocalEntity()) + { + m_bNoNewVehicleTask = true; + m_NoNewVehicleTaskReasonID = ReasonID; + // Check if player is actually in the vehicle and didn't abandon entering before firing the event. + } else if(m_pLocalPlayer->IsInVehicle()) { + CLuaArguments Arguments; + Arguments.PushElement(m_pLocalPlayer); + Arguments.PushNumber(ucSeat); + pInOutVehicle->CallEvent("onClientVehicleEnter", Arguments, true); + + Arguments.DeleteArguments(); + Arguments.PushElement(pInOutVehicle); + Arguments.PushNumber(ucSeat); + + m_pLocalPlayer->CallEvent("onClientPlayerVehicleEnter", Arguments, true); + } } } } @@ -1801,19 +1854,45 @@ void CClientGame::UpdateVehicleInOut() // Jax: this happens when we try to warp into a streamed out vehicle, including when we use CClientVehicle::StreamInNow // ..maybe we need a different way to detect bike falls? - // Tell the server - NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - if (pBitStream) + if (!pOccupiedVehicle->IsLocalEntity()) { - // Vehicle id - pBitStream->Write(pOccupiedVehicle->GetID()); - unsigned char ucAction = static_cast(VEHICLE_NOTIFY_FELL_OFF); - pBitStream->WriteBits(&ucAction, 4); + // Tell the server + NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); + if (pBitStream) + { + // Vehicle id + pBitStream->Write(pOccupiedVehicle->GetID()); + unsigned char ucAction = static_cast(VEHICLE_NOTIFY_FELL_OFF); + pBitStream->WriteBits(&ucAction, 4); + + // Send it and destroy the packet + g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(pBitStream); + + /* + // Make it undamagable if we're not syncing it + CDeathmatchVehicle* pInOutVehicle = static_cast < CDeathmatchVehicle* > ( pOccupiedVehicle ); + if ( pInOutVehicle ) + { + if ( pInOutVehicle->IsSyncing () ) + { + pInOutVehicle->SetCanBeDamaged ( true ); + pInOutVehicle->SetTyresCanBurst ( true ); + } + else + { + pInOutVehicle->SetCanBeDamaged ( false ); + pInOutVehicle->SetTyresCanBurst ( false ); + } + } + */ + } + } - // Send it and destroy the packet - g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); - g_pNet->DeallocateNetBitStream(pBitStream); + ResetVehicleInOut(); + if (!pOccupiedVehicle->IsLocalEntity()) + { // We're not allowed to enter any vehicle before we get a confirm m_bNoNewVehicleTask = true; m_NoNewVehicleTaskReasonID = pOccupiedVehicle->GetID(); @@ -1821,27 +1900,24 @@ void CClientGame::UpdateVehicleInOut() // Remove him from the vehicle m_pLocalPlayer->RemoveFromVehicle(); - /* - // Make it undamagable if we're not syncing it - CDeathmatchVehicle* pInOutVehicle = static_cast < CDeathmatchVehicle* > ( pOccupiedVehicle ); - if ( pInOutVehicle ) - { - if ( pInOutVehicle->IsSyncing () ) - { - pInOutVehicle->SetCanBeDamaged ( true ); - pInOutVehicle->SetTyresCanBurst ( true ); - } - else - { - pInOutVehicle->SetCanBeDamaged ( false ); - pInOutVehicle->SetTyresCanBurst ( false ); - } - } - */ + #ifdef MTA_DEBUG + g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_out"); + #endif + } + else + { + CLuaArguments Arguments; + Arguments.PushElement(m_pLocalPlayer); + Arguments.PushNumber(m_pLocalPlayer->GetOccupiedVehicleSeat()); + pOccupiedVehicle->CallEvent("onClientVehicleExit", Arguments, true); -#ifdef MTA_DEBUG - g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_notify_fell_off"); -#endif + Arguments.DeleteArguments(); + Arguments.PushElement(pOccupiedVehicle); + Arguments.PushNumber(m_pLocalPlayer->GetOccupiedVehicleSeat()); + + m_pLocalPlayer->CallEvent("onClientPlayerVehicleExit", Arguments, true); + + CClientVehicle::UnpairPedAndVehicle(m_pLocalPlayer, pOccupiedVehicle); } } } @@ -2286,6 +2362,16 @@ void CClientGame::ChangeVehicleWeapon(bool bNext) void CClientGame::ResetVehicleInOut() { + if (m_pJackingPed != NULL) + { + if (m_pJackingPed->IsLocalEntity()) + { + m_pJackingPed->ResetInOutState(); + } + } + + m_pLocalPlayer->ResetInOutState(); + m_ulLastVehicleInOutTime = 0; m_bIsGettingOutOfVehicle = false; m_bIsGettingIntoVehicle = false; @@ -2296,6 +2382,8 @@ void CClientGame::ResetVehicleInOut() m_bNoNewVehicleTask = false; m_NoNewVehicleTaskReasonID = INVALID_ELEMENT_ID; m_pGettingJackedBy = NULL; + m_pJackingPed = NULL; + m_pLocalPlayer->SetVehicleInOutState(VEHICLE_INOUT_NONE); } void CClientGame::SetAllDimensions(unsigned short usDimension) @@ -2808,6 +2896,10 @@ void CClientGame::AddBuiltInEvents() m_Events.AddEvent("onClientPedChoke", "", NULL, false); m_Events.AddEvent("onClientPedHeliKilled", "heli", NULL, false); m_Events.AddEvent("onClientPedHitByWaterCannon", "vehicle", NULL, false); + m_Events.AddEvent("onClientPedEnterVehicle", "ped, seat, jacking", NULL, false); + m_Events.AddEvent("onClientPedExitVehicle", "ped, seat, jacker", NULL, false); + m_Events.AddEvent("onClientPedStartEnterVehicle", "ped, seat, door, jacking", NULL, false); + m_Events.AddEvent("onClientPedStartExitVehicle", "ped, seat, door, jacker", NULL, false); m_Events.AddEvent("onClientPedStep", "foot", nullptr, false); // Vehicle events @@ -3913,10 +4005,14 @@ void CClientGame::FireHandler(CFire* pFire) void CClientGame::ProjectileInitiateHandler(CClientProjectile* pProjectile) { + CClientEntity* pCreator = pProjectile->GetCreator(); + if (pProjectile->IsLocal()) { + bool bSyncProjectile = true; + // Did the local player create this projectile? - if (m_pLocalPlayer && pProjectile->GetCreator() == m_pLocalPlayer) + if (m_pLocalPlayer && pCreator == m_pLocalPlayer) { // Physics says our projectile should start off at our velocity CVector vecVelocity, vecPlayerVelocity; @@ -3924,16 +4020,30 @@ void CClientGame::ProjectileInitiateHandler(CClientProjectile* pProjectile) m_pLocalPlayer->GetMoveSpeed(vecPlayerVelocity); vecVelocity += vecPlayerVelocity; pProjectile->SetVelocity(vecVelocity); + } + else if(pCreator) + { + if (pCreator->GetType() == CCLIENTVEHICLE) + { + if (pCreator->IsLocalEntity()) + { + // Projectiles created by client-side vehicles should not be synced. + bSyncProjectile = false; + } + } } - SendProjectileSync(pProjectile); + if (bSyncProjectile) + { + SendProjectileSync(pProjectile); + } } // Renew the interior and dimension - if (pProjectile->GetCreator()) + if (pCreator) { - pProjectile->SetInterior(pProjectile->GetCreator()->GetInterior()); - pProjectile->SetDimension(pProjectile->GetCreator()->GetDimension()); + pProjectile->SetInterior(pCreator->GetInterior()); + pProjectile->SetDimension(pCreator->GetDimension()); } // Validate the projectile for our element tree @@ -3941,7 +4051,7 @@ void CClientGame::ProjectileInitiateHandler(CClientProjectile* pProjectile) // Call our creation event CLuaArguments Arguments; - Arguments.PushElement(pProjectile->GetCreator()); + Arguments.PushElement(pCreator); pProjectile->CallEvent("onClientProjectileCreation", Arguments, true); } @@ -4601,6 +4711,14 @@ bool CClientGame::ApplyPedDamageFromGame(eWeaponType weaponUsed, float fDamage, AnimationId animID; GetDeathAnim(pDamagedPed, pEvent, animGroup, animID); pDamagedPed->Kill(weaponUsed, hitZone, false, false, animGroup, animID); + + CClientVehicle* pOccupiedVehicle = pDamagedPed->GetOccupiedVehicle(); + + if (pOccupiedVehicle) + { + pOccupiedVehicle->UnpairPedAndVehicle(pDamagedPed); + } + return true; } if (fPreviousHealth > 0.0f) @@ -4664,8 +4782,20 @@ void CClientGame::DeathHandler(CPed* pKilledPedSA, unsigned char ucDeathReason, pKilledPed->CallEvent("onClientPedWasted", Arguments, true); - // Notify the server - SendPedWastedPacket(pKilledPed, INVALID_ELEMENT_ID, ucDeathReason, ucBodyPart); + if (!pKilledPed->IsLocalEntity()) + { + // Notify the server + SendPedWastedPacket(pKilledPed, INVALID_ELEMENT_ID, ucDeathReason, ucBodyPart); + } + else + { + CClientVehicle* pOccupiedVehicle = pKilledPed->GetOccupiedVehicle(); + + if (pOccupiedVehicle) + { + pOccupiedVehicle->UnpairPedAndVehicle(pKilledPed); + } + } } bool CClientGame::VehicleCollisionHandler(CVehicleSAInterface*& pCollidingVehicle, CEntitySAInterface* pCollidedWith, int iModelIndex, float fDamageImpulseMag, @@ -5095,7 +5225,7 @@ bool CClientGame::ProcessMessage(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lPa return false; } -void CClientGame::ProcessVehicleInOutKey(bool bPassenger) +bool CClientGame::ProcessExitVehicle() { // Are we already sending an in/out request or not allowed to create a new in/out? if (m_bNoNewVehicleTask // Are we permitted to even enter a vehicle? @@ -5104,9 +5234,9 @@ void CClientGame::ProcessVehicleInOutKey(bool bPassenger) || m_bIsGettingIntoVehicle // We can't enter a vehicle we're currently entering... || m_bIsGettingOutOfVehicle // We can't enter a vehicle we're currently leaving... || CClientTime::GetTime() < m_ulLastVehicleInOutTime + VEHICLE_INOUT_DELAY // We are trying to enter the vehicle to soon - ) + ) { - return; + return false; } // Reset the "is jacking" bit @@ -5116,59 +5246,120 @@ void CClientGame::ProcessVehicleInOutKey(bool bPassenger) if (!m_pLocalPlayer) { // No local player. Stop. - return; + return false; } - // If the player is in a vehicle we need to leave the vehicle. CClientVehicle* pOccupiedVehicle = m_pLocalPlayer->GetOccupiedVehicle(); - if (pOccupiedVehicle) + + if (!pOccupiedVehicle) { - // Only let us leave the vehicle if: - // - we press F (as driver) - // - we press either F or G as a passenger - if (bPassenger && m_pLocalPlayer->GetOccupiedVehicleSeat() == 0) - { - // Driver pressed G, so stop. - return; - } + return false; + } - // We're about to exit a vehicle - // Send an in request - NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); - if (!pBitStream) - { - return; - } + unsigned char ucDoor = g_pGame->GetCarEnterExit()->ComputeTargetDoorToExit(m_pLocalPlayer->GetGamePlayer(), pOccupiedVehicle->GetGameVehicle()); + + if (pOccupiedVehicle->IsLocalEntity()) + { + unsigned char ucSeat = m_pLocalPlayer->GetOccupiedVehicleSeat(); + m_VehicleInOutID = pOccupiedVehicle->GetID(); + m_ucVehicleInOutSeat = ucSeat; - // Write the vehicle id to it and that we're requesting to get out of it - pBitStream->Write(pOccupiedVehicle->GetID()); - unsigned char ucAction = static_cast(VEHICLE_REQUEST_OUT); - pBitStream->WriteBits(&ucAction, 4); + m_VehicleInOutID = pOccupiedVehicle->GetID(); + m_bIsGettingOutOfVehicle = true; + m_ulLastVehicleInOutTime = CClientTime::GetTime(); - unsigned char ucDoor = g_pGame->GetCarEnterExit()->ComputeTargetDoorToExit(m_pLocalPlayer->GetGamePlayer(), pOccupiedVehicle->GetGameVehicle()); if (ucDoor >= 2 && ucDoor <= 5) { ucDoor -= 2; - pBitStream->WriteBits(&ucDoor, 2); } - // Send and destroy it - g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); - g_pNet->DeallocateNetBitStream(pBitStream); + m_pLocalPlayer->GetOutOfVehicle(ucDoor); + m_pLocalPlayer->SetVehicleInOutState(VEHICLE_INOUT_GETTING_OUT); - // We're now exiting a vehicle - m_bIsGettingOutOfVehicle = true; - m_ulLastVehicleInOutTime = CClientTime::GetTime(); + CLuaArguments Arguments; + Arguments.PushElement(m_pLocalPlayer); + Arguments.PushNumber(ucSeat); + Arguments.PushNumber(ucDoor); + pOccupiedVehicle->CallEvent("onClientVehicleStartExit", Arguments, true); + + return true; + } + + // We're about to exit a vehicle + // Send an in request + NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); + if (!pBitStream) + { + return false; + } + + // Write the vehicle id to it and that we're requesting to get out of it + pBitStream->Write(pOccupiedVehicle->GetID()); + unsigned char ucAction = static_cast(VEHICLE_REQUEST_OUT); + pBitStream->WriteBits(&ucAction, 4); + + if (ucDoor >= 2 && ucDoor <= 5) + { + ucDoor -= 2; + pBitStream->WriteBits(&ucDoor, 2); + } + + // Send and destroy it + g_pNet->SendPacket(PACKET_ID_VEHICLE_INOUT, pBitStream, PACKET_PRIORITY_HIGH, PACKET_RELIABILITY_RELIABLE_ORDERED); + g_pNet->DeallocateNetBitStream(pBitStream); + + // We're now exiting a vehicle + m_bIsGettingOutOfVehicle = true; + m_ulLastVehicleInOutTime = CClientTime::GetTime(); #ifdef MTA_DEBUG - g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_request_out"); + g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_request_out"); #endif - return; + + return true; +} + +bool CClientGame::ProcessEnterVehicle(CClientVehicle* pVehicle, unsigned int uiSeat, unsigned int uiDoor) +{ + // Are we already sending an in/out request or not allowed to create a new in/out? + if (m_bNoNewVehicleTask // Are we permitted to even enter a vehicle? + || m_VehicleInOutID != INVALID_ELEMENT_ID // Make sure we're not already processing a vehicle enter (would refer to valid ID if we were) + || m_bIsGettingJacked // Make sure we're not currently getting carjacked && + || m_bIsGettingIntoVehicle // We can't enter a vehicle we're currently entering... + || m_bIsGettingOutOfVehicle // We can't enter a vehicle we're currently leaving... + || CClientTime::GetTime() < m_ulLastVehicleInOutTime + VEHICLE_INOUT_DELAY // We are trying to enter the vehicle to soon + ) + { + return false; } - // - // We're going to enter a vehicle - // + // Reset the "is jacking" bit + m_bIsJackingVehicle = false; + + // Got a local player model? + if (!m_pLocalPlayer) + { + // No local player. Stop. + return false; + } + + if (!pVehicle || !pVehicle->IsEnterable()) + { + // Stop if there isn't a vehicle, or the vehicle is not enterable + return false; + } + + CVector vecPlayerPosition; + CVector vecVehiclePosition; + + m_pLocalPlayer->GetPosition(vecPlayerPosition); + pVehicle->GetPosition(vecVehiclePosition); + + // Check if we are too far away from the vehicle. + if (DistanceBetweenPoints3D(vecPlayerPosition, vecVehiclePosition) > 20.0f) + { + return false; + } // If the Jump task is playing and we are in water - I know right // Kill the task. @@ -5176,10 +5367,9 @@ void CClientGame::ProcessVehicleInOutKey(bool bPassenger) CTask* pTask = m_pLocalPlayer->GetCurrentPrimaryTask(); if (pTask && pTask->GetTaskType() == TASK_COMPLEX_JUMP) // Kill jump task - breaks warp in entry and doesn't really matter { - CClientVehicle* pVehicle = m_pLocalPlayer->GetClosestVehicleInRange(true, !bPassenger, bPassenger, false, nullptr, nullptr, 20.0f); if (pVehicle && (pVehicle->IsInWater() || - m_pLocalPlayer->IsInWater())) // Make sure we are about to warp in (this bug only happens when someone jumps into water with a vehicle) + m_pLocalPlayer->IsInWater())) // Make sure we are about to warp in (this bug only happens when someone jumps into water with a vehicle) { m_pLocalPlayer->KillTask(3, true); // Kill jump task if we are about to warp in } @@ -5191,7 +5381,7 @@ void CClientGame::ProcessVehicleInOutKey(bool bPassenger) if (m_pLocalPlayer->GetCurrentPrimaryTask()) { // We already have a primary task, so stop. - return; + return false; } // Are we holding the aim_weapon key? @@ -5199,45 +5389,22 @@ void CClientGame::ProcessVehicleInOutKey(bool bPassenger) if (pBind && pBind->bState) { // Stop because the player is probably using rshift + f/g - return; + return false; } if (m_pLocalPlayer->IsClimbing() // Make sure we're not currently climbing || m_pLocalPlayer->HasJetPack() // Make sure we don't have a jetpack || m_pLocalPlayer->IsUsingGun() // Make sure we're not using a gun (have the gun task active) - we stop it in UpdatePlayerTasks anyway || m_pLocalPlayer->IsRunningAnimation() // Make sure we aren't running an animation - ) - { - return; - } - - // Grab the closest vehicle - unsigned int uiDoor = 0; - CClientVehicle* pVehicle = m_pLocalPlayer->GetClosestVehicleInRange(true, !bPassenger, bPassenger, false, &uiDoor, nullptr, 20.0f); - unsigned int uiSeat = uiDoor; - - if (bPassenger && uiDoor == 0) - { - // We're trying to enter as a passenger, yet our closest door - // is the driver's door. Force an enter for the passenger seat. - uiSeat = 1; - } - else if (!bPassenger) + ) { - // We want to drive. Force our seat to the driver's seat. - uiSeat = 0; - } - - if (!pVehicle || !pVehicle->IsEnterable()) - { - // Stop if there isn't a vehicle, or the vehicle is not enterable - return; + return false; } // If the vehicle's a boat, make sure we're standing on it (we need a dif task to enter boats properly) if (pVehicle->GetVehicleType() == CLIENTVEHICLE_BOAT && m_pLocalPlayer->GetContactEntity() != pVehicle) { - return; + return false; } // Call the onClientVehicleStartEnter event for remote players @@ -5250,14 +5417,67 @@ void CClientGame::ProcessVehicleInOutKey(bool bPassenger) if (!pVehicle->CallEvent("onClientVehicleStartEnter", Arguments, true)) { // Event has been cancelled - return; + return false; + } + + if (pVehicle->IsLocalEntity()) + { + CClientPed* pOccupyingPed = pVehicle->GetOccupant(uiSeat); + + if (pOccupyingPed) + { + unsigned char ucOccupyingDoor = g_pGame->GetCarEnterExit()->ComputeTargetDoorToExit(pOccupyingPed->GetGamePlayer(), pVehicle->GetGameVehicle()); + unsigned char ucOccupyingSeat = pOccupyingPed->GetOccupiedVehicleSeat(); + + if (ucOccupyingDoor >= 2 && ucOccupyingDoor <= 5) + { + ucOccupyingDoor -= 2; + } + + CLuaArguments ExitArguments; + ExitArguments.PushElement(pOccupyingPed); // ped + ExitArguments.PushNumber(ucOccupyingSeat); // seat + ExitArguments.PushNumber(ucOccupyingDoor); // door + + ExitArguments.PushElement(m_pLocalPlayer); // ped that is carjacking. + + if (!pVehicle->CallEvent("onClientPedStartExitVehicle", ExitArguments, true)) + { + // Event has been cancelled + return false; + } + } + + m_VehicleInOutID = pVehicle->GetID(); + m_ucVehicleInOutSeat = uiSeat; + + m_bIsGettingIntoVehicle = true; + m_ulLastVehicleInOutTime = CClientTime::GetTime(); + + m_pLocalPlayer->GetIntoVehicle(pVehicle, uiSeat, uiDoor); + + if (pOccupyingPed) { + m_bIsJackingVehicle = true; + m_pJackingPed = pOccupyingPed; + + pOccupyingPed->SetJacker(m_pLocalPlayer); + m_pLocalPlayer->SetVehicleInOutState(VEHICLE_INOUT_JACKING); + pOccupyingPed->SetVehicleInOutState(VEHICLE_INOUT_GETTING_JACKED); + + CClientVehicle::SetPedOccupiedVehicle(pOccupyingPed, pVehicle, uiSeat, uiDoor); + CClientVehicle::SetPedOccupyingVehicle(pOccupyingPed, pVehicle, uiSeat, uiDoor); + } else { + m_pLocalPlayer->SetVehicleInOutState(VEHICLE_INOUT_GETTING_IN); + } + + return true; } // Send an in request NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); if (!pBitStream) { - return; + return false; } // Write the vehicle id to it and that we're requesting to get into it @@ -5282,6 +5502,111 @@ void CClientGame::ProcessVehicleInOutKey(bool bPassenger) #ifdef MTA_DEBUG g_pCore->GetConsole()->Printf("* Sent_InOut: vehicle_request_in"); #endif + + return true; +} + +void CClientGame::ProcessVehicleInOutKey(bool bPassenger) +{ + // Are we already sending an in/out request or not allowed to create a new in/out? + if (m_bNoNewVehicleTask // Are we permitted to even enter a vehicle? + || m_VehicleInOutID != INVALID_ELEMENT_ID // Make sure we're not already processing a vehicle enter (would refer to valid ID if we were) + || m_bIsGettingJacked // Make sure we're not currently getting carjacked && + || m_bIsGettingIntoVehicle // We can't enter a vehicle we're currently entering... + || m_bIsGettingOutOfVehicle // We can't enter a vehicle we're currently leaving... + || CClientTime::GetTime() < m_ulLastVehicleInOutTime + VEHICLE_INOUT_DELAY // We are trying to enter the vehicle to soon + ) + { + return; + } + + // Got a local player model? + if (!m_pLocalPlayer) + { + // No local player. Stop. + return; + } + + // If the player is in a vehicle we need to leave the vehicle. + CClientVehicle* pOccupiedVehicle = m_pLocalPlayer->GetOccupiedVehicle(); + if (pOccupiedVehicle) + { + // Only let us leave the vehicle if: + // - we press F (as driver) + // - we press either F or G as a passenger + if (bPassenger && m_pLocalPlayer->GetOccupiedVehicleSeat() == 0) + { + // Driver pressed G, so stop. + return; + } + + ProcessExitVehicle(); + return; + } + + // + // We're going to enter a vehicle + // + + // If the Jump task is playing and we are in water - I know right + // Kill the task. + // + CTask* pTask = m_pLocalPlayer->GetCurrentPrimaryTask(); + if (pTask && pTask->GetTaskType() == TASK_COMPLEX_JUMP) // Kill jump task - breaks warp in entry and doesn't really matter + { + CClientVehicle* pVehicle = m_pLocalPlayer->GetClosestVehicleInRange(true, !bPassenger, bPassenger, false, nullptr, nullptr, 20.0f); + if (pVehicle && + (pVehicle->IsInWater() || + m_pLocalPlayer->IsInWater())) // Make sure we are about to warp in (this bug only happens when someone jumps into water with a vehicle) + { + m_pLocalPlayer->KillTask(3, true); // Kill jump task if we are about to warp in + } + } + + // Make sure we don't have any other primary tasks running, otherwise our 'enter-vehicle' + // task will replace it and fuck it up! + // + if (m_pLocalPlayer->GetCurrentPrimaryTask()) + { + // We already have a primary task, so stop. + return; + } + + // Are we holding the aim_weapon key? + SBindableGTAControl* pBind = g_pCore->GetKeyBinds()->GetBindableFromControl("aim_weapon"); + if (pBind && pBind->bState) + { + // Stop because the player is probably using rshift + f/g + return; + } + + if (m_pLocalPlayer->IsClimbing() // Make sure we're not currently climbing + || m_pLocalPlayer->HasJetPack() // Make sure we don't have a jetpack + || m_pLocalPlayer->IsUsingGun() // Make sure we're not using a gun (have the gun task active) - we stop it in UpdatePlayerTasks anyway + || m_pLocalPlayer->IsRunningAnimation() // Make sure we aren't running an animation + ) + { + return; + } + + // Grab the closest vehicle + unsigned int uiDoor = 0; + CClientVehicle* pVehicle = m_pLocalPlayer->GetClosestVehicleInRange(true, !bPassenger, bPassenger, false, &uiDoor, nullptr, 20.0f); + unsigned int uiSeat = uiDoor; + + if (bPassenger && uiDoor == 0) + { + // We're trying to enter as a passenger, yet our closest door + // is the driver's door. Force an enter for the passenger seat. + uiSeat = 1; + } + else if (!bPassenger) + { + // We want to drive. Force our seat to the driver's seat. + uiSeat = 0; + } + + ProcessEnterVehicle(pVehicle, uiSeat, uiDoor); } // Shot compensation (Jax): diff --git a/Client/mods/deathmatch/logic/CClientGame.h b/Client/mods/deathmatch/logic/CClientGame.h index 73dc73e13e7..0b1eccf81e1 100644 --- a/Client/mods/deathmatch/logic/CClientGame.h +++ b/Client/mods/deathmatch/logic/CClientGame.h @@ -345,6 +345,8 @@ class CClientGame bool GetDamageSent() { return m_bDamageSent; } void SetDamageSent(bool b) { m_bDamageSent = b; } + bool ProcessExitVehicle(); + bool ProcessEnterVehicle(CClientVehicle* pVehicle, unsigned int uiSeat, unsigned int uiDoor); void ProcessVehicleInOutKey(bool bPassenger); void ResetVehicleInOut(); @@ -605,6 +607,14 @@ class CClientGame void SendFireSync(CFire* pFire); void SendProjectileSync(CClientProjectile* pProjectile); + void SetGettingOutOfVehicle(bool bGettingOutOfVehicle) { m_bIsGettingOutOfVehicle = bGettingOutOfVehicle; }; + void SetGettingIntoVehicle(bool bGettingIntoVehicle) { m_bIsGettingIntoVehicle = bGettingIntoVehicle; }; + void SetVehicleInOutID(ElementID vehicleInOutID) { m_VehicleInOutID = vehicleInOutID; }; + void SetVehicleInOutSeat(unsigned char ucVehicleInOutSeat) { m_ucVehicleInOutSeat = ucVehicleInOutSeat; }; + + void SetGettingJacked(bool bIsGettingJacked) { m_bIsGettingJacked = bIsGettingJacked; }; + void SetGettingJackedBy(CClientPed* pJacker) { m_pGettingJackedBy = pJacker; }; + void SetServerVersionSortable(const SString& strVersion) { m_strServerVersionSortable = strVersion; } const SString& GetServerVersionSortable() { return m_strServerVersionSortable; } @@ -729,13 +739,14 @@ class CClientGame unsigned long m_ulLastVehicleInOutTime; bool m_bIsGettingOutOfVehicle; bool m_bIsGettingIntoVehicle; + CClientPed* m_pJackingPed; bool m_bIsJackingVehicle; bool m_bIsGettingJacked; ElementID m_VehicleInOutID; unsigned char m_ucVehicleInOutSeat; bool m_bNoNewVehicleTask; ElementID m_NoNewVehicleTaskReasonID; - CClientPlayer* m_pGettingJackedBy; + CClientPed* m_pGettingJackedBy; CEntity* m_pTargetedGameEntity; CClientEntity* m_pTargetedEntity; diff --git a/Client/mods/deathmatch/logic/CClientPed.cpp b/Client/mods/deathmatch/logic/CClientPed.cpp index 6b53f77e1d6..479dffedd14 100644 --- a/Client/mods/deathmatch/logic/CClientPed.cpp +++ b/Client/mods/deathmatch/logic/CClientPed.cpp @@ -1098,7 +1098,7 @@ CClientVehicle* CClientPed::GetRealOccupiedVehicle() CClientVehicle* CClientPed::GetClosestVehicleInRange(bool bGetPositionFromClosestDoor, bool bCheckDriverDoor, bool bCheckPassengerDoors, bool bCheckStreamedOutVehicles, unsigned int* uiClosestDoor, CVector* pClosestDoorPosition, - float fWithinRange) + float fWithinRange, bool bLocalOnly) { if (bGetPositionFromClosestDoor) { @@ -1135,6 +1135,9 @@ CClientVehicle* CClientPed::GetClosestVehicleInRange(bool bGetPositionFromCloses if (!pGameVehicle && bGetPositionFromClosestDoor) continue; + if (bLocalOnly && !pTempVehicle->IsLocalEntity()) + continue; + // Should we take the position from the closest door instead of center of vehicle if (bGetPositionFromClosestDoor && pTempVehicle->GetModel() != VT_RCBARON) { @@ -1158,10 +1161,10 @@ CClientVehicle* CClientPed::GetClosestVehicleInRange(bool bGetPositionFromCloses iClosestDoor = iPassengerDoor; vecClosestDoorPosition = vecVehiclePosition = vecPassengerPos; } - if (bCheckDriverDoor) + if (bCheckDriverDoor && bCheckPassengerDoors) { // If they're different, find the closest - if (iFrontDoor != iPassengerDoor && iPassengerDoor < 2) + if (iFrontDoor != iPassengerDoor) { float fDistanceFromFront = DistanceBetweenPoints3D(vecPosition, vecFrontPos); float fDistanceFromPassenger = DistanceBetweenPoints3D(vecPosition, vecPassengerPos); @@ -1169,6 +1172,9 @@ CClientVehicle* CClientPed::GetClosestVehicleInRange(bool bGetPositionFromCloses { iClosestDoor = iPassengerDoor; vecClosestDoorPosition = vecVehiclePosition = vecPassengerPos; + } else { + iClosestDoor = iFrontDoor; + vecClosestDoorPosition = vecFrontPos; } } } @@ -1252,7 +1258,7 @@ bool CClientPed::GetClosestDoor(CClientVehicle* pVehicle, bool bCheckDriverDoor, iClosestDoor = iPassengerDoor; vecClosestDoorPosition = vecPassengerPos; } - if (bCheckDriverDoor) + if (bCheckDriverDoor && bCheckPassengerDoors) { // If they're different, find the closest if (iFrontDoor != iPassengerDoor) @@ -1263,6 +1269,9 @@ bool CClientPed::GetClosestDoor(CClientVehicle* pVehicle, bool bCheckDriverDoor, { iClosestDoor = iPassengerDoor; vecClosestDoorPosition = vecPassengerPos; + } else { + iClosestDoor = iFrontDoor; + vecClosestDoorPosition = vecFrontPos; } } } @@ -1291,6 +1300,225 @@ bool CClientPed::GetClosestDoor(CClientVehicle* pVehicle, bool bCheckDriverDoor, return false; } +void CClientPed::ResetInOutState() +{ + m_pInOutVehicle = NULL; + m_bIsEnteringVehicle = false; + m_bIsLeavingVehicle = false; + m_uiInOutSeat = NULL; + m_ucLeavingDoor = NULL; + m_bForceGettingIn = false; + m_pEnteringVehicle = NULL; + + CClientPed* pJackingPed = GetJacking(); + + if (pJackingPed != NULL) + { + if (pJackingPed->IsLocalPlayer()) + { + g_pClientGame->ResetVehicleInOut(); + } + else + { + pJackingPed->ResetInOutState(); + } + } + + SetJacker(NULL); + SetJacking(NULL); +} + +bool CClientPed::GracefullyEnterCar(CClientVehicle* pVehicle, unsigned int uiSeat, unsigned int uiDoor) +{ + // Check if we are already entering a vehicle or are already in a vehicle. + // Player leaves should be handled in CClientGame instead. + if (IsEnteringVehicle() || IsInVehicle() || IsLocalPlayer() || !IsLocalEntity()) + { + return false; + } + + if (!pVehicle || !pVehicle->IsEnterable()) + { + // Stop if there isn't a vehicle, or the vehicle is not enterable + return false; + } + + CVector vecPedPosition; + CVector vecVehiclePosition; + + GetPosition(vecPedPosition); + pVehicle->GetPosition(vecVehiclePosition); + + // Check if the is too far away from the vehicle. + if (DistanceBetweenPoints3D(vecPedPosition, vecVehiclePosition) > 20.0f) + { + return false; + } + + // Make sure the ped doesn't have any other primary tasks running, otherwise our 'enter-vehicle' + // task will replace it and fuck it up! + // + if (GetCurrentPrimaryTask()) + { + // We already have a primary task, so stop. + return false; + } + + if (IsClimbing() // Make sure we're not currently climbing + || HasJetPack() // Make sure we don't have a jetpack + || IsUsingGun() // Make sure we're not using a gun (have the gun task active) + || IsRunningAnimation() // Make sure we aren't running an animation + ) + { + return false; + } + + // If the vehicle's a boat, make sure we're standing on it (we need a dif task to enter boats properly) + if (pVehicle->GetVehicleType() == CLIENTVEHICLE_BOAT && GetContactEntity() != pVehicle) + { + return false; + } + + m_ucEnteringDoor = uiDoor; + m_uiInOutSeat = uiSeat; + + CClientPed* pOccupyingPed = pVehicle->GetOccupant(uiSeat); + + CLuaArguments Arguments; + Arguments.PushElement(this); // ped + Arguments.PushNumber(uiSeat); // seat + Arguments.PushNumber(uiDoor); // Door + + if (pOccupyingPed) + { + Arguments.PushElement(pOccupyingPed); // ped being jacked + } + + if (!this->CallEvent("onClientPedStartEnterVehicle", Arguments, true)) + { + // Event has been cancelled + return false; + } + + if (pOccupyingPed) + { + unsigned char ucOccupyingDoor = g_pGame->GetCarEnterExit()->ComputeTargetDoorToExit(pOccupyingPed->GetGamePlayer(), pVehicle->GetGameVehicle()); + unsigned char ucOccupyingSeat = pOccupyingPed->GetOccupiedVehicleSeat(); + + if (ucOccupyingDoor >= 2 && ucOccupyingDoor <= 5) + { + ucOccupyingDoor -= 2; + } + + CLuaArguments ExitArguments; + ExitArguments.PushElement(pOccupyingPed); // ped + ExitArguments.PushNumber(ucOccupyingSeat); // seat + ExitArguments.PushNumber(ucOccupyingDoor); // door + + if (pOccupyingPed->IsLocalPlayer()) + { + if (!pVehicle->CallEvent("onClientVehicleStartExit", ExitArguments, true)) + { + // Event has been cancelled + return false; + } + } + else + { + ExitArguments.PushElement(this); // ped that is carjacking. + + if (!this->CallEvent("onClientPedStartExitVehicle", ExitArguments, true)) + { + // Event has been cancelled + return false; + } + } + } + + SetEnteringVehicle(true); + SetInOutVehicle(pVehicle); + + GetIntoVehicle(pVehicle, uiSeat, uiDoor); + + if (pOccupyingPed) { + SetVehicleInOutState(VEHICLE_INOUT_JACKING); + pOccupyingPed->SetVehicleInOutState(VEHICLE_INOUT_GETTING_JACKED); + + if (pOccupyingPed->IsLocalPlayer()) + { + g_pClientGame->SetGettingJacked(true); + g_pClientGame->SetGettingJackedBy(this); + g_pClientGame->SetGettingOutOfVehicle(true); + g_pClientGame->SetVehicleInOutID(pVehicle->GetID()); + g_pClientGame->SetVehicleInOutSeat(uiSeat); + } + else + { + pOccupyingPed->SetJacker(this); + } + + SetJacking(pOccupyingPed); + + // We haven't yet entered the car. The ped being carjacked is still occopying it. + // Workaround for early-unpair issue. + CClientVehicle::SetPedOccupiedVehicle(pOccupyingPed, pVehicle, uiSeat, uiDoor); + CClientVehicle::SetPedOccupyingVehicle(pOccupyingPed, pVehicle, uiSeat, uiDoor); + + + } else { + SetVehicleInOutState(VEHICLE_INOUT_GETTING_IN); + } + + return true; +} + +bool CClientPed::GracefullyExitCar() +{ + // Check if we are already leaving a vehicle or aren't in a vehicle. + // Player leaves should be handled in CClientGame instead. + if (IsLeavingVehicle() || !IsInVehicle() || IsLocalPlayer() || !IsLocalEntity()) + { + return false; + } + + CClientVehicle* pOccupiedVehicle = GetOccupiedVehicle(); + + if (!pOccupiedVehicle) + { + return false; + } + + unsigned char ucDoor = g_pGame->GetCarEnterExit()->ComputeTargetDoorToExit(GetGamePlayer(), pOccupiedVehicle->GetGameVehicle()); + unsigned char ucSeat = GetOccupiedVehicleSeat(); + + if (ucDoor >= 2 && ucDoor <= 5) + { + ucDoor -= 2; + } + + CLuaArguments Arguments; + Arguments.PushElement(this); // ped + Arguments.PushNumber(ucSeat); // seat + Arguments.PushNumber(ucDoor); // Door + + if (!this->CallEvent("onClientPedStartExitVehicle", Arguments, true)) + { + // Event has been cancelled + return false; + } + + m_ucLeavingDoor = ucDoor; + m_uiInOutSeat = GetOccupiedVehicleSeat(); + + SetLeavingVehicle(true); + SetInOutVehicle(pOccupiedVehicle); + + GetOutOfVehicle(ucDoor); + SetVehicleInOutState(VEHICLE_INOUT_GETTING_OUT); + + return true; +} + void CClientPed::GetOutOfVehicle(unsigned char ucDoor) { if (ucDoor != 0xFF) @@ -1340,6 +1568,7 @@ void CClientPed::GetIntoVehicle(CClientVehicle* pVehicle, unsigned int uiSeat, u // Do it _GetIntoVehicle(pVehicle, uiSeat, ucDoor); + m_pEnteringVehicle = pVehicle; m_uiOccupiedVehicleSeat = uiSeat; m_ucEnteringDoor = ucDoor; m_bForceGettingIn = true; @@ -2644,6 +2873,88 @@ void CClientPed::StreamedInPulse(bool bDoStandardPulses) // ControllerState checks and fixes are done at the same same as everything else unless using alt pulse order bool bDoControllerStateFixPulse = g_pClientGame->IsUsingAlternatePulseOrder() ? !bDoStandardPulses : bDoStandardPulses; + if (IsLocalEntity()) + { + // Are we being carjacked? + if (!m_bIsLeavingVehicle && (IsGettingJacked() || IsLeavingVehicle())) + { + CClientPed* pJacker = GetJacker(); + CClientVehicle* pOccupiedVehicle = GetOccupiedVehicle(); + + if (pJacker && pOccupiedVehicle) + { + unsigned char ucDoor = g_pGame->GetCarEnterExit()->ComputeTargetDoorToExit(GetGamePlayer(), pOccupiedVehicle->GetGameVehicle()); + unsigned char ucSeat = GetOccupiedVehicleSeat(); + + if (ucDoor >= 2 && ucDoor <= 5) + { + ucDoor -= 2; + } + + m_ucLeavingDoor = ucDoor; + m_uiInOutSeat = GetOccupiedVehicleSeat(); + + SetLeavingVehicle(true); + SetInOutVehicle(pOccupiedVehicle); + + SetVehicleInOutState(VEHICLE_INOUT_GETTING_OUT); + } + } + + if (m_bIsLeavingVehicle && !IsLeavingVehicle() && !IsGettingJacked() && !IsGettingOutOfVehicle() && m_pInOutVehicle != NULL) + { + m_pInOutVehicle->UnpairPedAndVehicle(this); + m_pOccupyingVehicle = NULL; + + m_pInOutVehicle->CalcAndUpdateCanBeDamagedFlag(); + m_pInOutVehicle->CalcAndUpdateTyresCanBurstFlag(); + + CLuaArguments Arguments; + Arguments.PushElement(this); // ped + Arguments.PushNumber(m_uiInOutSeat); // seat + + if (GetJacker()) + { + Arguments.PushElement(GetJacker()); // ped who has carjacked us + } + + this->CallEvent("onClientPedExitVehicle", Arguments, true); + + ResetInOutState(); + } + + if (m_bIsEnteringVehicle && !IsEnteringVehicle() && !IsGettingIntoVehicle() && m_pInOutVehicle != NULL) + { + // Only update the occupying state if we are actually inside the vehicle. + if (GetRealOccupiedVehicle()) { + CClientVehicle::SetPedOccupiedVehicle(this, m_pInOutVehicle, m_uiInOutSeat, m_ucEnteringDoor); + CClientVehicle::SetPedOccupyingVehicle(this, m_pInOutVehicle, m_uiInOutSeat, m_ucEnteringDoor); + + m_pInOutVehicle->CalcAndUpdateCanBeDamagedFlag(); + m_pInOutVehicle->CalcAndUpdateTyresCanBurstFlag(); + + CLuaArguments Arguments; + Arguments.PushElement(this); // ped + Arguments.PushNumber(m_uiInOutSeat); // seat + + if (GetJacking()) + { + Arguments.PushElement(GetJacking()); // ped we have carjacked + } + + this->CallEvent("onClientPedEnterVehicle", Arguments, true); + } + else + { + // Seems like we somehow abandoned getting into the vehicle. + // For safety reasons let's make sure the ped knows this too. + KillTask(TASK_PRIORITY_PRIMARY, true); + } + + ResetInOutState(); + } + } + if (!bDoStandardPulses) { if (bDoControllerStateFixPulse) @@ -4123,6 +4434,13 @@ void CClientPed::StreamIn(bool bInstantly) void CClientPed::StreamOut() { + // Is the ped being streamed out a local ped? + if (IsLocalEntity() && !m_bIsLocalPlayer) + { + // Yes, let's reset the vehicle in/out state + ResetInOutState(); + } + // Make sure we have a player ped and that we're not // the local player if (m_pPlayerPed && !m_bIsLocalPlayer) @@ -4466,6 +4784,7 @@ void CClientPed::_GetIntoVehicle(CClientVehicle* pVehicle, unsigned int uiSeat, { // Create and set the get-in task CTaskComplexEnterCarAsDriver* pInTask = g_pGame->GetTasks()->CreateTaskComplexEnterCarAsDriver(pGameVehicle); + if (pInTask) { pInTask->SetTargetDoor(ucDoor); diff --git a/Client/mods/deathmatch/logic/CClientPed.h b/Client/mods/deathmatch/logic/CClientPed.h index 4218d80224f..31999b4ce3f 100644 --- a/Client/mods/deathmatch/logic/CClientPed.h +++ b/Client/mods/deathmatch/logic/CClientPed.h @@ -239,13 +239,18 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule CClientVehicle* GetRealOccupiedVehicle(); CClientVehicle* GetClosestVehicleInRange(bool bGetPositionFromClosestDoor, bool bCheckDriverDoor, bool bCheckPassengerDoors, bool bCheckStreamedOutVehicles, - unsigned int* uiClosestDoor = NULL, CVector* pClosestDoorPosition = NULL, float fWithinRange = 6000.0f); + unsigned int* uiClosestDoor = NULL, CVector* pClosestDoorPosition = NULL, float fWithinRange = 6000.0f, bool bLocalOnly = false); bool GetClosestDoor(CClientVehicle* pVehicle, bool bCheckDriverDoor, bool bCheckPassengerDoors, unsigned int& uiClosestDoor, CVector* pClosestDoorPosition = NULL); void GetIntoVehicle(CClientVehicle* pVehicle, unsigned int uiSeat = 0, unsigned char ucDoor = 0); void GetOutOfVehicle(unsigned char ucDoor); + void ResetInOutState(); + + bool GracefullyEnterCar(CClientVehicle* pVehicle, unsigned int uiSeat, unsigned int uiDoor); + bool GracefullyExitCar(); + void WarpIntoVehicle(CClientVehicle* pVehicle, unsigned int uiSeat = 0); CClientVehicle* RemoveFromVehicle(bool bIgnoreIfGettingOut = false); @@ -368,6 +373,13 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule bool IsInWater(); bool IsOnGround(); + void SetJacker(CClientPed* pJacker) { m_pJacker = pJacker; }; + void SetJacking(CClientPed* pJacking) { m_pJacking = pJacking; }; + + void SetEnteringVehicle(bool bIsEnteringVehicle) { m_bIsEnteringVehicle -= bIsEnteringVehicle; }; + void SetLeavingVehicle(bool bIsLeavingVehicle) { m_bIsLeavingVehicle = bIsLeavingVehicle; }; + void SetInOutVehicle(CClientVehicle* pVehicle) { m_pInOutVehicle = pVehicle; }; + bool IsClimbing(); bool IsRadioOn() { return m_bRadioOn; }; void NextRadioChannel(); @@ -404,6 +416,9 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule bool IsGettingOutOfVehicle(); bool IsGettingJacked(); + CClientPed* GetJacker() { return m_pJacker; }; + CClientPed* GetJacking() { return m_pJacking; }; + CClientEntity* GetContactEntity(); bool HasAkimboPointingUpwards(); @@ -569,6 +584,10 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule void SetTaskTypeToBeRestoredOnAnimEnd(eTaskType taskType) { m_eTaskTypeToBeRestoredOnAnimEnd = taskType; } eTaskType GetTaskTypeToBeRestoredOnAnimEnd() { return m_eTaskTypeToBeRestoredOnAnimEnd; } + CClientVehicle* GetTargetVehicle() { return m_pEnteringVehicle; } + unsigned char GetTargetDoor() { return m_ucEnteringDoor; } + unsigned int GetTargetSeat() { return m_uiOccupiedVehicleSeat; } + bool IsWarpInToVehicleRequired() { return m_bWarpInToVehicleRequired; } void SetWarpInToVehicleRequired(bool warp) { m_bWarpInToVehicleRequired = warp; } @@ -611,6 +630,12 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule unsigned long m_ulLastTimeSprintPressed; unsigned long m_ulBlockSprintReleaseTime; bool m_bWasSprintButtonDown; + CClientPed* m_pJacking; + CClientPed* m_pJacker; + bool m_bIsEnteringVehicle; + bool m_bIsLeavingVehicle; + unsigned int m_uiInOutSeat; + CClientVehicle* m_pInOutVehicle; CModelInfo* m_pLoadedModelInfo; eWeaponSlot m_pOutOfVehicleWeaponSlot; float m_fBeginAimX; @@ -687,6 +712,7 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule float m_fLighting; unsigned char m_ucEnteringDoor; unsigned char m_ucLeavingDoor; + CClientVehicle* m_pEnteringVehicle; bool m_bPendingRebuildPlayer; uint m_uiFrameLastRebuildPlayer; diff --git a/Client/mods/deathmatch/logic/CClientVehicle.cpp b/Client/mods/deathmatch/logic/CClientVehicle.cpp index 43372863054..52ce082d6f2 100644 --- a/Client/mods/deathmatch/logic/CClientVehicle.cpp +++ b/Client/mods/deathmatch/logic/CClientVehicle.cpp @@ -1165,6 +1165,9 @@ void CClientVehicle::CalcAndUpdateCanBeDamagedFlag() if (m_bSyncUnoccupiedDamage) bCanBeDamaged = true; + if (IsLocalEntity()) + bCanBeDamaged = true; + // Script override if (!m_bScriptCanBeDamaged) bCanBeDamaged = false; @@ -1214,6 +1217,9 @@ void CClientVehicle::CalcAndUpdateTyresCanBurstFlag() if (m_bSyncUnoccupiedDamage) bTyresCanBurst = true; + if (IsLocalEntity()) + bTyresCanBurst = true; + // Script override if (!m_bScriptCanBeDamaged) bTyresCanBurst = false; @@ -3917,17 +3923,13 @@ bool CClientVehicle::IsEnterable() { if (m_pVehicle) { - // Server vehicle? - if (!IsLocalEntity()) + if (GetHealth() > 0.0f) { - if (GetHealth() > 0.0f) + if (!IsInWater() || (GetVehicleType() == CLIENTVEHICLE_BOAT || m_usModel == 447 /* sea sparrow */ + || m_usModel == 417 /* levithan */ + || m_usModel == 460 /* skimmer */)) { - if (!IsInWater() || (GetVehicleType() == CLIENTVEHICLE_BOAT || m_usModel == 447 /* sea sparrow */ - || m_usModel == 417 /* levithan */ - || m_usModel == 460 /* skimmer */)) - { - return true; - } + return true; } } } diff --git a/Client/mods/deathmatch/logic/CNetAPI.cpp b/Client/mods/deathmatch/logic/CNetAPI.cpp index 1bfb78db5e3..8181d4c2889 100644 --- a/Client/mods/deathmatch/logic/CNetAPI.cpp +++ b/Client/mods/deathmatch/logic/CNetAPI.cpp @@ -319,16 +319,34 @@ void CNetAPI::DoPulse() NetBitStreamInterface* pBitStream = g_pNet->AllocateNetBitStream(); if (pBitStream) { - // Write our data - WriteVehiclePuresync(pPlayer, pVehicle, *pBitStream); + // Is the vehicle created on the server? + if (!pVehicle->IsLocalEntity()) + { + // It is, sync it accordingly + // Write our data + WriteVehiclePuresync(pPlayer, pVehicle, *pBitStream); - // Send the packet and destroy it - g_pNet->SendPacket(PACKET_ID_PLAYER_VEHICLE_PURESYNC, pBitStream, PACKET_PRIORITY_MEDIUM, PACKET_RELIABILITY_UNRELIABLE_SEQUENCED); - g_pNet->DeallocateNetBitStream(pBitStream); + // Send the packet and destroy it + g_pNet->SendPacket(PACKET_ID_PLAYER_VEHICLE_PURESYNC, pBitStream, PACKET_PRIORITY_MEDIUM, PACKET_RELIABILITY_UNRELIABLE_SEQUENCED); + g_pNet->DeallocateNetBitStream(pBitStream); + } + else + { + // It was created client-side, we'll sync to the server as if we were on-foot instead + // Write our data + WritePlayerPuresync(pPlayer, *pBitStream); + + // Send the packet and destroy it + g_pNet->SendPacket(PACKET_ID_PLAYER_PURESYNC, pBitStream, PACKET_PRIORITY_MEDIUM, PACKET_RELIABILITY_UNRELIABLE_SEQUENCED); + g_pNet->DeallocateNetBitStream(pBitStream); + } } - // Sync its damage model too - static_cast(pVehicle)->SyncDamageModel(); + if (!pVehicle->IsLocalEntity()) + { + // Sync its damage model too + static_cast(pVehicle)->SyncDamageModel(); + } } else { @@ -1118,11 +1136,14 @@ void CNetAPI::WritePlayerPuresync(CClientPlayer* pPlayerModel, NetBitStreamInter // If the player is in contact with a object/vehicle, make that the origin if (bInContact) { - BitStream.Write(pContactEntity->GetID()); + if (!pContactEntity->IsLocalEntity()) + { + BitStream.Write(pContactEntity->GetID()); - CVector vecOrigin; - pContactEntity->GetPosition(vecOrigin); - vecPosition -= vecOrigin; + CVector vecOrigin; + pContactEntity->GetPosition(vecOrigin); + vecPosition -= vecOrigin; + } } SPositionSync position(false); diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp index 9cdb305b358..e31c1301215 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp @@ -1787,6 +1787,54 @@ bool CStaticFunctionDefinitions::GetPedOccupiedVehicleSeat(CClientPed& pPed, uin return false; } +CClientVehicle* CStaticFunctionDefinitions::GetPedNearestVehicleEntryPoint(CClientPed& pPed, bool bCheckDriverDoor, bool bCheckPassengersDoors, + unsigned int& uiEntryPoint, CVector& vecClosestDoorPosition) +{ + // This function should only work on local peds. + if (!pPed.IsLocalEntity() && !pPed.IsLocalPlayer()) + { + return false; + } + + if (pPed.IsInVehicle()) + { + // The ped is already in a car, why bother checking the nearest car seat? + return false; + } + + CClientVehicle* pVehicle = pPed.GetClosestVehicleInRange(true, bCheckDriverDoor, bCheckPassengersDoors, false, &uiEntryPoint, &vecClosestDoorPosition, 20.0f, (!pPed.IsLocalPlayer())); + + return pVehicle; +} + +CClientVehicle* CStaticFunctionDefinitions::GetPedEnterVehicleTarget(CClientPed& pPed, unsigned int& uiSeat, unsigned int& uiEntryPoint) +{ + // This function only works on local peds. + if (!pPed.IsLocalEntity() && !pPed.IsLocalPlayer()) + { + return false; + } + + if (pPed.IsEnteringVehicle()) + { + if (!pPed.GetOccupiedVehicle()) + { + uiSeat = pPed.GetTargetSeat(); + uiEntryPoint = pPed.GetTargetDoor(); + CClientVehicle* pTargetVehicle = pPed.GetTargetVehicle(); + + if (!pTargetVehicle->IsLocalEntity() && !pPed.IsLocalPlayer()) + { + return false; + } + + return pTargetVehicle; + } + } + + return false; +} + const char* CStaticFunctionDefinitions::GetPedSimplestTask(CClientPed& Ped) { CTaskManager* pTaskManager = Ped.GetTaskManager(); @@ -2134,6 +2182,13 @@ bool CStaticFunctionDefinitions::SetPedAnimation(CClientEntity& Entity, const SS if (IS_PED(&Entity)) { CClientPed& Ped = static_cast(Entity); + + // Ped is trying to enter a vehicle, cancel it. + if (Ped.IsEnteringVehicle()) + { + CancelPedEnterVehicle(Ped); + } + if (strBlockName && szAnimName) { std::unique_ptr pBlock = g_pGame->GetAnimManager()->GetAnimationBlock(strBlockName); @@ -2516,6 +2571,129 @@ bool CStaticFunctionDefinitions::SetPedOxygenLevel(CClientEntity& Entity, float return false; } +bool CStaticFunctionDefinitions::SetPedExitVehicle(CClientPed& pPed) +{ + // This function only works on local peds. + if (!pPed.IsLocalEntity() && !pPed.IsLocalPlayer()) + { + return false; + } + + if (!pPed.GetGamePlayer()) + { + return false; + } + + // We are not inside a vehicle, how are we supposed to exit? + if (!pPed.GetOccupiedVehicle()) + { + return false; + } + + if (pPed.IsLocalPlayer()) + { + m_pClientGame->ResetVehicleInOut(); + return m_pClientGame->ProcessExitVehicle(); + } + else + { + return pPed.GracefullyExitCar(); + } + + return false; +} + +bool CStaticFunctionDefinitions::SetPedEnterVehicle(CClientPed& pPed, CClientVehicle& pVehicle, unsigned int uiSeat) +{ + // This function only works on local peds. + if (!pPed.IsLocalEntity() && !pPed.IsLocalPlayer()) + { + return false; + } + + if (!pPed.GetGamePlayer()) + { + return false; + } + + // We are already inside a vehicle. + if (pPed.GetOccupiedVehicle()) + { + return false; + } + + if (!pVehicle.IsEnterable()) + { + return false; + } + + if (pPed.IsDead() || pVehicle.GetHealth() <= 0.0f) + { + return false; + } + + // Valid seat id for that vehicle? + uchar ucMaxPassengers = CClientVehicleManager::GetMaxPassengerCount(pVehicle.GetModel()); + if (uiSeat > ucMaxPassengers || ucMaxPassengers == 255) + { + return false; + } + + unsigned int uiDoor = uiSeat; + + if (uiSeat == 0) { + CVector vecDoorPos; + pPed.GetClosestDoor(&pVehicle, true, false, uiDoor, &vecDoorPos); + } + + if (pPed.IsLocalPlayer()) + { + m_pClientGame->ResetVehicleInOut(); + return m_pClientGame->ProcessEnterVehicle(&pVehicle, uiSeat, uiDoor); + } + else + { + if (!pVehicle.IsLocalEntity()) + { + return false; + } + + return pPed.GracefullyEnterCar(&pVehicle, uiSeat, uiDoor); + } + + return false; +} + +bool CStaticFunctionDefinitions::CancelPedEnterVehicle(CClientPed& pPed) +{ + // This function only works on local peds. + if (!pPed.IsLocalEntity() && !pPed.IsLocalPlayer()) + { + return false; + } + + if (pPed.IsEnteringVehicle()) + { + if (!pPed.GetOccupiedVehicle()) + { + if (pPed.IsLocalPlayer()) + { + m_pClientGame->ResetVehicleInOut(); + } + else + { + pPed.ResetInOutState(); + } + + pPed.KillTask(TASK_PRIORITY_PRIMARY, true); + + return true; + } + } + + return false; +} + bool CStaticFunctionDefinitions::GetBodyPartName(unsigned char ucID, SString& strOutName) { if (ucID <= 10) @@ -2753,6 +2931,9 @@ CClientVehicle* CStaticFunctionDefinitions::CreateVehicle(CResource& Resource, u if (szRegPlate) pVehicle->SetRegPlate(szRegPlate); + pVehicle->CalcAndUpdateCanBeDamagedFlag(); + pVehicle->CalcAndUpdateTyresCanBurstFlag(); + return pVehicle; } @@ -9709,7 +9890,7 @@ bool CStaticFunctionDefinitions::GetPedOxygenLevel(CClientPed& Ped, float& fOxyg bool CStaticFunctionDefinitions::WarpPedIntoVehicle(CClientPed* pPed, CClientVehicle* pVehicle, unsigned int uiSeat) { - if (pPed->IsLocalEntity() != pVehicle->IsLocalEntity()) + if (pPed->IsLocalEntity() != pVehicle->IsLocalEntity() && !pPed->IsLocalPlayer()) return false; if (pPed->IsLocalEntity()) diff --git a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h index d8fc31326fe..b7a6b66e723 100644 --- a/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h +++ b/Client/mods/deathmatch/logic/CStaticFunctionDefinitions.h @@ -147,6 +147,9 @@ class CStaticFunctionDefinitions static bool IsPedHeadless(CClientPed& Ped, bool& bHeadless); static bool IsPedFrozen(CClientPed& Ped, bool& bFrozen); static bool GetPedOccupiedVehicleSeat(CClientPed& Ped, uint& uiSeat); + static CClientVehicle* GetPedNearestVehicleEntryPoint(CClientPed& pPed, bool bCheckDriverDoor, bool bCheckPassengersDoors, + unsigned int& uiEntryPoint, CVector& vecClosestDoorPosition); + static CClientVehicle* GetPedEnterVehicleTarget(CClientPed& pPed, unsigned int& uiSeat, unsigned int& uiEntryPoint); static bool IsPedFootBloodEnabled(CClientPed& Ped, bool& bHasFootBlood); static bool GetPedCameraRotation(CClientPed& Ped, float& fRotation); static bool GetPedWeaponMuzzlePosition(CClientPed& Ped, CVector& vecPosition); @@ -189,6 +192,10 @@ class CStaticFunctionDefinitions static bool WarpPedIntoVehicle(CClientPed* pPed, CClientVehicle* pVehicle, unsigned int uiSeat); static bool SetPedOxygenLevel(CClientEntity& Entity, float fOxygen); + static bool SetPedExitVehicle(CClientPed& pPed); + static bool SetPedEnterVehicle(CClientPed& pPed, CClientVehicle& pVehicle, unsigned int uiSeat); + static bool CancelPedEnterVehicle(CClientPed& pPed); + // Extra Clothes functions static bool GetBodyPartName(unsigned char ucID, SString& strOutName); static bool GetClothesByTypeIndex(unsigned char ucType, unsigned char ucIndex, SString& strOutTexture, SString& strOutModel); diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp index aefa8518b01..598d37fb1da 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp @@ -45,6 +45,8 @@ void CLuaPedDefs::LoadFunctions() {"getPedTotalAmmo", GetPedTotalAmmo}, {"getPedOccupiedVehicle", GetPedOccupiedVehicle}, {"getPedOccupiedVehicleSeat", GetPedOccupiedVehicleSeat}, + {"getPedNearestVehicleEntryPoint", GetPedNearestVehicleEntryPoint}, + {"getPedEnterVehicleTarget", GetPedEnterVehicleTarget}, {"getPedArmor", GetPedArmor}, {"isPedChoking", IsPedChoking}, {"isPedDucked", IsPedDucked}, @@ -99,6 +101,9 @@ void CLuaPedDefs::LoadFunctions() {"setPedArmor", ArgumentParser}, {"givePedWeapon", GivePedWeapon}, {"isPedReloadingWeapon", IsPedReloadingWeapon}, + {"setPedExitVehicle", SetPedExitVehicle}, + {"setPedEnterVehicle", SetPedEnterVehicle}, + {"cancelPedEnterVehicle", CancelPedEnterVehicle} }; // Add functions @@ -136,6 +141,8 @@ void CLuaPedDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "getMoveState", "getPedMoveState"); lua_classfunction(luaVM, "getOccupiedVehicle", "getPedOccupiedVehicle"); lua_classfunction(luaVM, "getOccupiedVehicleSeat", "getPedOccupiedVehicleSeat"); + lua_classfunction(luaVM, "getNearestVehicleEntryPoint", OOP_GetPedNearestVehicleEntryPoint); + lua_classfunction(luaVM, "getEnterVehicleTarget", "getPedEnterVehicleTarget"); lua_classfunction(luaVM, "getOxygenLevel", "getPedOxygenLevel"); lua_classfunction(luaVM, "getStat", "getPedStat"); lua_classfunction(luaVM, "getTarget", "getPedTarget"); @@ -188,6 +195,9 @@ void CLuaPedDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "setStat", "setPedStat"); lua_classfunction(luaVM, "giveWeapon", "givePedWeapon"); lua_classfunction(luaVM, "isReloadingWeapon", "isPedReloadingWeapon"); + lua_classfunction(luaVM, "setExitVehicle", "setPedExitVehicle"); + lua_classfunction(luaVM, "setEnterVehicle", "setPedEnterVehicle"); + lua_classfunction(luaVM, "cancelEnterVehicle", "cancelPedEnterVehicle"); lua_classvariable(luaVM, "vehicle", OOP_WarpPedIntoVehicle, GetPedOccupiedVehicle); lua_classvariable(luaVM, "vehicleSeat", NULL, "getPedOccupiedVehicleSeat"); @@ -495,6 +505,106 @@ int CLuaPedDefs::GetPedOccupiedVehicleSeat(lua_State* luaVM) return 1; } +int CLuaPedDefs::GetPedNearestVehicleEntryPoint(lua_State* luaVM) +{ + CClientPed* pPed = NULL; + bool bCheckDriverDoor = true; + bool bCheckPassengersDoors = true; + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pPed); + argStream.ReadBool(bCheckDriverDoor, true); + argStream.ReadBool(bCheckPassengersDoors, true); + + if (!argStream.HasErrors()) + { + unsigned int uiEntryPoint; + CVector vecClosestDoorPosition; + + CClientVehicle* pVehicle = CStaticFunctionDefinitions::GetPedNearestVehicleEntryPoint(*pPed, bCheckDriverDoor, bCheckPassengersDoors, + uiEntryPoint, vecClosestDoorPosition); + + if (pVehicle) + { + lua_pushelement(luaVM, pVehicle); + lua_pushnumber(luaVM, uiEntryPoint); + lua_pushnumber(luaVM, vecClosestDoorPosition.fX); + lua_pushnumber(luaVM, vecClosestDoorPosition.fY); + lua_pushnumber(luaVM, vecClosestDoorPosition.fZ); + return 5; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + // Failed + lua_pushboolean(luaVM, false); + return 1; +} + +int CLuaPedDefs::OOP_GetPedNearestVehicleEntryPoint(lua_State* luaVM) +{ + // Verify the argument + CClientPed* pPed = NULL; + bool bCheckDriverDoor = true; + bool bCheckPassengersDoors = true; + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pPed); + argStream.ReadBool(bCheckDriverDoor, true); + argStream.ReadBool(bCheckPassengersDoors, true); + + if (!argStream.HasErrors()) + { + unsigned int uiEntryPoint; + CVector vecClosestDoorPosition; + + CClientVehicle* pVehicle = CStaticFunctionDefinitions::GetPedNearestVehicleEntryPoint(*pPed, bCheckDriverDoor, bCheckPassengersDoors, + uiEntryPoint, vecClosestDoorPosition); + + if (pVehicle) + { + lua_pushelement(luaVM, pVehicle); + lua_pushnumber(luaVM, uiEntryPoint); + lua_pushvector(luaVM, vecClosestDoorPosition); + return 3; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + // Failed + lua_pushboolean(luaVM, false); + return 1; +} + +int CLuaPedDefs::GetPedEnterVehicleTarget(lua_State* luaVM) +{ + CClientPed* pPed = NULL; + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pPed); + + if (!argStream.HasErrors()) + { + unsigned int uiSeat; + unsigned int uiEntryPoint; + + CClientVehicle* pVehicle = CStaticFunctionDefinitions::GetPedEnterVehicleTarget(*pPed, uiSeat, uiEntryPoint); + + if (pVehicle) + { + lua_pushelement(luaVM, pVehicle); + lua_pushnumber(luaVM, uiSeat); + lua_pushnumber(luaVM, uiEntryPoint); + return 3; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + // Failed + lua_pushboolean(luaVM, false); + return 1; +} + int CLuaPedDefs::GetPedTask(lua_State* luaVM) { // Verify the argument @@ -1570,7 +1680,7 @@ int CLuaPedDefs::WarpPedIntoVehicle(lua_State* luaVM) if (!argStream.HasErrors()) { - if (!pPed->IsLocalEntity() || !pVehicle->IsLocalEntity()) + if ((!pPed->IsLocalEntity() && !pPed->IsLocalPlayer()) || !pVehicle->IsLocalEntity()) argStream.SetCustomError("This client side function will only work with client created peds and vehicles"); } @@ -1606,7 +1716,7 @@ int CLuaPedDefs::OOP_WarpPedIntoVehicle(lua_State* luaVM) MinClientReqCheck(argStream, MIN_CLIENT_REQ_WARPPEDINTOVEHICLE_CLIENTSIDE, "function is being called client side"); if (!argStream.HasErrors()) { - if (!pPed->IsLocalEntity() || !pVehicle->IsLocalEntity()) + if ((!pPed->IsLocalEntity() && !pPed->IsLocalPlayer()) || !pVehicle->IsLocalEntity()) argStream.SetCustomError("This client side function will only work with client created peds and vehicles"); } @@ -1658,8 +1768,16 @@ int CLuaPedDefs::RemovePedFromVehicle(lua_State* luaVM) if (!argStream.HasErrors()) { - if (!pPed->IsLocalEntity()) + CClientVehicle* pOccupiedVehicle = pPed->GetOccupiedVehicle(); + + if (!pPed->IsLocalEntity() && !pPed->IsLocalPlayer()) argStream.SetCustomError("This client side function will only work with client created peds"); + + if (pOccupiedVehicle) + { + if (!pOccupiedVehicle->IsLocalEntity()) + argStream.SetCustomError("This client side function will only work with client created peds"); + } } if (!argStream.HasErrors()) @@ -2287,6 +2405,76 @@ int CLuaPedDefs::SetPedOxygenLevel(lua_State* luaVM) return 1; } +int CLuaPedDefs::SetPedExitVehicle(lua_State* luaVM) +{ + CClientPed* pPed = NULL; + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pPed); + + if (!argStream.HasErrors()) + { + if (CStaticFunctionDefinitions::SetPedExitVehicle(*pPed)) + { + lua_pushboolean(luaVM, true); + return 1; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + // Failed + lua_pushboolean(luaVM, false); + return 1; +} + +int CLuaPedDefs::SetPedEnterVehicle(lua_State* luaVM) +{ + CClientPed* pPed = NULL; + CClientVehicle* pVehicle = NULL; + unsigned int uiSeat = 0; + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pPed); + argStream.ReadUserData(pVehicle); + argStream.ReadNumber(uiSeat); + + if (!argStream.HasErrors()) + { + if (CStaticFunctionDefinitions::SetPedEnterVehicle(*pPed, *pVehicle, uiSeat)) + { + lua_pushboolean(luaVM, true); + return 1; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + // Failed + lua_pushboolean(luaVM, false); + return 1; +} + +int CLuaPedDefs::CancelPedEnterVehicle(lua_State* luaVM) +{ + CClientPed* pPed = NULL; + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pPed); + + if (!argStream.HasErrors()) + { + if (CStaticFunctionDefinitions::CancelPedEnterVehicle(*pPed)) + { + lua_pushboolean(luaVM, true); + return 1; + } + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + // Failed + lua_pushboolean(luaVM, false); + return 1; +} + int CLuaPedDefs::CreatePed(lua_State* luaVM) { // Verify the argument diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.h index 6220982cfa4..fedd2c131a5 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.h @@ -36,6 +36,8 @@ class CLuaPedDefs : public CLuaDefs LUA_DECLARE(GetPedStat); LUA_DECLARE(GetPedOccupiedVehicle); LUA_DECLARE(GetPedOccupiedVehicleSeat); + LUA_DECLARE_OOP(GetPedNearestVehicleEntryPoint); + LUA_DECLARE(GetPedEnterVehicleTarget); LUA_DECLARE(GetPedArmor); LUA_DECLARE(IsPedChoking); LUA_DECLARE(IsPedDucked); @@ -100,4 +102,7 @@ class CLuaPedDefs : public CLuaDefs LUA_DECLARE(RemovePedFromVehicle); LUA_DECLARE(SetPedOxygenLevel); LUA_DECLARE(SetPedStat); + LUA_DECLARE(SetPedExitVehicle); + LUA_DECLARE(SetPedEnterVehicle); + LUA_DECLARE(CancelPedEnterVehicle); }; diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.cpp b/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.cpp index 32b382b0532..9ea75433d5f 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.cpp +++ b/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.cpp @@ -83,6 +83,7 @@ void CLuaVehicleDefs::LoadFunctions() {"getVehicleComponents", GetVehicleComponents}, {"getVehicleModelExhaustFumesPosition", GetVehicleModelExhaustFumesPosition}, {"getVehicleModelDummyPosition", GetVehicleModelDummyPosition}, + {"getVehicleEntryPoints", GetVehicleEntryPoints}, {"getVehicleWheelScale", ArgumentParser}, {"getVehicleModelWheelSize", ArgumentParser}, @@ -226,6 +227,7 @@ void CLuaVehicleDefs::AddClass(lua_State* luaVM) lua_classfunction(luaVM, "getUpgradeOnSlot", "getVehicleUpgradeOnSlot"); lua_classfunction(luaVM, "getModelExhaustFumesPosition", OOP_GetVehicleModelExhaustFumesPosition); lua_classfunction(luaVM, "getVehicleModelDummyPosition", OOP_GetVehicleModelDummyPosition); + lua_classfunction(luaVM, "getEntryPoints", OOP_GetVehicleEntryPoints); lua_classfunction(luaVM, "getWheelScale", "getVehicleWheelScale"); lua_classfunction(luaVM, "getModelWheelSize", "getVehicleModelWheelSize"); @@ -3881,6 +3883,145 @@ int CLuaVehicleDefs::IsVehicleWindowOpen(lua_State* luaVM) return 1; } +int CLuaVehicleDefs::GetVehicleEntryPoints(lua_State* luaVM) +{ + CClientVehicle* pVehicle = NULL; + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pVehicle); + + if (!argStream.HasErrors()) + { + unsigned char ucMaxPassengers = CClientVehicleManager::GetMaxPassengerCount(pVehicle->GetModel()); + + if (ucMaxPassengers == 255) + { + lua_pushboolean(luaVM, false); + return 1; + } + + CVehicle* pGameVehicle = pVehicle->GetGameVehicle(); + + if (!pGameVehicle) + { + lua_pushboolean(luaVM, false); + return 1; + } + + lua_newtable(luaVM); + + for (unsigned int i = 8; i <= 11; i++) + { + CVector vecDoorPosition; + g_pGame->GetCarEnterExit()->GetPositionToOpenCarDoor(vecDoorPosition, pGameVehicle, i); + + unsigned int uiDoor = i; + + switch (uiDoor) + { + case 10: + uiDoor = 0; + break; + case 8: + uiDoor = 1; + break; + case 11: + uiDoor = 2; + break; + case 9: + uiDoor = 3; + break; + } + + lua_pushnumber(luaVM, uiDoor); + lua_createtable(luaVM, 3, 0); + + lua_pushnumber(luaVM, 1); + lua_pushnumber(luaVM, vecDoorPosition.fX); + lua_settable(luaVM, -3); + + lua_pushnumber(luaVM, 2); + lua_pushnumber(luaVM, vecDoorPosition.fY); + lua_settable(luaVM, -3); + + lua_pushnumber(luaVM, 3); + lua_pushnumber(luaVM, vecDoorPosition.fZ); + lua_settable(luaVM, -3); + + lua_settable(luaVM, -3); + } + + return 1; + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + lua_pushboolean(luaVM, false); + return 1; +} + +int CLuaVehicleDefs::OOP_GetVehicleEntryPoints(lua_State* luaVM) +{ + CClientVehicle* pVehicle = NULL; + CScriptArgReader argStream(luaVM); + argStream.ReadUserData(pVehicle); + + if (!argStream.HasErrors()) + { + unsigned char ucMaxPassengers = CClientVehicleManager::GetMaxPassengerCount(pVehicle->GetModel()); + + if (ucMaxPassengers == 255) + { + lua_pushboolean(luaVM, false); + return 1; + } + + CVehicle* pGameVehicle = pVehicle->GetGameVehicle(); + + if (!pGameVehicle) + { + lua_pushboolean(luaVM, false); + return 1; + } + + lua_newtable(luaVM); + + for (unsigned int i = 8; i <= 11; i++) + { + CVector vecDoorPosition; + g_pGame->GetCarEnterExit()->GetPositionToOpenCarDoor(vecDoorPosition, pGameVehicle, i); + + unsigned int uiDoor = i; + + switch (uiDoor) + { + case 10: + uiDoor = 0; + break; + case 8: + uiDoor = 1; + break; + case 11: + uiDoor = 2; + break; + case 9: + uiDoor = 3; + break; + } + + lua_pushnumber(luaVM, uiDoor); + lua_pushvector(luaVM, vecDoorPosition); + lua_settable(luaVM, -3); + } + + return 1; + } + else + m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage()); + + lua_pushboolean(luaVM, false); + return 1; +} + int CLuaVehicleDefs::SetVehicleModelDummyPosition(lua_State* luaVM) { // bool setVehicleModelDummyPosition ( int modelID, vehicle-dummy dummy, float x, float y, float z ) diff --git a/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.h b/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.h index 8d3c015df60..6946bc01222 100644 --- a/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.h +++ b/Client/mods/deathmatch/logic/luadefs/CLuaVehicleDefs.h @@ -79,6 +79,7 @@ class CLuaVehicleDefs : public CLuaDefs LUA_DECLARE(GetVehicleNitroLevel); LUA_DECLARE(GetHeliBladeCollisionsEnabled); LUA_DECLARE(IsVehicleWindowOpen); + LUA_DECLARE_OOP(GetVehicleEntryPoints); LUA_DECLARE(FixVehicle); LUA_DECLARE(BlowVehicle); diff --git a/Client/sdk/game/CCarEnterExit.h b/Client/sdk/game/CCarEnterExit.h index 079a22ead46..0c70935b5ab 100644 --- a/Client/sdk/game/CCarEnterExit.h +++ b/Client/sdk/game/CCarEnterExit.h @@ -21,4 +21,5 @@ class CCarEnterExit bool bCheckIfRoomToGetIn) = 0; virtual int ComputeTargetDoorToExit(CPed* pPed, CVehicle* pVehicle) = 0; virtual bool IsRoomForPedToLeaveCar(CVehicle* pVehicle, int iDoor, CVector* pUnknown = 0) = 0; + virtual void GetPositionToOpenCarDoor(CVector& vecPosition, CVehicle* pVehicle, unsigned int uiDoor) = 0; };