Doing nospread is trivial, however I still so threads with people having trouble doing it. So I thought it would help newbies to actually see how to figure it out, that is why I post this explanation.
The spread is calculated in FX_FireBullets, so by reversing this function you will see something like this:
RandomSeed(iSeed + 1);and by following into C_CSPlayer::FireBullet( ..., flSpreadX, flSpreadY ) with this:
flRandPi_1 = RandomFloat(0, 0x40C90FDBu);
flCosRandPi_1 = cos(flRandPi_1);
flSinRandPi_1 = sin(flRandPi_1);
fRandInaccuracy = RandomFloat(0, flInaccuracy);
v48 = flCosRandPi_1 * fRandInaccuracy;
v47 = fRandInaccuracy * flSinRandPi_1;
while ( iBullet < FileWeaponInfo_t::GetAttributeInt(pWeaponInfo, "bullets", 0) )
{
flRandPi_2 = RandomFloat(0, 0x40C90FDBu);
flSinRandPi_2 = sin(flRandPi_2);
flCosRandPi_2 = cos(flRandPi_2);
flRandSpread = RandomFloat(0, LODWORD(flSpread));
v54[iBullet] = flCosRandPi_2 * flRandSpread;
v55[iBullet++] = flRandSpread * flSinRandPi_2;
}
while ( iBullet2 < FileWeaponInfo_t::GetAttributeInt(pWeaponInfo, "bullets", 0) )
{
flSpreadY = v47 + v55[iBullet2];
v32 = v48 + v54[iBullet2++];
flSpreadX = v32;
...
}
AngleVectors(shootAngles0, &vecDirShooting, &vecRight, &vecUp);So where do you get flSpread and flInaccuracy? By looking where FX_FireBullets is called you will find C_WeaponCSBaseGun::CSBaseGunFire and see this:
...
vecDir.x = (float)((float)(vecRight.x * flSpreadX) + vecDirShooting.x) + (float)(vecUp.x * flSpreadY);
vecDir.y = (float)((float)(flSpreadX * vecRight.y) + vecDirShooting.y) + (float)(flSpreadY * vecUp.y);
vecDir.z = (float)((float)(vecRight.z * flSpreadX) + vecDirShooting.z) + (float)(vecUp.z * flSpreadY);
C_WeaponCSBaseGun::CSBaseGunFire<al>(int a1<ecx>, ... )
{
pWeapon = a1;
...
(*(void (__thiscall **)(int))(*(_DWORD *)pWeapon + 1840))(pWeapon);// GetSpread()
*(float *)&flSpread = v7;
(*(void (__thiscall **)(int))(*(_DWORD *)pWeapon + 1836))(pWeapon);// GetInaccuracy()
*(float *)&flInaccuracy = v7;
}
So now we put this all together:
#define WEAPON_SPREAD_OFFSET (1836/4)
float CNoSpread::GetInaccuracy( C_BaseCombatWeapon *pWeapon )
{
DWORD dwInaccuracyVMT = ( *reinterpret_cast< PDWORD_PTR* >( pWeapon ) )[ WEAPON_SPREAD_OFFSET ];
return ( ( float ( __thiscall* )( C_BaseCombatWeapon* ) ) dwInaccuracyVMT )( pWeapon );
}
float CNoSpread::GetSpread( C_BaseCombatWeapon *pWeapon )
{
DWORD dwSpreadVMT = ( *reinterpret_cast< PDWORD_PTR* >( pWeapon ) )[ WEAPON_SPREAD_OFFSET + 1 ];
return ( ( float ( __thiscall* )( C_BaseCombatWeapon* ) ) dwSpreadVMT )( pWeapon );
}
void CNoSpread::UpdateAccuracyPenalty( C_BaseCombatWeapon *pWeapon )
{
DWORD dwUpdateVMT = ( *reinterpret_cast< PDWORD_PTR* >( pWeapon ) )[ WEAPON_SPREAD_OFFSET + 2 ];
return ( ( void ( __thiscall* )( C_BaseCombatWeapon* ) ) dwUpdateVMT )( pWeapon );
}
void CNoSpread::CompansateSpread( CUserCmd *pCmd, C_BaseCombatWeapon *pWeapon )You have to call UpdateAccuracyPenalty( ) because you want up-to-date values for the next tick.
{
Vector vecForward, vecRight, vecUp, vecAntiDir;
float flSpread, flInaccuracy, flSpreadX, flSpreadY;
QAngle qAntiSpread;
UpdateAccuracyPenalty( pWeapon );
flSpread = GetSpread( pWeapon );
flInaccuracy = GetInaccuracy( pWeapon );
RandomSeed( (pCmd->random_seed & 0xFF) + 1 );
float flRandPi_1 = RandomFloat( 0.0f, 2.0f * M_PI_F );
float flRandInaccuracy = RandomFloat( 0.0f, flInaccuracy );
float flRandPi_2 = RandomFloat( 0.0f, 2.0f * M_PI_F );
float flRandSpread = RandomFloat( 0.0f, flSpread );
float flSpreadX = cos(flRandPi_1) * flRandInaccuracy + cos(flRandPi_2) * flRandSpread;
float flSpreadY = sin(flRandPi_1) * flRandInaccuracy + sin(flRandPi_2) * flRandSpread;
AngleVectors( pCmd->viewangles, &vecForward, &vecRight, &vecUp );
vecAntiDir = vecDirShooting + ( vecRight * -flSpreadX ) + ( vecUp * -flSpreadY );
vecAntiDir.NormalizeInPlace( );
VectorAngles( vecAntiDir, qAntiSpread );
pCmd->viewangles = qAntiSpread;
}
