Battlefield Style Hitmarker

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).
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)