Tutorials

Coding a Basic Leaning System



This is for single player mods but can easily be put into use for multi-player mods.

First things first we need to create two new buttons one for leaning left and one for leaning right. So open up in_buttons.h.

If you know how to create a button skip this part.

Now you should see a bunch of button definitions for example.

#define IN_GRENADE2		(1 << 24)	// grenade 2


Ok so now we want to add our new buttons. After the last button definition add

#define IN_LEANLEFT		(1 << 25)   //Lean left
#define IN_LEANRIGHT	(1 << 26)   //Lean right


Now the (1 << 25 ) and (1 << 26) should be one higher than the next one. By default the last buttons defined is IN_GRENADE2 which is (1 << 25). So when I added IN_LEANLEFT I put (1 << 25) as 25 is one higher than 24.

Ok now we need to open in_main.cpp

On about line 103 you should see some kbutton_t classes being defined. We need to add our buttons to this section of code.

So After

static  kbutton_t   in_grenade2;


Add

static  kbutton_t	in_leanleft;
static  kbutton_t   in_leanright;



So we have now defined our two buttons. Now we need to create a function for when the button is pressed and when it is released.

So scroll down to around line 468 and you should see aload of functions similar to theese.

void IN_Grenade2Up( const CCommand &args ) { KeyUp( &in_grenade2, args[1] ); }
void IN_Grenade2Down( const CCommand &args ) { KeyDown( &in_grenade2, args[1] ); }



Now we are going to create 4 four new functions after these two. Two for each button. One to handle when the button is pressed and one to handle when the buttons is released.

Add

void IN_LeanLeftDown( const CCommand &args ) { KeyDown( &in_leanleft, args[1] ); }
void IN_LeanLeftUp( const CCommand &args ) { KeyUp( &in_leanleft, args[1] ); }
void IN_LeanRightDown( const CCommand &args ) {KeyDown(&in_leanright, args[1] ); }
void IN_LeanRightUp( const CCommand &args ) {KeyUp(&in_leanright, args[1] ); }



Now you may be wondering what this code all does it is quite simple really.
We are creating a new function and parsing it a CCommand which points to args. Then inside of that function we call the KeyUp or KeyDown function and parse that a pointer to the kbutton_t that we created earlier. We are also parsing it the args with the value 1.

Now as c++ doesn't count white space this could also be written as

void IN_LeanRightUp( const CCommand &args ) 
{
KeyUp(&in_leanright, args[1] );
 }


This shows the structure of the code in a more text-book manner.

So now we need to scroll all the way down to line 1308.

You should now see some code that looks like this.

CalcButtonBits( bits, IN_GRENADE2, s_ClearInputState, &in_grenade2, bResetState );


So below this add

CalcButtonBits( bits, IN_LEANLEFT, s_ClearInputState, &in_leanleft, bResetState );
	CalcButtonBits( bits, IN_LEANRIGHT, s_ClearInputState, &in_leanright, bResetState );


Right so this code calls the CalcButtonBits function and parses it a integer called bits, we then parse this function out button we defined inside in_buttons.h, then we parse it a variable that is defined earlier in this file, then we parse it a pointer to our kbutton_t that we defined earlier. then finally we parse a integer called bResetState.
Now if we look at the function this is all contained within.

