Skip to content

Commit

Permalink
Add server side weapon reloading (PR #3936, Fixes #1525)
Browse files Browse the repository at this point in the history
  • Loading branch information
Nico8340 authored Jan 10, 2025
1 parent 859b0da commit e71f482
Show file tree
Hide file tree
Showing 19 changed files with 161 additions and 95 deletions.
4 changes: 4 additions & 0 deletions Client/mods/deathmatch/logic/CClientGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3085,6 +3085,7 @@ void CClientGame::UpdateMimics()
bool bSunbathing = m_pLocalPlayer->IsSunbathing();
bool bDoingDriveby = m_pLocalPlayer->IsDoingGangDriveby();
bool bStealthAiming = m_pLocalPlayer->IsStealthAiming();
bool reloadingWeapon = m_pLocalPlayer->IsReloadingWeapon();

// Is the current weapon goggles (44 or 45) or a camera (43), or a detonator (40), don't apply the fire key
if (weaponSlot == 11 || weaponSlot == 12 || ucWeaponType == 43)
Expand Down Expand Up @@ -3143,6 +3144,9 @@ void CClientGame::UpdateMimics()
pMimic->SetDoingGangDriveby(bDoingDriveby);
pMimic->SetStealthAiming(bStealthAiming);

if (reloadingWeapon)
pMimic->ReloadWeapon();

Controller.ShockButtonL = 0;

if (m_bMimicLag)
Expand Down
63 changes: 27 additions & 36 deletions Client/mods/deathmatch/logic/CClientPed.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6008,51 +6008,42 @@ void CClientPed::SetSpeechEnabled(bool bEnabled)
m_bSpeechEnabled = bEnabled;
}

bool CClientPed::CanReloadWeapon()
bool CClientPed::CanReloadWeapon() noexcept
{
unsigned long ulNow = CClientTime::GetTime();
CControllerState Current;
GetControllerState(Current);
int iWeaponType = GetWeapon()->GetType();
// Hes not Aiming, ducked or if he is ducked he is not currently moving and he hasn't moved while crouching in the last 300ms (sometimes the crouching move
// anim runs over and kills the reload animation)
if (Current.RightShoulder1 == false && (!IsDucked() || (Current.LeftStickX == 0 && Current.LeftStickY == 0)) &&
ulNow - m_ulLastTimeMovedWhileCrouched > 300)
{
// Ignore certain weapons (anything without clip ammo)
if (iWeaponType >= WEAPONTYPE_PISTOL && iWeaponType <= WEAPONTYPE_TEC9 && iWeaponType != WEAPONTYPE_SHOTGUN)
{
return true;
}
}
return false;
const auto time = CClientTime::GetTime();
CControllerState state;
GetControllerState(state);

const auto weapon = GetWeapon()->GetType();

if (state.RightShoulder1 || (IsDucked() && (state.LeftStickX != 0 || state.LeftStickY != 0)) || time - m_ulLastTimeMovedWhileCrouched <= 300)
return false;

if (weapon < WEAPONTYPE_PISTOL || weapon > WEAPONTYPE_TEC9 || weapon == WEAPONTYPE_SHOTGUN)
return false;

return true;
}

bool CClientPed::ReloadWeapon()
bool CClientPed::ReloadWeapon() noexcept
{
if (m_pTaskManager)
{
CWeapon* pWeapon = GetWeapon();
CTask* pTask = m_pTaskManager->GetTaskSecondary(TASK_SECONDARY_ATTACK);
if (!m_pTaskManager)
return false;

// Check his control states for anything that can cancel the anim instantly and make sure he is not firing
if (CanReloadWeapon() && (!pTask || (pTask && pTask->GetTaskType() != TASK_SIMPLE_USE_GUN)))
{
// Play anim + reload
pWeapon->SetState(WEAPONSTATE_RELOADING);
auto* weapon = GetWeapon();
auto* task = m_pTaskManager->GetTaskSecondary(TASK_SECONDARY_ATTACK);

return true;
}
}
return false;
if (!CanReloadWeapon() || (task && task->GetTaskType() == TASK_SIMPLE_USE_GUN))
return false;

weapon->SetState(WEAPONSTATE_RELOADING);
return true;
}

