Thursday, October 30, 2014

Counter Strike: Global Offensive No Spread Explanation



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);
           
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;
    ...
}
and by following into C_CSPlayer::FireBullet( ..., flSpreadX, flSpreadY ) with this:
AngleVectors(shootAngles0, &vecDirShooting, &vecRight, &vecUp);
...
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);
So where do you get flSpread and flInaccuracy? By looking where FX_FireBullets is called you will find C_WeaponCSBaseGun::CSBaseGunFire and see this:

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 )
{
    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;

}
You have to call UpdateAccuracyPenalty( ) because you want up-to-date values for the next tick.

Wednesday, February 22, 2012

Saturday, February 18, 2012

[CSS] killerra - GUI 0.91

Finally, I had the time to make an update for my CSS GUI. Some minor bugs are fixed and it's updated to work with the latest version of CSS.

Available startparameters:
  • +killerra [my player-config with GFX-tweaks]
  • +killerra2 [my player-config without GFX-tweaks]
  • +fps [high fps config]

I've also added different filtersettings for the console:
  • filter0 [filter off]
  • filter1 [display damage messages only]
  • filter2 [highlight damage messages]
You have to find out the other features on your own. When I have more time I'll think about additional features.





Screenshots:






Old design:


Monday, January 30, 2012

Friday, January 27, 2012

So here we go, I've stated a blog. I might post how to's and others shit here.