|
29 | 29 | #include <game/CTaskManager.h> |
30 | 30 | #include <game/CWanted.h> |
31 | 31 | #include <game/CWeapon.h> |
| 32 | +#include <game/CWeaponInfo.h> |
32 | 33 | #include <game/CWeaponStatManager.h> |
33 | 34 | #include <game/CWeather.h> |
34 | 35 | #include <game/Task.h> |
|
38 | 39 | #include <game/CVehicleAudioSettingsManager.h> |
39 | 40 | #include <windowsx.h> |
40 | 41 | #include "CServerInfo.h" |
| 42 | +#include "CClientPed.h" |
41 | 43 |
|
42 | 44 | SString StringZeroPadout(const SString& strInput, uint uiPadoutSize) |
43 | 45 | { |
@@ -102,10 +104,11 @@ CClientGame::CClientGame(bool bLocalPlay) : m_ServerInfo(new CServerInfo()) |
102 | 104 | m_TargetedPlayerID = INVALID_ELEMENT_ID; |
103 | 105 | m_pDamageEntity = NULL; |
104 | 106 | m_DamagerID = INVALID_ELEMENT_ID; |
105 | | - m_ucDamageBodyPiece = 0xFF; |
106 | | - m_ucDamageWeapon = 0xFF; |
| 107 | + m_ucDamageBodyPiece = BODYPART_INVALID; |
| 108 | + m_ucDamageWeapon = WEAPONTYPE_INVALID; |
107 | 109 | m_ulDamageTime = 0; |
108 | 110 | m_bDamageSent = true; |
| 111 | + m_serverProcessedDeath = false; |
109 | 112 | m_bShowNetstat = false; |
110 | 113 | m_bShowFPS = false; |
111 | 114 | m_bHudAreaNameDisabled = false; |
@@ -1456,10 +1459,18 @@ void CClientGame::DoPulses() |
1456 | 1459 | if (CClientTime::GetTime() - m_ulDamageTime > 2000) |
1457 | 1460 | { |
1458 | 1461 | m_DamagerID = INVALID_ELEMENT_ID; |
1459 | | - m_ucDamageWeapon = 0xFF; |
1460 | | - m_ucDamageBodyPiece = 0xFF; |
| 1462 | + m_ucDamageWeapon = WEAPONTYPE_INVALID; |
| 1463 | + m_ucDamageBodyPiece = BODYPART_INVALID; |
| 1464 | + } |
| 1465 | + // Check if we need to trigger death event |
| 1466 | + if (!m_pLocalPlayer->IsDeadOnNetwork() && m_pLocalPlayer->GetHealth() == 0.0f) |
| 1467 | + { |
| 1468 | + // Only call DoWastedCheck if server hasn't already processed this death |
| 1469 | + // This prevents duplicate events when server processes death via unified context system |
| 1470 | + if (!m_serverProcessedDeath) { |
| 1471 | + DoWastedCheck(m_DamagerID, m_ucDamageWeapon, m_ucDamageBodyPiece); |
| 1472 | + } |
1461 | 1473 | } |
1462 | | - DoWastedCheck(m_DamagerID, m_ucDamageWeapon, m_ucDamageBodyPiece); |
1463 | 1474 | } |
1464 | 1475 |
|
1465 | 1476 | // Game hacks, restore certain variables |
@@ -2002,7 +2013,7 @@ void CClientGame::UpdateFireKey() |
2002 | 2013 | { |
2003 | 2014 | if (pTargetPed->IsLocalEntity()) |
2004 | 2015 | { |
2005 | | - CStaticFunctionDefinitions::KillPed(*pTargetPed, m_pLocalPlayer, 4 /*WEAPONTYPE_KNIFE*/, 9 /*BODYPART_HEAD*/, true); |
| 2016 | + CStaticFunctionDefinitions::KillPed(*pTargetPed, m_pLocalPlayer, WEAPONTYPE_KNIFE, BODYPART_HEAD, true); |
2006 | 2017 | return; |
2007 | 2018 | } |
2008 | 2019 |
|
@@ -4502,7 +4513,9 @@ bool CClientGame::ApplyPedDamageFromGame(eWeaponType weaponUsed, float fDamage, |
4502 | 4513 | GetDeathAnim(pDamagedPed, pEvent, animGroup, animID); |
4503 | 4514 |
|
4504 | 4515 | // Check if we're dead |
4505 | | - DoWastedCheck(damagerID, weaponUsed, hitZone, animGroup, animID); |
| 4516 | + if (!m_serverProcessedDeath) { |
| 4517 | + DoWastedCheck(damagerID, weaponUsed, hitZone, animGroup, animID); |
| 4518 | + } |
4506 | 4519 | } |
4507 | 4520 |
|
4508 | 4521 | // Allow GTA to kill us if we've fell to our death |
@@ -4570,6 +4583,14 @@ void CClientGame::DeathHandler(CPed* pKilledPedSA, unsigned char ucDeathReason, |
4570 | 4583 | if (!pKilledPed) |
4571 | 4584 | return; |
4572 | 4585 |
|
| 4586 | + // For local player in vehicle explosions, set damage data for consistent client events |
| 4587 | + if (IS_PLAYER(pKilledPed) && pKilledPed->IsLocalPlayer() && ucDeathReason == WEAPONTYPE_EXPLOSION) |
| 4588 | + { |
| 4589 | + // Set explosion damage data so DoWastedCheck uses correct parameters |
| 4590 | + SetExplosionDamageData(); |
| 4591 | + return; // Local player death is handled by DoWastedCheck |
| 4592 | + } |
| 4593 | + |
4573 | 4594 | // Not required for remote players. Local player is handled in DoPulses->DoWastedCheck |
4574 | 4595 | if (IS_PLAYER(pKilledPed)) |
4575 | 4596 | return; |
@@ -5596,11 +5617,11 @@ void CClientGame::DoWastedCheck(ElementID damagerID, unsigned char ucWeapon, uns |
5596 | 5617 | Arguments.PushElement(pKiller); |
5597 | 5618 | else |
5598 | 5619 | Arguments.PushBoolean(false); |
5599 | | - if (ucWeapon != 0xFF) |
| 5620 | + if (ucWeapon != WEAPONTYPE_INVALID) |
5600 | 5621 | Arguments.PushNumber(ucWeapon); |
5601 | 5622 | else |
5602 | 5623 | Arguments.PushBoolean(false); |
5603 | | - if (ucBodyPiece != 0xFF) |
| 5624 | + if (ucBodyPiece != BODYPART_INVALID) |
5604 | 5625 | Arguments.PushNumber(ucBodyPiece); |
5605 | 5626 | else |
5606 | 5627 | Arguments.PushBoolean(false); |
@@ -7150,3 +7171,20 @@ void CClientGame::AudioZoneRadioSwitchHandler(DWORD dwStationID) |
7150 | 7171 | g_pGame->GetAudioEngine()->StartRadio(dwStationID); |
7151 | 7172 | } |
7152 | 7173 | } |
| 7174 | + |
| 7175 | +////////////////////////////////////////////////////////////////// |
| 7176 | +// |
| 7177 | +// CClientGame::TryGetCurrentWeapon |
| 7178 | +// |
| 7179 | +// Helper method to get current weapon type with error handling |
| 7180 | +// Returns actual weapon type or WEAPONTYPE_UNARMED as fallback |
| 7181 | +// |
| 7182 | +////////////////////////////////////////////////////////////////// |
| 7183 | +std::uint8_t CClientGame::TryGetCurrentWeapon(CClientPlayer* player) |
| 7184 | +{ |
| 7185 | + if (!player) |
| 7186 | + return WEAPONTYPE_UNARMED; |
| 7187 | + |
| 7188 | + eWeaponType weaponType = player->GetCurrentWeaponType(); |
| 7189 | + return (weaponType != WEAPONTYPE_INVALID) ? static_cast<std::uint8_t>(weaponType) : WEAPONTYPE_UNARMED; |
| 7190 | +} |
0 commit comments