Ironsights: Difference between revisions
(We don't need a dead link on the page.) |
|||
(15 intermediate revisions by 12 users not shown) | |||
Line 1: | Line 1: | ||
{{cleanup}} | {{cleanup}} | ||
The goal of this tutorial is to create an ironsight system | The goal of this tutorial is to create an ironsight system. It moves the origin of the weapon position up to the Player's eyes. | ||
Here's a tutorial that implements a server-controlled Ironsight system: [[Adding Ironsights]]. | |||
It might be better for MP mods, but works just as fine in SP. | |||
== Intro == | == Intro == | ||
HL2DM SDK | HL2DM and Singleplayer SDK | ||
This could work with the Scratch as well. Normally all you have to do is replace hl2mp_ with sdk_xxx.cpp and you will find the right files to put it in. | This could work with the Scratch as well. Normally all you have to do is replace hl2mp_ with sdk_xxx.cpp and you will find the right files to put it in. | ||
Line 16: | Line 15: | ||
He has credits to most of this code as it is now. | He has credits to most of this code as it is now. | ||
{{todo|Change the fov on toggle}} | |||
== hl2mp_weapon_parse.h == | == hl2mp_weapon_parse.h (DM) or weapon_parse.h (Singleplayer) == | ||
Add this in the bottom of the file. Only add the lines with //ADDED behind. | Add this in the bottom of the file. Only add the lines with //ADDED behind. | ||
< | <source lang=cpp> | ||
public: | public: | ||
Line 36: | Line 28: | ||
int m_iPlayerDamage; | int m_iPlayerDamage; | ||
}; | }; | ||
</ | </source> | ||
== hl2mp_weapon_parse.cpp == | == hl2mp_weapon_parse.cpp (DM) or weapon_parse.cpp (Singleplayer) == | ||
Add this in the file. | Add this in the file. | ||
< | <source lang=cpp> | ||
void CHL2MPSWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName ) | void CHL2MPSWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName ) | ||
{ | { | ||
Line 63: | Line 55: | ||
... | ... | ||
} | } | ||
</ | </source> | ||
== baseviewmodel_shared.cpp == | == baseviewmodel_shared.cpp == | ||
Change in the beginning of the file so it looks like this | Change in the beginning of the file so it looks like this | ||
< | <source lang=cpp> | ||
#include "cbase.h" | #include "cbase.h" | ||
#include "baseviewmodel_shared.h" | #include "baseviewmodel_shared.h" | ||
Line 86: | Line 78: | ||
// memdbgon must be the last include file in a .cpp file!!! | // memdbgon must be the last include file in a .cpp file!!! | ||
#include "tier0/memdbgon.h" | #include "tier0/memdbgon.h" | ||
</ | </source> | ||
Then under that add so it should look like this. | Then under that add so it should look like this. | ||
< | <source lang=cpp> | ||
//THIS IS ALREADY IN THE CODE, just so you know where we are | //THIS IS ALREADY IN THE CODE, just so you know where we are | ||
#define VIEWMODEL_ANIMATION_PARITY_BITS 3 | #define VIEWMODEL_ANIMATION_PARITY_BITS 3 | ||
Line 148: | Line 140: | ||
if (pVm) | if (pVm) | ||
{ | { | ||
pPlayer->m_Local.m_iHideHUD ^= HIDEHUD_CROSSHAIR ; | |||
pVm->m_bExpSighted ^= true; | pVm->m_bExpSighted ^= true; | ||
gIronsightedTime = gpGlobals->curtime; | gIronsightedTime = gpGlobals->curtime; | ||
Line 189: | Line 181: | ||
} | } | ||
#endif | #endif | ||
</ | </source> | ||
Scroll down some and you will find the ViewModel::Spawn | Scroll down some and you will find the ViewModel::Spawn | ||
Edit it to look like this: | Edit it to look like this: | ||
< | <source lang=cpp> | ||
void CBaseViewModel::Spawn( void ) | void CBaseViewModel::Spawn( void ) | ||
{ | { | ||
Line 209: | Line 201: | ||
} | } | ||
</ | </source> | ||
=== Animating the gun === | === Animating the gun === | ||
Line 215: | Line 207: | ||
Look for void CBaseViewModel::CalcViewModelView. | Look for void CBaseViewModel::CalcViewModelView. | ||
Replace the whole function with this. | Replace the whole function with this. | ||
< | <source lang=cpp> | ||
void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles ) | void CBaseViewModel::CalcViewModelView( CBasePlayer *owner, const Vector& eyePosition, const QAngle& eyeAngles ) | ||
{ | { | ||
Line 260: | Line 252: | ||
#endif | #endif | ||
} | } | ||
</ | </source> | ||
Looks easy huh? What does it do then? | Looks easy huh? What does it do then? | ||
Line 268: | Line 260: | ||
== baseviewmodel_shared.h == | == baseviewmodel_shared.h == | ||
You need to add a couple of public defines here. | You need to add a couple of public defines here. | ||
< | <source lang=cpp> | ||
class CBaseViewModel : public CBaseAnimating | class CBaseViewModel : public CBaseAnimating | ||
{ | { | ||
Line 283: | Line 275: | ||
DECLARE_DATADESC(); | DECLARE_DATADESC(); | ||
#endif | #endif | ||
</ | </source> | ||
== Extra == | == Extra == | ||
Line 290: | Line 282: | ||
Look for this line: '''float gIronsightedTime(0.0f);''' | Look for this line: '''float gIronsightedTime(0.0f);''' | ||
Below it add this. | Below it add this. | ||
< | <source lang=cpp> | ||
float gMoveTime(0.1f); //Jorg40 - Seconds to use to move the model up to players view | float gMoveTime(0.1f); //Jorg40 - Seconds to use to move the model up to players view | ||
</ | </source> | ||
Last line. Go to the delta | Last line. Go to the delta | ||
'''float delta(gpGlobals->curtime - gIronsightedTime);''' | '''float delta(gpGlobals->curtime - gIronsightedTime);''' | ||
Change it to look like this: | Change it to look like this: | ||
< | <source lang=cpp> | ||
float delta((gpGlobals->curtime - gIronsightedTime) / gMoveTime); | float delta((gpGlobals->curtime - gIronsightedTime) / gMoveTime); | ||
</ | </source> | ||
Done! All we need to do is to change the script files. | Done! All we need to do is to change the script files. | ||
== weapon_pistol.txt == | == weapon_pistol.txt == | ||
Add this somewhere in the script file. | Add this somewhere around the "WeaponData" section in the script file. | ||
< | <source lang=cpp> | ||
ExpOffset | ExpOffset | ||
{ | { | ||
Line 315: | Line 307: | ||
// "zori" "2.0" // Does not work | // "zori" "2.0" // Does not work | ||
} | } | ||
</ | </source> | ||
== Singleplayer Differences == | |||
To achieve this in singleplayer make the following alterations: | |||
Replace: | |||
<source lang=cpp>CWeaponHL2MPBase *pWeapon = dynamic_cast<CWeaponHL2MPBase *>(pPlayer->GetActiveWeapon()); </source> | |||
with | |||
<source lang=cpp>CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); </source> | |||
Replace: | |||
<source lang=cpp>CWeaponHL2MPBase *pWeapon = dynamic_cast<CWeaponHL2MPBase *>(ToHL2MPPlayer(owner)->GetActiveWeapon());</source> | |||
with | |||
<source lang=cpp>CBaseCombatWeapon *pWeapon = owner->GetActiveWeapon();</source> | |||
Replace: | |||
<source lang=cpp>ConVar cl_exp_test_wpn_offset("cl_exp_test_wpn_offset", "0", 0, "Tests weapon offsets", (FnChangeCallback_t)ExpWpnTestOffset);</source> | |||
with | |||
= | <source lang=cpp>ConVar cl_exp_test_wpn_offset("cl_exp_test_wpn_offset", "0", 0, "Tests weapon offsets", (FnChangeCallback)ExpWpnTestOffset);</source> | ||
Replace: | |||
<source lang=cpp>#include "convar.h" | |||
#include "c_hl2mp_player.h" | |||
#include "weapon_hl2mpbase.h"</source> | |||
with | with | ||
< | <source lang=cpp>#include "convar.h" | ||
#include "c_baseplayer.h"</source> | |||
Replace: | |||
pWeapon->GetHL2MPWpnData() | <source lang=cpp>pWeapon->GetHL2MPWpnData()</source> | ||
with | with | ||
< | <source lang=cpp>pWeapon->GetWpnData()</source> | ||
== Results == | == Results == | ||
[[ | [[File:Ironsights result.jpg|thumb|right|300px|Example of the end result]] | ||
* An example image is shown on the right. | * An example image is shown on the right. | ||
{{clr}} | {{clr}} | ||
Line 349: | Line 367: | ||
How you set m_expFactor (time based interpolation is the easiest) and m_bExpSighted (button press) is up to you. Also , consider how you may pass this information to the server so ironsighted mode can be propagated over to other clients (i.e. other players will see that you're in ironsighted mode). | How you set m_expFactor (time based interpolation is the easiest) and m_bExpSighted (button press) is up to you. Also , consider how you may pass this information to the server so ironsighted mode can be propagated over to other clients (i.e. other players will see that you're in ironsighted mode). | ||
Note from | Also in-game, open console, write '''cl_exp''' and all the commands that are needed will show up. | ||
Note from Pretador: Adding the Keybinds: | |||
To add the keybind to toggle ironsight, open up your kb_act. | |||
Under "#Valve_Combat_Title" Add: | |||
"ironsight_toggle" "#MOD_ironsight_toggle" | |||
Then open your mod_english.txt in mod/resource and write: | |||
"MOD_ironsight_toggle" "ironsight_toggle" | |||
[[Category: | [[Category:Weapons programming]] |
Latest revision as of 07:53, 11 January 2024

For help, see the VDC Editing Help and Wikipedia cleanup process. Also, remember to check for any notes left by the tagger at this article's talk page.
The goal of this tutorial is to create an ironsight system. It moves the origin of the weapon position up to the Player's eyes.
Here's a tutorial that implements a server-controlled Ironsight system: Adding Ironsights. It might be better for MP mods, but works just as fine in SP.
Intro
HL2DM and Singleplayer SDK
This could work with the Scratch as well. Normally all you have to do is replace hl2mp_ with sdk_xxx.cpp and you will find the right files to put it in.
This is a community project. The original code comes from this man: Cin.
He has credits to most of this code as it is now.
hl2mp_weapon_parse.h (DM) or weapon_parse.h (Singleplayer)
Add this in the bottom of the file. Only add the lines with //ADDED behind.
public:
//jorg40 - ironsight
Vector m_expOffset; //ADDED
QAngle m_expOriOffset; //ADDED
int m_iPlayerDamage;
};
hl2mp_weapon_parse.cpp (DM) or weapon_parse.cpp (Singleplayer)
Add this in the file.
void CHL2MPSWeaponInfo::Parse( KeyValues *pKeyValuesData, const char *szWeaponName )
{
...
// this just saves off the data in the script file for later use
KeyValues *pEt = pKeyValuesData->FindKey("ExpOffset");
if (pEt)
{
m_expOffset.x = pEt->GetFloat("x", 0.0f);
m_expOffset.y = pEt->GetFloat("y", 0.0f);
m_expOffset.z = pEt->GetFloat("z", 0.0f);
m_expOriOffset.x = pEt->GetFloat("xori", 0.0f);
m_expOriOffset.y = pEt->GetFloat("yori", 0.0f);
m_expOriOffset.z = pEt->GetFloat("zori", 0.0f);
}
else
{
m_expOffset = vec3_origin;
m_expOriOffset.Init();
}
...
}
Change in the beginning of the file so it looks like this
#include "cbase.h"
#include "baseviewmodel_shared.h"
#include "datacache/imdlcache.h"
#if defined( CLIENT_DLL )
#include "iprediction.h"
#include "prediction.h"
// cin: 070105 - ironsight mode changes
#include "convar.h"
#include "c_hl2mp_player.h"
#include "weapon_hl2mpbase.h"
#else
#include "vguiscreen.h"
#endif
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
Then under that add so it should look like this.
//THIS IS ALREADY IN THE CODE, just so you know where we are
#define VIEWMODEL_ANIMATION_PARITY_BITS 3
#define SCREEN_OVERLAY_MATERIAL "vgui/screens/vgui_overlay"
//ALL BELOW IS NEW
#if defined( CLIENT_DLL )
void ExpWpnTestOffset(ConVar *pConVar, char *pszString);
ConVar cl_exp_test_wpn_offset("cl_exp_test_wpn_offset", "0", 0, "Tests weapon offsets",
(FnChangeCallback_t)ExpWpnTestOffset);
ConVar cl_exp_test_wpn_offset_x("cl_exp_test_wpn_offset_x", "0");
ConVar cl_exp_test_wpn_offset_y("cl_exp_test_wpn_offset_y", "0");
ConVar cl_exp_test_wpn_offset_z("cl_exp_test_wpn_offset_z", "0");
ConVar cl_exp_test_wpn_ori_offset_x("cl_exp_test_wpn_ori_offset_x", "0");
ConVar cl_exp_test_wpn_ori_offset_y("cl_exp_test_wpn_ori_offset_y", "0");
ConVar cl_exp_test_wpn_ori_offset_z("cl_exp_test_wpn_ori_offset_z", "0");
// cin: 070105 - applies existing weapon offsets when
// entering test mode (this will not be called upon
// weapon change, so beware)
// this mode should only be used for calibrating the
// ironsighted mode offests for a particular weapon
void ExpWpnTestOffset(ConVar *pConVar, char *pszString)
{
CBasePlayer *pPlayer = UTIL_PlayerByIndex(engine->GetLocalPlayer());
if (pPlayer)
{
CWeaponHL2MPBase *pWeapon = dynamic_cast<CWeaponHL2MPBase *>(pPlayer->GetActiveWeapon());
if (pWeapon)
{
cl_exp_test_wpn_offset_x.SetValue(pWeapon->GetHL2MPWpnData().m_expOffset.x);
cl_exp_test_wpn_offset_y.SetValue(pWeapon->GetHL2MPWpnData().m_expOffset.y);
cl_exp_test_wpn_offset_z.SetValue(pWeapon->GetHL2MPWpnData().m_expOffset.z);
cl_exp_test_wpn_ori_offset_x.SetValue(pWeapon->GetHL2MPWpnData().m_expOriOffset.x);
cl_exp_test_wpn_ori_offset_y.SetValue(pWeapon->GetHL2MPWpnData().m_expOriOffset.y);
cl_exp_test_wpn_ori_offset_z.SetValue(pWeapon->GetHL2MPWpnData().m_expOriOffset.z);
}
}
}
// last time ironsighted mode was toggled
float gIronsightedTime(0.0f);
// I bound this to a key for testing(i.e. bind [ ironsight_toggle)
CON_COMMAND(ironsight_toggle, "toggles ironsight mode for the current weapon")
{
if (gpGlobals->curtime - gIronsightedTime < 0.5f)
return;
CBasePlayer *pPlayer = UTIL_PlayerByIndex(engine->GetLocalPlayer());
if (pPlayer)
{
C_BaseViewModel *pVm = pPlayer->GetViewModel();
if (pVm)
{
pPlayer->m_Local.m_iHideHUD ^= HIDEHUD_CROSSHAIR ;
pVm->m_bExpSighted ^= true;
gIronsightedTime = gpGlobals->curtime;
}
}
}
void CalcExpWpnOffsets(CBasePlayer *owner, Vector &pos, QAngle &ang)
{
Vector forward, right, up, offset;
// this is a simple test mode to help determine the proper values
// to place in the weapon script
if (cl_exp_test_wpn_offset.GetBool())
{
ang.x += cl_exp_test_wpn_ori_offset_x.GetFloat();
ang.y += cl_exp_test_wpn_ori_offset_y.GetFloat();
ang.z += cl_exp_test_wpn_ori_offset_z.GetFloat();
offset.Init(cl_exp_test_wpn_offset_x.GetFloat(),
cl_exp_test_wpn_offset_y.GetFloat(),
cl_exp_test_wpn_offset_z.GetFloat());
}
else
{
CWeaponHL2MPBase *pWeapon = dynamic_cast<CWeaponHL2MPBase *>(ToHL2MPPlayer(owner)->GetActiveWeapon());
if (pWeapon)
{
ang += pWeapon->GetHL2MPWpnData().m_expOriOffset;
offset = pWeapon->GetHL2MPWpnData().m_expOffset;
}
}
// get eye direction angles
AngleVectors(ang, &forward, &right, &up);
// apply the offsets
pos += forward * offset.x;
pos += right * offset.y;
pos += up * offset.z;
}
#endif
Scroll down some and you will find the ViewModel::Spawn Edit it to look like this:
void CBaseViewModel::Spawn( void )
{
Precache( );
SetSize( Vector( -8, -4, -2), Vector(8, 4, 2) );
SetSolid( SOLID_NONE );
#ifdef CLIENT_DLL
// cin: 070105 - ironsighted mode changes
m_bExpSighted = false;
m_expFactor = 0.0f;
gIronsightedTime = 0.0f;
#endif
}
Animating the gun
Now while we are still in the file we need some more. Look for void CBaseViewModel::CalcViewModelView. Replace the whole function with this.
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
if ( pWeapon != NULL )
{
#if defined( CLIENT_DLL )
if ( !prediction->InPrediction() )
#endif
{
pWeapon->AddViewmodelBob( this, vmorigin, vmangles );
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
// cin: 070105 - ironsighted mode changes
// get the wpn offsets
CalcExpWpnOffsets(owner, vmorigin, vmangles);
// get delta time for 1 sec interpolation and interpolate to/from positional offset
float delta(gpGlobals->curtime - gIronsightedTime);
m_expFactor = (m_bExpSighted) ?
(delta > 1.0f) ? 1.0f : delta :
(delta > 1.0f) ? 0.0f : 1.0f - delta;
Vector difPos(vmorigin - eyePosition);
vmorigin = eyePosition + (difPos * m_expFactor);
SetLocalOrigin(vmorigin);
SetLocalAngles(vmangles);
#endif
}
Looks easy huh? What does it do then? The delta variables says what is what. 1.0 is when the gun is fully ironsighted, and 0.0 says when it is not sighted at all. So this codes calculates the distance between and slide the gun from 0 to 1.
You need to add a couple of public defines here.
class CBaseViewModel : public CBaseAnimating
{
DECLARE_CLASS( CBaseViewModel, CBaseAnimating );
public:
bool m_bExpSighted; //ADDED
float m_expFactor; //ADDED
DECLARE_NETWORKCLASS();
DECLARE_PREDICTABLE();
#if !defined( CLIENT_DLL )
DECLARE_DATADESC();
#endif
Extra
Some of you might think the gun is is moving up to the designated point a bit too slow, then here is the solution to it. Nothing hard nothing particular cool stuff, just 2 lines of code.
Look for this line: float gIronsightedTime(0.0f); Below it add this.
float gMoveTime(0.1f); //Jorg40 - Seconds to use to move the model up to players view
Last line. Go to the delta float delta(gpGlobals->curtime - gIronsightedTime); Change it to look like this:
float delta((gpGlobals->curtime - gIronsightedTime) / gMoveTime);
Done! All we need to do is to change the script files.
weapon_pistol.txt
Add this somewhere around the "WeaponData" section in the script file.
ExpOffset
{
"x" "-15"
"y" "-5.5"
"z" "4.2"
// "xori" "-2.9" //Does not work
// "yori" "1.1" //Does not work
// "zori" "2.0" // Does not work
}
Singleplayer Differences
To achieve this in singleplayer make the following alterations:
Replace:
CWeaponHL2MPBase *pWeapon = dynamic_cast<CWeaponHL2MPBase *>(pPlayer->GetActiveWeapon());
with
CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon();
Replace:
CWeaponHL2MPBase *pWeapon = dynamic_cast<CWeaponHL2MPBase *>(ToHL2MPPlayer(owner)->GetActiveWeapon());
with
CBaseCombatWeapon *pWeapon = owner->GetActiveWeapon();
Replace:
ConVar cl_exp_test_wpn_offset("cl_exp_test_wpn_offset", "0", 0, "Tests weapon offsets", (FnChangeCallback_t)ExpWpnTestOffset);
with
ConVar cl_exp_test_wpn_offset("cl_exp_test_wpn_offset", "0", 0, "Tests weapon offsets", (FnChangeCallback)ExpWpnTestOffset);
Replace:
#include "convar.h"
#include "c_hl2mp_player.h"
#include "weapon_hl2mpbase.h"
with
#include "convar.h"
#include "c_baseplayer.h"
Replace:
pWeapon->GetHL2MPWpnData()
with
pWeapon->GetWpnData()
Results
- An example image is shown on the right.
Notes
Quote/Note from the real author: How you set m_expFactor (time based interpolation is the easiest) and m_bExpSighted (button press) is up to you. Also , consider how you may pass this information to the server so ironsighted mode can be propagated over to other clients (i.e. other players will see that you're in ironsighted mode).
Also in-game, open console, write cl_exp and all the commands that are needed will show up.
Note from Pretador: Adding the Keybinds: To add the keybind to toggle ironsight, open up your kb_act. Under "#Valve_Combat_Title" Add:
"ironsight_toggle" "#MOD_ironsight_toggle"
Then open your mod_english.txt in mod/resource and write:
"MOD_ironsight_toggle" "ironsight_toggle"