Difference between revisions of "Battlefield Style Hitmarker"

From Valve Developer Community
Jump to: navigation, search
m (WEAPON_X.CPP)
m (The Code)
Line 7: Line 7:
 
=The Code=
 
=The Code=
  
Create the following files and place them inside your client (C:\MOD\src\game\client).
+
Create the following files and place them inside your client (C:\MOD\src\game\client). Add them to the Client project in the game solution.
  
 
==HUD_HITMARKER.H==
 
==HUD_HITMARKER.H==

Revision as of 10:13, 9 January 2014

Note:This has been tested on the 2013 and 2007 SDK's.

The Goal

We are going to re-create battlefield style hitmarkers for all weapons, complete with animation. This is going to be a very simple copy & paste tutorial, and there is room for contribution and tweaks.

The Code

Create the following files and place them inside your client (C:\MOD\src\game\client). Add them to the Client project in the game solution.

HUD_HITMARKER.H

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Hit Marker
//
//=============================================================================//

#include "hudelement.h"
#include "hud_numericdisplay.h"
#include <vgui_controls/Panel.h>
 
class CHudHitmarker : public vgui::Panel, public CHudElement
{
	DECLARE_CLASS_SIMPLE( CHudHitmarker, vgui::Panel );
 
public:
	CHudHitmarker( const char *pElementName );
 
	void Init();
	void Reset();
	bool ShouldDraw();

	void MsgFunc_ShowHitmarker( bf_read &msg );
 
protected:
	virtual void ApplySchemeSettings(vgui::IScheme *scheme);
	virtual void Paint( void );
 
private:
	bool m_bHitmarkerShow;

	CPanelAnimationVar( Color, m_HitmarkerColor, "HitMarkerColor", "255 255 255 255" );
};

HUD_HITMARKER.CPP

//========= Copyright © 1996-2005, Valve Corporation, All rights reserved. ============//
//
// Purpose: Hit Marker
//
//=============================================================================//

#include "cbase.h"
#include "hudelement.h"
#include "hud_macros.h"
#include "hud_hitmarker.h"
#include "iclientmode.h"
#include "c_baseplayer.h"

// VGUI panel includes
#include <vgui_controls/AnimationController.h>
#include <vgui/ISurface.h>
#include <vgui/ILocalize.h>

using namespace vgui;
 
// memdbgon must be the last include file in a .cpp file!
#include "tier0/memdbgon.h"
 
DECLARE_HUDELEMENT( CHudHitmarker );
DECLARE_HUD_MESSAGE( CHudHitmarker, ShowHitmarker );

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
 
