The eventual fix for this, courtesy of @BC46 and @Aingar turned out to be a bit more complicated. Posting it here for visibility:
struct SrvGun
{
void* vtable;
CELauncher* launcher;
};
DWORD server;
UINT projectilesPerFire;
typedef bool (__fastcall HandlePlayerLauncherFire)(SrvGun *srvGun, PVOID _edx, Vector *vector);
bool __fastcall HandlePlayerLauncherFire_Hook(SrvGun *srvGun, PVOID _edx, Vector *vector)
{
projectilesPerFire = srvGun->launcher->GetProjectilesPerFire();
return ((HandlePlayerLauncherFire*) (server + 0xD840))(srvGun, _edx, vector);
}
__declspec(naked) void SetProjectilesPerFire()
{
__asm {
push 0x3F800000
push [projectilesPerFire]
mov eax, [server]
add eax, 0xD91A
jmp eax
}
}
server = (DWORD) GetModuleHandleA("server.dll");
Hook(server + 0xD9A9, (DWORD) HandlePlayerLauncherFire_Hook, 5);
Hook(server + 0xDC09, (DWORD) HandlePlayerLauncherFire_Hook, 5);
Hook(server + 0xE009, (DWORD) HandlePlayerLauncherFire_Hook, 5);
Hook(server + 0xD913, (DWORD) SetProjectilesPerFire, 5, true);
This also fixes some serverside issues with ammo counts saving incorrectly on client reconnect,