Adding Ironsights
This is a tutorial how to add Ironsights and is an alternative to Jorg's code.
The new approach allows you to control the toggling server-side and thus you can add different bullet spread and such easily. It also has working angles and fov-change. However, the code still has to be edited to work in multiplayer and also only has been tested with a singleplayer OB-Mod.
New Approach
Note: There are no global variables or functions used in my code, so everything goes into the corresponding class-definition.
Weaponscript
At first, we will add some custom script-variables to be able to adjust the ironsight-offset via the weapon-script.
weapon_parse.h
Add this to the member-variables of FileWeaponInfo_t:
Vector vecIronsightPosOffset;
QAngle angIronsightAngOffset;
float flIronsightFOVOffset;
weapon_parse.cpp
This goes to FileWeaponInfo_t::Parse:
KeyValues *pSights = pKeyValuesData->FindKey( "IronSight" );
if (pSights)
{
vecIronsightPosOffset.x = pSights->GetFloat( "forward", 0.0f );
vecIronsightPosOffset.y = pSights->GetFloat( "right", 0.0f );
vecIronsightPosOffset.z = pSights->GetFloat( "up", 0.0f );
angIronsightAngOffset[PITCH] = pSights->GetFloat( "pitch", 0.0f );
angIronsightAngOffset[YAW] = pSights->GetFloat( "yaw", 0.0f );
angIronsightAngOffset[ROLL] = pSights->GetFloat( "roll", 0.0f );
flIronsightFOVOffset = pSights->GetFloat( "fov", 0.0f );
}
else
{
vecIronsightPosOffset = vec3_origin;
angIronsightAngOffset.Init();
flIronsightFOVOffset = 0.0f;
}
weapon_smg1.txt
This is an example of how to add the ironsight-offsets to your script:
IronSight { "forward" "-10" "right" "-6.91" "up" "0.185" "roll" "-20" "fov" "-20" }
Getting the offsets
Now we will add simple functions to get the information we parsed from the weapon-scripts. But we also want to have console-variables to overwrite the parsed info so we can easily make new ironsight-offsets.
This goes to the public functions:
Vector GetIronsightPositionOffset( void ) const;
QAngle GetIronsightAngleOffset( void ) const;
float GetIronsightFOVOffset( void ) const;
These ConVars usually go after the includes:
ConVar viewmodel_adjust_forward( "viewmodel_adjust_forward", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_right( "viewmodel_adjust_right", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_up( "viewmodel_adjust_up", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_pitch( "viewmodel_adjust_pitch", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_yaw( "viewmodel_adjust_yaw", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_roll( "viewmodel_adjust_roll", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_fov( "viewmodel_adjust_fov", "0", FCVAR_REPLICATED );
ConVar viewmodel_adjust_enabled( "viewmodel_adjust_enabled", "0", FCVAR_REPLICATED|FCVAR_CHEAT, "enabled viewmodel adjusting", vm_adjust_enable_callback );
And then you add the function definitions:
Vector CBaseCombatWeapon::GetIronsightPositionOffset( void ) const
{
if( viewmodel_adjust_enabled.GetBool() )
return Vector( viewmodel_adjust_forward.GetFloat(), viewmodel_adjust_right.GetFloat(), viewmodel_adjust_up.GetFloat() );
return GetWpnData().vecIronsightPosOffset;
}
QAngle CBaseCombatWeapon::GetIronsightAngleOffset( void ) const
{
if( viewmodel_adjust_enabled.GetBool() )
return QAngle( viewmodel_adjust_pitch.GetFloat(), viewmodel_adjust_yaw.GetFloat(), viewmodel_adjust_roll.GetFloat() );
return GetWpnData().angIronsightAngOffset;
}
float CBaseCombatWeapon::GetIronsightFOVOffset( void ) const
{
if( viewmodel_adjust_enabled.GetBool() )
return viewmodel_adjust_fov.GetFloat();
return GetWpnData().flIronsightFOVOffset;
}
Adding toggle-functions
Of course you also want to be able to use the sights.
Add these two networked variables:
CNetworkVar( bool, m_bIsIronsighted );
CNetworkVar( float, m_flIronsightedTime );
Network them and give 'em default values.
Constructor:
m_bIsIronsighted = false;
m_flIronsightedTime = 0.0f;
Network-table (DT_BaseCombatWeapon):
SendPropBool( SENDINFO( m_bIsIronsighted ) ),
SendPropFloat( SENDINFO( m_flIronsightedTime ) ),
and
RecvPropBool( RECVINFO( m_bIsIronsighted ) ),
RecvPropFloat( RECVINFO( m_flIronsightedTime ) ),
Now that we have the variables, we also want accessors:
virtual bool HasIronsights( void ) { return true; } //default yes
bool IsIronsighted( void );
void ToggleIronsights( void ) { m_bIsIronsighted ? DisableIronsights() : EnableIronsights(); }
void EnableIronsights( void );
void DisableIronsights( void );
And of course define them:
bool CBaseCombatWeapon::IsIronsighted( void )
{
return ( m_bIsIronsighted || viewmodel_adjust_enabled.GetBool() );
}
void CBaseCombatWeapon::EnableIronsights( void )
{
if( !HasIronsights() || m_bIsIronsighted )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if( !pOwner )
return;
if( pOwner->SetFOV( this, pOwner->GetDefaultFOV()+GetIronsightFOVOffset(), 1.0f, 0.3f ) ) //modify these values to adjust how fast the fov is applied
{
m_bIsIronsighted = true;
m_flIronsightedTime = gpGlobals->curtime;
}
}
void CBaseCombatWeapon::DisableIronsights( void )
{
if( !HasIronsights() || !m_bIsIronsighted )
return;
CBasePlayer *pOwner = ToBasePlayer( GetOwner() );
if( !pOwner )
return;
if( pOwner->SetFOV( this, 0, 0.4f, 0.1f ) ) //modify these values to adjust how fast the fov is applied
{
m_bIsIronsighted = false;
m_flIronsightedTime = gpGlobals->curtime;
}
}
void vm_adjust_enable_callback( IConVar *pConVar, char const *pOldString, float flOldValue )
{
ConVarRef sv_cheats( "sv_cheats" );
if( !sv_cheats.IsValid() || !sv_cheats.GetBool() )
return;
ConVarRef var( pConVar );
if( var.GetBool() )
var.SetValue( "0" );
}
hl2_player.cpp
You probably want a command to switch between normal and sighted mode. I've put them in hl2_player.cpp.
void CC_ToggleIronSights( void )
{
CBasePlayer* pPlayer = UTIL_GetCommandClient();
if ( pPlayer == NULL )
return;
CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
if( pWeapon == NULL )
return;
pWeapon->ToggleIronsights();
}
static ConCommand toggle_ironsight("toggle_ironsight", CC_ToggleIronSights);
Adjust the viewmodel
Ok, now for the last step we want to move the viewmodel according to the offsets. That code is based off Jorg's. Thanks from z33ky.
There are still some little issues to fix, though. Viewmodel-bob is still active while iron-sighting and the viewmodel lags way too much when going out of ironsight and rotating the view. You can try adding m_flIronsightedTime to this or moving CalcIronsights in front of those bob-things, I don't know. Since I'm not working on Delusion: Source anymore I didn't try to fix those issues.
void CBaseViewModel::CalcIronsights( Vector &pos, QAngle &ang )
{
CBaseCombatWeapon *pWeapon = GetOwningWeapon();
if ( !pWeapon )
return;
//get delta time for interpolation
float delta( ( gpGlobals->curtime - pWeapon->m_flIronsightedTime ) / 0.4f ); //modify this value to adjust how fast the interpolation is
float exp = ( pWeapon->IsIronsighted() ) ?
( delta > 1.0f ) ? 1.0f : delta : //normal blending
( delta > 1.0f ) ? 0.0f : 1.0f - delta; //reverse interpolation
if( exp == 0.0f ) //fully not ironsighted; save performance
return;
Vector newPos = pos;
QAngle newAng = ang;
Vector vForward, vRight, vUp, vOffset;
AngleVectors( newAng, &vForward, &vRight, &vUp );
vOffset = pWeapon->GetIronsightPositionOffset();
newPos += vForward * vOffset.x;
newPos += vRight * vOffset.y;
newPos += vUp * vOffset.z;
newAng += pWeapon->GetIronsightAngleOffset();
//fov is handled by CBaseCombatWeapon
pos += ( newPos - pos ) * exp;
ang += ( newAng - ang ) * exp;
}
Make use of this code in CBaseViewModel::CalcViewModelView:
void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles )
{
// UNDONE: Calc this on the server? Disabled for now as it seems unnecessary to have this info on the server
#if defined( CLIENT_DLL )
QAngle vmangoriginal = eyeAngles;
QAngle vmangles = eyeAngles;
Vector vmorigin = eyePosition;
CBaseCombatWeapon *pWeapon = m_hWeapon.Get();
//Allow weapon lagging
//only if not in ironsight-mode
if( pWeapon == NULL || !pWeapon->IsIronsighted() )
{
if ( pWeapon != NULL )
{
#if defined( CLIENT_DLL )
if ( !prediction->InPrediction() )
#endif
{
// add weapon-specific bob
pWeapon->AddViewmodelBob( this, vmorigin, vmangles );
}
}
// Add model-specific bob even if no weapon associated (for head bob for off hand models)
AddViewModelBob( owner, vmorigin, vmangles );
// Add lag
CalcViewModelLag( vmorigin, vmangles, vmangoriginal );
#if defined( CLIENT_DLL )
if ( !prediction->InPrediction() )
{
// Let the viewmodel shake at about 10% of the amplitude of the player's view
vieweffects->ApplyShake( vmorigin, vmangles, 0.1 );
}
#endif
}
CalcIronsights( vmorigin, vmangles );
SetLocalOrigin( vmorigin );
SetLocalAngles( vmangles );
#endif
}
Don't forget to declare the new function:
void CalcIronsights( Vector &pos, QAngle &ang );
Add keybind
Last but not least, add the key to the Options/Keyboard-menu:
kb_act.lst
Probably somewhere in "#Valve_Combat_Title":
"toggle_ironsight" "#MOD_Toggle_Ironsight"
And don't forget to add this #MOD_Toggle_Ironsight to your resource/MOD_english.txt (and the other lanuages).
Notes
It would be great if someone would add the changes you need for a HL2MP-mod and a Scratch-mod and maybe for the ep1-engine (if any are needed).
I would appreciate it if you would credit me, z33ky, if you use this code (modified or unmodified) - but there is no need to.
Also, if you fix the issues, post how to!