bool CClientPed::IsReloadingWeapon()
bool CClientPed::IsReloadingWeapon() noexcept
{
if (CWeapon* weapon = GetWeapon(); weapon != nullptr)
return weapon->GetState() == WEAPONSTATE_RELOADING;
else
return false;
auto* weapon = GetWeapon();
return weapon && weapon->GetState() == WEAPONSTATE_RELOADING;
}

bool CClientPed::ShouldBeStealthAiming()
Expand Down
7 changes: 4 additions & 3 deletions Client/mods/deathmatch/logic/CClientPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ struct SLastSyncedPedData
float fRotation;
bool bOnFire;
bool bIsInWater;
bool isReloadingWeapon;
};

struct SRestoreWeaponItem
Expand Down Expand Up @@ -495,9 +496,9 @@ class CClientPed : public CClientStreamElement, public CAntiCheatModule
bool GetBulletImpactData(CClientEntity** ppVictim = 0, CVector* pvecHitPosition = 0);
void ClearBulletImpactData() { m_bBulletImpactData = false; }

bool CanReloadWeapon();
bool ReloadWeapon();
bool IsReloadingWeapon();
bool CanReloadWeapon() noexcept;
bool ReloadWeapon() noexcept;
bool IsReloadingWeapon() noexcept;

bool ShouldBeStealthAiming();
bool IsStealthAiming() { return m_bStealthAiming; }
Expand Down
3 changes: 3 additions & 0 deletions Client/mods/deathmatch/logic/CNetAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1118,6 +1118,9 @@ void CNetAPI::WritePlayerPuresync(CClientPlayer* pPlayerModel, NetBitStreamInter
flags.data.bSyncingVelocity = (!flags.data.bIsOnGround || (pPlayerModel->GetPlayerSyncCount() % 4) == 0);
flags.data.bStealthAiming = (pPlayerModel->IsStealthAiming() == true);

if (BitStream.Can(eBitStreamVersion::IsPedReloadingWeapon))
flags.data2.isReloadingWeapon = (pPlayerModel->IsReloadingWeapon() == true);

if (pPlayerWeapon->GetSlot() > 15)
flags.data.bHasAWeapon = false;

Expand Down
11 changes: 11 additions & 0 deletions Client/mods/deathmatch/logic/CPedSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,8 @@ void CPedSync::WritePedInformation(NetBitStreamInterface* pBitStream, CClientPed
ucFlags |= 0x20;
if (pPed->IsInWater() != pPed->m_LastSyncedData->bIsInWater)
ucFlags |= 0x40;
if (pPed->IsReloadingWeapon() != pPed->m_LastSyncedData->isReloadingWeapon && pBitStream->Can(eBitStreamVersion::IsPedReloadingWeapon))
ucFlags |= 0x60;
if (pPed->HasSyncedAnim() && (!pPed->IsRunningAnimation() || pPed->m_animationOverridedByClient))
ucFlags |= 0x80;

Expand Down Expand Up @@ -380,6 +382,7 @@ void CPedSync::WritePedInformation(NetBitStreamInterface* pBitStream, CClientPed
pBitStream->Write(pPed->GetHealth());
pPed->m_LastSyncedData->fHealth = pPed->GetHealth();
}

if (ucFlags & 0x10)
{
pBitStream->Write(pPed->GetArmor());
Expand All @@ -398,6 +401,14 @@ void CPedSync::WritePedInformation(NetBitStreamInterface* pBitStream, CClientPed
pPed->m_LastSyncedData->bIsInWater = pPed->IsInWater();
}

if (ucFlags & 0x60 && pBitStream->Can(eBitStreamVersion::IsPedReloadingWeapon))
{
bool isReloadingWeapon = pPed->IsReloadingWeapon();

pBitStream->WriteBit(isReloadingWeapon);
pPed->m_LastSyncedData->isReloadingWeapon = isReloadingWeapon;
}

// The animation has been overwritten or interrupted by the client
if (ucFlags & 0x80 && pBitStream->Can(eBitStreamVersion::AnimationsSync))
{
Expand Down
22 changes: 4 additions & 18 deletions Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ void CLuaPedDefs::LoadFunctions()
{"isPedChoking", IsPedChoking},
{"isPedDucked", IsPedDucked},
{"isPedDead", IsPedDead},
{"isPedReloadingWeapon", IsPedReloadingWeapon},
{"isPedReloadingWeapon", ArgumentParserWarn<false, IsPedReloadingWeapon>},
{"killPedTask", ArgumentParser<killPedTask>},
};

Expand Down Expand Up @@ -1233,25 +1233,11 @@ int CLuaPedDefs::GivePedWeapon(lua_State* luaVM)
return 1;
}

int CLuaPedDefs::IsPedReloadingWeapon(lua_State* luaVM)
bool CLuaPedDefs::IsPedReloadingWeapon(CClientPed* const ped) noexcept
{
// Verify the argument
CClientPed* pPed = NULL;
CScriptArgReader argStream(luaVM);
argStream.ReadUserData(pPed);

if (!argStream.HasErrors())
{
lua_pushboolean(luaVM, pPed->IsReloadingWeapon());
return 1;
}
else
m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage());

lua_pushboolean(luaVM, false);
return 1;
return ped->IsReloadingWeapon();
}

int CLuaPedDefs::GetPedClothes(lua_State* luaVM)
{
// Verify the argument
Expand Down
2 changes: 1 addition & 1 deletion Client/mods/deathmatch/logic/luadefs/CLuaPedDefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ class CLuaPedDefs : public CLuaDefs
static bool SetPedArmor(CClientPed* const ped, const float armor);
LUA_DECLARE(SetPedWeaponSlot);
LUA_DECLARE(GivePedWeapon);
LUA_DECLARE(IsPedReloadingWeapon);
static bool IsPedReloadingWeapon(CClientPed* const ped) noexcept;
LUA_DECLARE(AddPedClothes);
LUA_DECLARE(RemovePedClothes);
LUA_DECLARE(SetPedControlState);
Expand Down
4 changes: 3 additions & 1 deletion Server/mods/deathmatch/logic/CGame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1610,6 +1610,8 @@ void CGame::AddBuiltInEvents()
m_Events.AddEvent("onPlayerTarget", "target", NULL, false);
m_Events.AddEvent("onPlayerWasted", "ammo, killer, weapon, bodypart, isStealth, animGroup, animID", nullptr, false);
m_Events.AddEvent("onPlayerWeaponSwitch", "previous, current", NULL, false);
m_Events.AddEvent("onPlayerWeaponFire", "weapon, endX, endY, endZ, hitElement, startX, startY, startZ", nullptr, false);
m_Events.AddEvent("onPlayerWeaponReload", "weapon, clip, ammo", nullptr, false);
m_Events.AddEvent("onPlayerMarkerHit", "marker, matchingDimension", NULL, false);
m_Events.AddEvent("onPlayerMarkerLeave", "marker, matchingDimension", NULL, false);
m_Events.AddEvent("onPlayerPickupHit", "pickup", NULL, false);
Expand Down Expand Up @@ -1644,6 +1646,7 @@ void CGame::AddBuiltInEvents()
m_Events.AddEvent("onPedVehicleExit", "vehicle, reason, jacker", NULL, false);
m_Events.AddEvent("onPedWasted", "ammo, killer, weapon, bodypart, isStealth, animGroup, animID", nullptr, false);
m_Events.AddEvent("onPedWeaponSwitch", "previous, current", NULL, false);
m_Events.AddEvent("onPedWeaponReload", "weapon, clip, ammo", nullptr, false);
m_Events.AddEvent("onPedDamage", "loss", NULL, false);

// Element events
Expand Down Expand Up @@ -1699,7 +1702,6 @@ void CGame::AddBuiltInEvents()

// Weapon events
m_Events.AddEvent("onWeaponFire", "", NULL, false);
m_Events.AddEvent("onPlayerWeaponFire", "weapon, endX, endY, endZ, hitElement, startX, startY, startZ", NULL, false);
}

void CGame::ProcessTrafficLights(long long llCurrentTime)
Expand Down
4 changes: 4 additions & 0 deletions Server/mods/deathmatch/logic/CPed.h
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,9 @@ class CPed : public CElement
bool IsStealthAiming() { return m_bStealthAiming; }
void SetStealthAiming(bool bAiming) { m_bStealthAiming = bAiming; }

bool IsReloadingWeapon() const noexcept { return m_reloadingWeapon; }
void SetReloadingWeapon(bool state) noexcept { m_reloadingWeapon = state; }

bool GetCollisionEnabled() { return m_bCollisionsEnabled; }
void SetCollisionEnabled(bool bCollisionEnabled) { m_bCollisionsEnabled = bCollisionEnabled; }

Expand Down Expand Up @@ -340,6 +343,7 @@ class CPed : public CElement
bool m_bHeadless;
bool m_bFrozen;
bool m_bStealthAiming;
bool m_reloadingWeapon{};
CVehicle* m_pJackingVehicle;
SPlayerAnimData m_animData{};

Expand Down
5 changes: 5 additions & 0 deletions Server/mods/deathmatch/logic/CPedSync.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,11 @@ void CPedSync::Packet_PedSync(CPedSyncPacket& Packet)
if (Data.ucFlags & 0x40)
pPed->SetInWater(Data.bIsInWater);

if (Data.ucFlags & 0x60)
{
pPed->SetReloadingWeapon(Data.isReloadingWeapon);
}

if (Data.ucFlags & 0x80)
pPed->SetAnimationData({});

Expand Down
44 changes: 33 additions & 11 deletions Server/mods/deathmatch/logic/CStaticFunctionDefinitions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4508,19 +4508,41 @@ bool CStaticFunctionDefinitions::SetPedFrozen(CElement* pElement, bool bIsFrozen
}
return false;
}
bool CStaticFunctionDefinitions::reloadPedWeapon(CElement* pElement)
{

bool CStaticFunctionDefinitions::ReloadPedWeapon(CElement* pElement) noexcept {
assert(pElement);
RUN_CHILDREN(reloadPedWeapon(*iter))
RUN_CHILDREN(ReloadPedWeapon(*iter))

if (IS_PED(pElement))
{
CPed* pPed = static_cast<CPed*>(pElement);
CBitStream BitStream;
m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(pPed, RELOAD_PED_WEAPON, *BitStream.pBitStream));
return true;
}
return false;
if (!IS_PED(pElement))
return false;

CPed* ped = static_cast<CPed*>(pElement);

bool result;
CLuaArguments arguments;

std::uint8_t weapon = ped->GetWeaponType();
std::uint16_t clip = ped->GetWeaponAmmoInClip();
std::uint16_t ammo = ped->GetWeaponTotalAmmo();

arguments.PushNumber(weapon);
arguments.PushNumber(clip);
arguments.PushNumber(ammo);

if (IS_PLAYER(pElement))
result = ped->CallEvent("onPlayerWeaponReload", arguments);
else
result = ped->CallEvent("onPedWeaponReload", arguments);

if (!result)
return false;

CBitStream stream;

ped->SetReloadingWeapon(true);
m_pPlayerManager->BroadcastOnlyJoined(CElementRPCPacket(ped, RELOAD_PED_WEAPON, *stream.pBitStream));

return true;
}