int CInput::GetButtonBits( int bResetState )
{
	int bits = 0;


you can see the bResetState is parsed into the main function.

Ok with that all done we have now created a button and this next little part is optional but it can be quite useful.

We are now going to created ConCommand that press our and release out button. A ConCommand is a console command in case you didn't know.

Ok scroll down to line 1464 and you should see

static ConCommand endgrenade2( "-grenade2", IN_Grenade2Up );
static ConCommand startgrenade2( "+grenade2", IN_Grenade2Down );


Now we are going to create some of our own.
Add this below

static ConCommand startleanleft( "+leanleft", IN_LeanLeftDown );
static ConCommand endleanleft( "-leanleft", IN_LeanLeftUp );
static ConCommand startleanright( "+leanright", IN_LeanRightDown );
static ConCommand endleanright( "-leanright", IN_LeanRightUp );


So what we have done their is create our console commands. Now we parsed the ConCommand a string which is what you type into the console to activate this command, then we parse it our void we created earlier.

If you want to add your button to the keyboard section of the options menu gui. It is quite simple.
Open kb_act.lst (inside the scripts folder of your mod directory) and add

"+leanleft"				"Lean Left"
"+leanright"			"Lean Right"


Under the section you want them to be listed.

Now we have finished everything to do with button creation. We can move onto creating our lean functionality. I've decided to place my code inside the hl2_player.cpp and hl2_player.h file. The code can be adapted to be put inside of gamemovement.cpp and .h if need be.

First off open hl2_player.h and scroll down to about line 297 this is just above the protected section of the class definition.

Now we need to define our function and variables.
So add

//New Leaning stuff
	void CheckLean();
	void StartLeaning();
	void StopLeaning();
	bool IsLeaning(){return m_bIsLeaning;}


Now if we were to compile now we would get an error because m_bIsLeaning isn't defined yet. We are going to define that now.

Scroll down into the protected section of the class and add.

	//New Leaning stuff
	bool				m_bIsLeaning;



Ok now open up hl2_player.cpp and scroll all the way down to line 3771 this should be after the FirePlayerProxyOutput function. This is where we are going to add our code. It doesn't matter were we add it really but I like this position as it is after all the stock coder for the class CHL2_Player.

First we are going to add the checklean code so add

void CHL2_Player::CheckLean()
{
	if(IsSuitEquipped())
		{
			if(m_afButtonPressed & IN_LEANLEFT)
				StartLeaning();
			else if(m_afButtonPressed & IN_LEANRIGHT)
				StartLeaning();
			else if (m_afButtonReleased & IN_LEANLEFT)
				StopLeaning();
			else if (m_afButtonReleased & IN_LEANRIGHT)
				StopLeaning();
		}
	else
		return;
}


Ok what this code does is quite simple, first it check if the player has a suit equipped and if not it returns. If the player does have a suit equipped it checks if the Leanleft button is pressed and if so it calls the leaning function, it then does the same thing for the LeanRight button. Then if none of the buttons are pressed it checks if the buttons has just been released and if so it calls the StopLeaning function.

After this function we are going to add our start leaning function so add

void CHL2_Player::StartLeaning()
{
	if(IsSprinting())
		StopSprinting();

	if(IsZooming())
		StopZooming();

	//Create new vectors
	Vector lean,currentoffset,newoffset;
	currentoffset = GetViewOffset();
	AngleVectors(EyeAngles(), NULL, &lean, NULL);
	newoffset = currentoffset;
	if(m_nButtons & IN_LEANLEFT)
	{
		lean *= -25;
		newoffset.x = clamp(lean.x, -40, 40);
		newoffset.y = clamp(lean.y, -40, 40);
		SetViewOffset( newoffset );
	}
	else if(m_nButtons & IN_LEANRIGHT)
	{
		lean *= 25;
		newoffset.x = clamp(lean.x, -40, 40);
		newoffset.y = clamp(lean.y, -40, 40);
		SetViewOffset( newoffset );
	}
	m_bIsLeaning = true;
}


Now this code is slightly more complicated so I will try my best to explain it. At first we check if we are sprint and if so we stop the player sprinting, we then check to see if we are zooming and if so we stop the player from zooming.

We then go ahead and create three new vectors called lean, currentoffset and newoffset. We then set currentoffset to equal the current view offset by calling the GetViewOffset function.

We then call a math function that get the vectors of an QAngle class and assigns them to defined vectors. The QAngle class we want to get our vectors from is returned from the EyeAngles function so we call that. We then only want to get the right vector so we call a NULL for the forward and Up vectors. But we parse the lean vector for right (side) vector.

Now we set the newoffset to equal the currentoffset. Then we check if the IN_LeanLeft buttons is pressed down and if it is we will times the lean vector (which goes right) by -10. We times by minus ten to make it go left instead of right. We then add the lean vector to the newoffset but also clamp the vector in both the X and Y direction. We only allow it to go between -40 and 40 for both axis. We then set the viewoffset to equal our new offset.

Next we do the same but for the right side therefore this time we times by ten to move it to the right.

After all these if statements we set our bool valve to equal true because we are leaning.

So now we need to add code to our StopLeaning function so after the StartLeaning function add

void CHL2_Player::StopLeaning()
{
	if (IsDucking())
		SetViewOffset( VEC_DUCK_VIEW );

	else
		SetViewOffset( VEC_VIEW );

	m_bIsLeaning = false;
}



This code if far simpler, basically we check if the player is ducking and if so we set the view offset to a predefined offset that equates to ducking. If we aren't ducking we set our view offset to equal the standard view offset.

So yeah now we got leaning right?
Well no not just yet.

We basically need to call our check lean function each frame to see if we need to lean. So do a find (Ctrl + F) for ItemPostFrame this should take you to a function that is called after each frame. So inside this function call CheckLean.

void CHL2_Player::ItemPostFrame()
{
	BaseClass::ItemPostFrame();

	if ( m_bPlayUseDenySound )
	{
		m_bPlayUseDenySound = false;
		EmitSound( "HL2Player.UseDeny" );
	}

	CheckLean();

	if(IsLeaning())
		Weapon_Lower();
}


Now you can see I added it after the EmitSound call.
Also I gave a use for the IsLeaning bool we created earlier. Basically I added a check to see if we are leaning and if so I decided to lower the players weapon.

So more use for the IsLeaning bool include.
In the CanSprint function you can add

&& !IsLeaning()


Inside the if statement to stop the player from sprinting while they are leaning.

Also Inside the CanZoom function I added

//If leaning don't
	if (IsLeaning())
		return false;


To stop the player from zooming while leaning.

Because I defined the bool IsLeaning() in the public section of the class you can use it pretty much anywhere/ any file.

Notes: This is just basic leaning you will probably want to add screen rotation as well.
you can also network the m_bIsleaning bool so you can use it on the client side.
If you see any errors please let me know I will try to fix them.

Sorry forgot to mention to increase the distance increase the clamp value and increase the value you time the lean vector by.

Right here is a video of it working in-game (I had quite low values on at his point which I edited later and put into the tutorial.



-Surfa

View comments ( 13 )

Back to top