CHudHitmarker::CHudHitmarker( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, "HudHitmarker")
{
	vgui::Panel *pParent = g_pClientMode->GetViewport();
	SetParent( pParent );
 
	// Hitmarker will not show when the player is dead
	SetHiddenBits( HIDEHUD_PLAYERDEAD );
 
	int screenWide, screenTall;
	GetHudSize(screenWide, screenTall);
	SetBounds(0, 0, screenWide, screenTall);
}
 
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudHitmarker::Init()
{
	HOOK_HUD_MESSAGE( CHudHitmarker, ShowHitmarker );

	SetAlpha( 0 );
	m_bHitmarkerShow = false;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudHitmarker::Reset()
{
	SetAlpha( 0 );
	m_bHitmarkerShow = false;
}
 
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudHitmarker::ApplySchemeSettings( vgui::IScheme *scheme )
{
	BaseClass::ApplySchemeSettings(scheme);
 
	SetPaintBackgroundEnabled(false);
	SetPaintBorderEnabled(false);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
bool CHudHitmarker::ShouldDraw( void )
{
	return ( m_bHitmarkerShow && CHudElement::ShouldDraw() );
}
 
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudHitmarker::Paint( void )
{
        C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
        if (!pPlayer)
        {
                return;
        }
 
	if (m_bHitmarkerShow)
	{
		int		x,y;

		// Find our screen position to start from
		x = XRES(320);
		y = YRES(240);

		vgui::surface()->DrawSetColor( m_HitmarkerColor );
		vgui::surface()->DrawLine( x - 6, y - 5, x - 11, y - 10 );
		vgui::surface()->DrawLine( x + 5, y - 5, x + 10, y - 10 );
		vgui::surface()->DrawLine( x - 6, y + 5, x - 11, y + 10 );
		vgui::surface()->DrawLine( x + 5, y + 5, x + 10, y + 10 );
	}
}
 
//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void CHudHitmarker::MsgFunc_ShowHitmarker(bf_read &msg)
{
	m_bHitmarkerShow = msg.ReadByte();

	g_pClientMode->GetViewportAnimationController()->StartAnimationSequence("HitMarkerShow");
}

Implementation

Once compiled, we now have the hud element created. The next step is to call it in the code, for this we will add a usermessage which gets called when a weapon fires and sucessfully hits an enemy.

X_USERMESSAGES.CPP

Depending on your mod base this will be hl2_usermessages.cpp or sdk_usermessages.cpp.

Add the following line to bottom of the file

usermessages->Register( "ShowHitmarker", 1); // Show Hit Marker

WEAPON_X.CPP

For this example I will be using the weapon_pistol.cpp, you will have to add this to all weapons used in your mod. The reasoning for this in my case is not all weapons use the MAX_TRACE_LENGTH, and I need to set this accordingly per weapon, else you could add this in the Baseclass::PrimaryAttack.

Declare the following underneath the rest of the function declarations near the top of the file.

void	DrawHitmarker( void );

Implement the function you just declared as follows.

void CWeaponPistol::DrawHitmarker( void )
{
	CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );

	if ( pPlayer == NULL )
    { return; }

	#ifndef CLIENT_DLL
	CSingleUserRecipientFilter filter(pPlayer);
        UserMessageBegin(filter, "ShowHitmarker");
        WRITE_BYTE(1);
        MessageEnd();
	#endif
}

Inside your weapon file find the ::PrimaryAttack function and add under CBasePlayer *pOwner = ToBasePlayer( GetOwner() ); add:

    CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
    if ( !pPlayer )
      { return; }

Finally, add this snippet at the bottom of the ::PrimaryAttack function.

        // set up the vectors and traceline
	trace_t tr;
	Vector	vecStart, vecStop, vecDir;

	// get the angles
	AngleVectors( pPlayer->EyeAngles( ), &vecDir );

	// get the vectors
	vecStart = pPlayer->Weapon_ShootPosition();
	vecStop = vecStart + vecDir * MAX_TRACE_LENGTH;

	// do the traceline
	UTIL_TraceLine( vecStart, vecStop, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr );

	// check to see if we hit a Player
	if ( tr.m_pEnt )
		{
			if ( tr.m_pEnt->IsPlayer() )
			{
				DrawHitmarker();
			}
		}
	}

Animation

Open up your hudanimations.txt file and add the following to the bottom of the file.

event HitMarkerShow
{
	Animate HudHitmarker Alpha 255 Linear 0.2 0.4
	
	RunEvent HitMarkerHide 0.4
}

event HitMarkerHide
{
	Animate HudHitmarker Alpha 0 Linear 0.2 0.4
}

You can edit the values here to achieve the look and feel you like, but I feel these settings emulate Battlefield's Style nicely.

Finishing Up

Single Player Tweaks

For single player mods, you'll find you need to edit a line within the ::PrimaryAttack code we added, rather than detecting players we need to detect NPC's.

Simply change the following:

	// check to see if we hit a Player
	if ( tr.m_pEnt )
		{
			if ( tr.m_pEnt->IsPlayer() )
			{
				DrawHitmarker();
			}
		}

to this:

	// check to see if we hit an NPC
	if ( tr.m_pEnt )
		{
			if ( tr.m_pEnt->IsNPC() )
			{
				DrawHitmarker();
			}
		}

So there we go, a very basic but fully functional Battlefield Style Hitmarker.

There is a lot of room for tweaking, for instance this will currently show when you hit any Players/NPC's regardless of their Team or AI Relationship. You could think about adding in functionality to differentiate between friendly and enemy Players/NPC's and display different properties accordingly.

Enjoy,

Ferrety (ferrety6012@gmail.com)