bool CStaticFunctionDefinitions::GetCameraMatrix(CPlayer* pPlayer, CVector& vecPosition, CVector& vecLookAt, float& fRoll, float& fFOV)
Expand Down
2 changes: 1 addition & 1 deletion Server/mods/deathmatch/logic/CStaticFunctionDefinitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ class CStaticFunctionDefinitions
static bool SetPedOnFire(CElement* pElement, bool bIsOnFire);
static bool SetPedHeadless(CElement* pElement, bool bIsHeadless);
static bool SetPedFrozen(CElement* pElement, bool bIsFrozen);
static bool reloadPedWeapon(CElement* pElement);
static bool ReloadPedWeapon(CElement* pElement) noexcept;
static bool SetWeaponProperty(eWeaponProperty eProperty, eWeaponType eWeapon, eWeaponSkill eSkillLevel, float fData);
static bool SetWeaponProperty(eWeaponProperty eProperty, eWeaponType eWeapon, eWeaponSkill eSkillLevel, int sData);
static bool SetWeaponPropertyFlag(eWeaponProperty eProperty, eWeaponType eWeapon, eWeaponSkill eSkillLevel, bool bEnable);
Expand Down
32 changes: 11 additions & 21 deletions Server/mods/deathmatch/logic/luadefs/CLuaPedDefs.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ void CLuaPedDefs::LoadFunctions()
{"getPedOccupiedVehicle", GetPedOccupiedVehicle},
{"getPedOccupiedVehicleSeat", GetPedOccupiedVehicleSeat},
{"isPedInVehicle", IsPedInVehicle},
{"isPedReloadingWeapon", ArgumentParser<IsPedReloadingWeapon>},

