diff --git a/Client/game_sa/CPedSA.cpp b/Client/game_sa/CPedSA.cpp index e9433852e6..1c67d4f341 100644 --- a/Client/game_sa/CPedSA.cpp +++ b/Client/game_sa/CPedSA.cpp @@ -213,8 +213,8 @@ void CPedSA::ClearWeapons() void CPedSA::RemoveWeaponModel(std::uint32_t model) { - // void __thiscall CPed::RemoveWeaponModel(CPed *this, int modelID) - ((void(__thiscall*)(CEntitySAInterface*, std::uint32_t))FUNC_RemoveWeaponModel)(m_pInterface, model); + if (auto* pedInterface = GetPedInterface()) + pedInterface->RemoveWeaponModel(model); } void CPedSA::ClearWeapon(eWeaponType weaponType) @@ -598,6 +598,73 @@ void CPedSA::SetInWaterFlags(bool inWater) physicalInterface->bSubmergedInWater = inWater; } +void __fastcall CPedSA::RemoveWeaponWhenEnteringVehicle(CPedSAInterface* pedInterface, void*, int jetpack) +{ + if (!pedInterface) + return; + + pedInterface->RemoveWeaponWhenEnteringVehicle(jetpack == 1); +} + +void CPedSAInterface::RemoveWeaponWhenEnteringVehicle(bool jetpack) +{ + // Bugfix #3659 (allow switch weapons while using jetpack - see PR #3573) + if (!jetpack) + { + if (auto* playerData = pPlayerData) + playerData->m_bInVehicleDontAllowWeaponChange = true; + } + + if (savedWeapon != eWeaponType::WEAPONTYPE_UNIDENTIFIED) + return; + + eWeaponSlot newSlot = WEAPONSLOT_MAX; + + if (IsPlayer()) + { + auto* playerInfo = pGame->GetPlayerInfo(); + if (playerInfo && playerInfo->CanDoDriveBy()) + { + const auto& smg = Weapons[WEAPONSLOT_TYPE_SMG]; + const auto& shotgun = Weapons[WEAPONSLOT_TYPE_SHOTGUN]; + const auto& pistol = Weapons[WEAPONSLOT_TYPE_HANDGUN]; + + const bool hasSMG = (smg.m_eWeaponType == eWeaponType::WEAPONTYPE_MICRO_UZI || smg.m_eWeaponType == eWeaponType::WEAPONTYPE_TEC9 || + (jetpack && smg.m_eWeaponType == eWeaponType::WEAPONTYPE_MP5)) && + smg.m_ammoTotal > 0; + + const bool hasSawnoff = jetpack && shotgun.m_eWeaponType == eWeaponType::WEAPONTYPE_SAWNOFF_SHOTGUN && shotgun.m_ammoTotal > 0; + + const bool hasPistol = jetpack && pistol.m_eWeaponType == eWeaponType::WEAPONTYPE_PISTOL && pistol.m_ammoTotal > 0; + + if (hasSMG) + { + newSlot = WEAPONSLOT_TYPE_SMG; + } + else if (hasSawnoff) // Bugfix - the default here was WEAPONSLOT_TYPE_HANDGUN + { + newSlot = WEAPONSLOT_TYPE_SHOTGUN; + } + else if (hasPistol) + { + newSlot = WEAPONSLOT_TYPE_HANDGUN; + } + } + } + + if (newSlot != WEAPONSLOT_MAX) + { + savedWeapon = Weapons[bCurrentWeaponSlot].m_eWeaponType; + SetCurrentWeapon(newSlot); + } + else if (!jetpack) // Bugfix #508 (weapons are invisible when wearing jetpack - see PR #3559) + { + auto weaponType = Weapons[bCurrentWeaponSlot].m_eWeaponType; + auto model = pGame->GetWeaponInfo(weaponType, eWeaponSkill::WEAPONSKILL_STD)->GetModel(); + RemoveWeaponModel(model); + } +} + //////////////////////////////////////////////////////////////// // // CPed_PreRenderAfterTest @@ -686,4 +753,9 @@ void CPedSA::StaticSetHooks() { EZHookInstall(CPed_PreRenderAfterTest); EZHookInstall(CPed_PreRenderAfterTest_Mid); + + HookInstallCall(0x68025A, (DWORD)CPedSA::RemoveWeaponWhenEnteringVehicle); // CTaskSimpleJetPack::ProcessPed + HookInstallCall(0x64DB4D, (DWORD)CPedSA::RemoveWeaponWhenEnteringVehicle); // CTaskSimpleCarGetIn::ProcessPed + HookInstallCall(0x64BCA3, (DWORD)CPedSA::RemoveWeaponWhenEnteringVehicle); // CTaskSimpleCarSetPedInAsDriver::ProcessPed + HookInstallCall(0x64B876, (DWORD)CPedSA::RemoveWeaponWhenEnteringVehicle); // CTaskSimpleCarSetPedInAsPassenger::ProcessPed } diff --git a/Client/game_sa/CPedSA.h b/Client/game_sa/CPedSA.h index a45397f1e9..4f0b7565dc 100644 --- a/Client/game_sa/CPedSA.h +++ b/Client/game_sa/CPedSA.h @@ -228,6 +228,15 @@ static_assert(sizeof(CPedStatSAInterface) == 0x34, "Invalid size for CPedStatSAI class CPedSAInterface : public CPhysicalSAInterface { +public: + bool IsPlayer() const noexcept { return bPedType < 2; } + + void SetCurrentWeapon(eWeaponSlot slot) { ((void(__thiscall*)(CPedSAInterface*, eWeaponSlot))FUNC_SetCurrentWeapon)(this, slot); } + + void RemoveWeaponModel(std::uint32_t model) { ((void(__thiscall*)(CEntitySAInterface*, std::uint32_t))FUNC_RemoveWeaponModel)(this, model); } + + void RemoveWeaponWhenEnteringVehicle(bool jetpack); + public: CPedSoundEntitySAInterface pedAudio; // CAEPedAudioEntity CPedSoundSAInterface pedSound; // CAEPedSpeechAudioEntity @@ -286,7 +295,7 @@ class CPedSAInterface : public CPhysicalSAInterface CVehicleSAInterface* vehicleDeadInFrontOf; int unk_594; - int bPedType; // ped type? 0 = player, >1 = ped? + int bPedType; // 0 = player, 1 = player2, > 1 = ped CPedStatSAInterface* pPedStats; CWeaponSAInterface Weapons[WEAPONSLOT_MAX]; eWeaponType savedWeapon; @@ -477,7 +486,8 @@ class CPedSA : public virtual CPed, public virtual CPhysicalSA void SetInWaterFlags(bool inWater) override; - static void StaticSetHooks(); + static void __fastcall RemoveWeaponWhenEnteringVehicle(CPedSAInterface* pedInterface, void*, int jetpack); + static void StaticSetHooks(); private: void ApplySwimAndSlopeRotations(); diff --git a/Client/game_sa/CPlayerInfoSA.h b/Client/game_sa/CPlayerInfoSA.h index bcad187ce8..e705715c2a 100644 --- a/Client/game_sa/CPlayerInfoSA.h +++ b/Client/game_sa/CPlayerInfoSA.h @@ -270,4 +270,6 @@ class CPlayerInfoSA : public CPlayerInfo float GetBikeFrontWheelDist() { return internalInterface->fBikeFrontWheelDist; } std::uint8_t GetMaxHealth() const { return internalInterface->MaxHealth; } std::uint8_t GetMaxArmor() const { return internalInterface->MaxArmour; } + + bool CanDoDriveBy() const override { return internalInterface->bCanDoDriveBy; } }; diff --git a/Client/multiplayer_sa/CMultiplayerSA_1.3.cpp b/Client/multiplayer_sa/CMultiplayerSA_1.3.cpp index ec474ec7ec..2d4a246f05 100644 --- a/Client/multiplayer_sa/CMultiplayerSA_1.3.cpp +++ b/Client/multiplayer_sa/CMultiplayerSA_1.3.cpp @@ -111,9 +111,6 @@ DWORD RETURN_CProjectile_FixTearGasCrash_Cont = 0x4C0409; #define HOOKPOS_CProjectile_FixExplosionLocation 0x738A77 DWORD RETURN_CProjectile_FixExplosionLocation = 0x738A86; -#define HOOKPOS_CPed_RemoveWeaponWhenEnteringVehicle 0x5E6370 -DWORD RETURN_CPed_RemoveWeaponWhenEnteringVehicle = 0x5E6379; - void HOOK_CVehicle_ProcessStuff_TestSirenTypeSingle(); void HOOK_CVehicle_ProcessStuff_PostPushSirenPositionSingle(); void HOOK_CVehicle_ProcessStuff_TestSirenTypeDual(); @@ -140,7 +137,6 @@ void HOOK_CVehicleModelInterface_SetClump(); void HOOK_CBoat_ApplyDamage(); void HOOK_CProjectile_FixTearGasCrash(); void HOOK_CProjectile_FixExplosionLocation(); -void HOOK_CPed_RemoveWeaponWhenEnteringVehicle(); void* __cdecl HOOK_CMemoryMgr_MallocAlign(int size, int alignment, int nHint); void __cdecl HOOK_CMemoryMgr_FreeAlign(void* ptr); @@ -198,9 +194,6 @@ void CMultiplayerSA::InitHooks_13() HookInstall(HOOKPOS_CProjectile_FixExplosionLocation, (DWORD)HOOK_CProjectile_FixExplosionLocation, 12); - // Fix invisible weapons during jetpack task - HookInstall(HOOKPOS_CPed_RemoveWeaponWhenEnteringVehicle, (DWORD)HOOK_CPed_RemoveWeaponWhenEnteringVehicle, 9); - InitHooks_ClothesSpeedUp(); EnableHooks_ClothesMemFix(true); InitHooks_FixBadAnimId(); @@ -1872,52 +1865,3 @@ static void __declspec(naked) HOOK_CProjectile_FixExplosionLocation() } // clang-format on } - -DWORD CPed_RemoveWeaponWhenEnteringVehicle_CalledFrom = 0; -static void __declspec(naked) HOOK_CPed_RemoveWeaponWhenEnteringVehicle() -{ - MTA_VERIFY_HOOK_LOCAL_SIZE; - - // clang-format off - __asm - { - push eax - mov eax, [esp+4] - mov CPed_RemoveWeaponWhenEnteringVehicle_CalledFrom, eax - pop eax - - push esi - mov esi, ecx - mov eax, [esi+480h] - } - // clang-format on - - // Called from CTaskSimpleJetPack::ProcessPed - if (CPed_RemoveWeaponWhenEnteringVehicle_CalledFrom == 0x68025F) - { - // clang-format off - __asm - { - mov pPedUsingJetpack, esi - } - // clang-format on - - if (AllowJetPack()) - { - // clang-format off - __asm - { - pop esi - retn 4 - } - // clang-format on - } - } - - // clang-format off - __asm - { - jmp RETURN_CPed_RemoveWeaponWhenEnteringVehicle - } - // clang-format on -} diff --git a/Client/sdk/game/CPlayerInfo.h b/Client/sdk/game/CPlayerInfo.h index 8314d0e30e..36409735e4 100644 --- a/Client/sdk/game/CPlayerInfo.h +++ b/Client/sdk/game/CPlayerInfo.h @@ -40,4 +40,6 @@ class CPlayerInfo virtual float GetBikeFrontWheelDist() = 0; virtual std::uint8_t GetMaxHealth() const = 0; virtual std::uint8_t GetMaxArmor() const = 0; + + virtual bool CanDoDriveBy() const = 0; }; diff --git a/Server/mods/deathmatch/logic/CGame.cpp b/Server/mods/deathmatch/logic/CGame.cpp index 2902c106f5..6bc5889a55 100644 --- a/Server/mods/deathmatch/logic/CGame.cpp +++ b/Server/mods/deathmatch/logic/CGame.cpp @@ -276,6 +276,7 @@ CGame::CGame() : m_FloodProtect(4, 30000, 30000) // Max of 4 connections per 30 m_JetpackWeapons[WEAPONTYPE_MICRO_UZI] = true; m_JetpackWeapons[WEAPONTYPE_TEC9] = true; m_JetpackWeapons[WEAPONTYPE_PISTOL] = true; + m_JetpackWeapons[WEAPONTYPE_SAWNOFF_SHOTGUN] = true; // Glitch names (for Lua interface) m_GlitchNames["quickreload"] = GLITCH_QUICKRELOAD; m_GlitchNames["fastfire"] = GLITCH_FASTFIRE; diff --git a/Server/mods/deathmatch/logic/CPed.cpp b/Server/mods/deathmatch/logic/CPed.cpp index 17ad9fc56c..c42063c5c9 100644 --- a/Server/mods/deathmatch/logic/CPed.cpp +++ b/Server/mods/deathmatch/logic/CPed.cpp @@ -538,25 +538,3 @@ void CPed::SetJackingVehicle(CVehicle* pVehicle) if (m_pJackingVehicle) m_pJackingVehicle->SetJackingPed(this); } - -void CPed::SetHasJetPack(bool bHasJetPack) -{ - if (m_bHasJetPack == bHasJetPack) - return; - - m_bHasJetPack = bHasJetPack; - - if (!bHasJetPack) - return; - - // Set weapon slot to 0 if weapon is disabled with jetpack to avoid HUD and audio bugs - eWeaponType weaponType = static_cast(GetWeaponType(GetWeaponSlot())); - if (weaponType <= WEAPONTYPE_UNARMED) - return; - - bool weaponEnabled; - CStaticFunctionDefinitions::GetJetpackWeaponEnabled(weaponType, weaponEnabled); - - if (!weaponEnabled) - CStaticFunctionDefinitions::SetPedWeaponSlot(this, 0); -} diff --git a/Server/mods/deathmatch/logic/CPed.h b/Server/mods/deathmatch/logic/CPed.h index e3c4420190..11da52a642 100644 --- a/Server/mods/deathmatch/logic/CPed.h +++ b/Server/mods/deathmatch/logic/CPed.h @@ -208,7 +208,7 @@ class CPed : public CElement static const char* GetBodyPartName(unsigned char ucID); bool HasJetPack() { return m_bHasJetPack; } - void SetHasJetPack(bool bHasJetPack); + void SetHasJetPack(bool bHasJetPack) { m_bHasJetPack = bHasJetPack; } bool IsInWater() { return m_bInWater; } void SetInWater(bool bInWater) { m_bInWater = bInWater; }