// Ped set functions
{"setPedArmor", SetPedArmor},
Expand All @@ -71,7 +72,7 @@ void CLuaPedDefs::LoadFunctions()
{"setPedOnFire", SetPedOnFire},
{"setPedHeadless", SetPedHeadless},
{"setPedFrozen", SetPedFrozen},
{"reloadPedWeapon", reloadPedWeapon},
{"reloadPedWeapon", ArgumentParserWarn<false, ReloadPedWeapon>},

// Weapon give/take functions
{"giveWeapon", GiveWeapon},
Expand Down Expand Up @@ -118,6 +119,7 @@ void CLuaPedDefs::AddClass(lua_State* luaVM)
lua_classfunction(luaVM, "isFrozen", "isPedFrozen");
lua_classfunction(luaVM, "isHeadless", "isPedHeadless");
lua_classfunction(luaVM, "isWearingJetpack", "isPedWearingJetpack"); // introduced in 1.5.5-9.13846
lua_classfunction(luaVM, "isReloadingWeapon", "isPedReloadingWeapon");

lua_classfunction(luaVM, "getArmor", "getPedArmor");
lua_classfunction(luaVM, "getFightingStyle", "getPedFightingStyle");
Expand Down Expand Up @@ -169,6 +171,7 @@ void CLuaPedDefs::AddClass(lua_State* luaVM)
lua_classvariable(luaVM, "vehicle", "warpPedIntoVehicle", "getPedOccupiedVehicle", OOP_WarpPedIntoVehicle, GetPedOccupiedVehicle);
lua_classvariable(luaVM, "walkingStyle", "setPedWalkingStyle", "getPedWalkingStyle");
lua_classvariable(luaVM, "jetpack", "setPedWearingJetpack", "isPedWearingJetpack"); // introduced in 1.5.5-9.13846
lua_classvariable(luaVM, "reloadingWeapon", nullptr, "isPedReloadingWeapon");

// TODO(qaisjp): setting this to any value will kill the ped. add OOP_KillPed that only allows `true`.
lua_classvariable(luaVM, "dead", "killPed", "isPedDead");
Expand Down Expand Up @@ -289,28 +292,15 @@ int CLuaPedDefs::GetPedWeaponSlot(lua_State* luaVM)
return 1;
}

int CLuaPedDefs::reloadPedWeapon(lua_State* luaVM)
bool CLuaPedDefs::ReloadPedWeapon(lua_State* vm, CPed* const ped) noexcept
{
CElement* pPed;

CScriptArgReader argStream(luaVM);
argStream.ReadUserData(pPed);

if (!argStream.HasErrors())
{
LogWarningIfPlayerHasNotJoinedYet(luaVM, pPed);

if (CStaticFunctionDefinitions::reloadPedWeapon(pPed))
{
lua_pushboolean(luaVM, true);
return 1;
}
}
else
m_pScriptDebugging->LogCustom(luaVM, argStream.GetFullErrorMessage());
LogWarningIfPlayerHasNotJoinedYet(vm, ped);
return CStaticFunctionDefinitions::ReloadPedWeapon(ped);
}

lua_pushboolean(luaVM, false);
return 1;
bool CLuaPedDefs::IsPedReloadingWeapon(CPed* const ped) noexcept
{
return ped->IsReloadingWeapon();
}

int CLuaPedDefs::IsPedDoingGangDriveby(lua_State* luaVM)
Expand Down
Loading

0 comments on commit e71f482

Please sign in to comment.