Adding a Scope: Difference between revisions
TomEdwards (talk | contribs) (Category:Weapons programming, syntax highlight) |
|||
Line 8: | Line 8: | ||
This material must be setup to be loaded as a HUD texture. Do this by adding the following to <code>scripts/hud_textures.txt</code>: | This material must be setup to be loaded as a HUD texture. Do this by adding the following to <code>scripts/hud_textures.txt</code>: | ||
< | |||
<source lang=ini> | |||
sprites/640_hud | |||
{ | { | ||
TextureData | TextureData | ||
{ | { | ||
scope | |||
{ | { | ||
file scope | |||
x 0 | |||
y 0 | |||
width 512 | |||
height 512 | |||
} | } | ||
Line 25: | Line 26: | ||
} | } | ||
} | } | ||
</ | </source> | ||
== The HUD element == | == The HUD element == | ||
This element displays your scope texture on the HUD. Just add the following code to <code>hud_scope.cpp</code>, which you should create somewhere in your game's '''client''' project. | This element displays your scope texture on the HUD. Just add the following code to <code>hud_scope.cpp</code>, which you should create somewhere in your game's '''client''' project. | ||
< | |||
#include | <source lang=cpp> | ||
#include | #include cbase.h | ||
#include | #include hudelement.h | ||
#include | #include hud_macros.h | ||
#include | #include iclientmode.h | ||
#include c_basehlplayer.h | |||
#include <vgui/IScheme.h> | #include <vgui/IScheme.h> | ||
Line 40: | Line 43: | ||
// memdbgon must be the last include file in a .cpp file! | // memdbgon must be the last include file in a .cpp file! | ||
#include | #include tier0/memdbgon.h | ||
/** | /** | ||
Line 64: | Line 66: | ||
CHudTexture* m_pScope; | CHudTexture* m_pScope; | ||
}; | }; | ||
DECLARE_HUDELEMENT( CHudScope ); | DECLARE_HUDELEMENT( CHudScope ); | ||
Line 70: | Line 71: | ||
using namespace vgui; | using namespace vgui; | ||
/** | /** | ||
Line 76: | Line 76: | ||
* are instantiated. | * are instantiated. | ||
*/ | */ | ||
CHudScope::CHudScope( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, | CHudScope::CHudScope( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, HudScope) | ||
{ | { | ||
vgui::Panel *pParent = g_pClientMode->GetViewport(); | vgui::Panel *pParent = g_pClientMode->GetViewport(); | ||
Line 93: | Line 93: | ||
} | } | ||
/** | /** | ||
Line 104: | Line 103: | ||
m_bShow = false; | m_bShow = false; | ||
} | } | ||
/** | /** | ||
Line 118: | Line 116: | ||
if (!m_pScope) | if (!m_pScope) | ||
{ | { | ||
m_pScope = gHUD.GetIcon( | m_pScope = gHUD.GetIcon(scope); | ||
} | } | ||
} | } | ||
/** | /** | ||
Line 136: | Line 133: | ||
if (m_bShow) | if (m_bShow) | ||
{ | { | ||
//Perform depth hack to prevent | //Perform depth hack to prevent clips by world | ||
materials->DepthRange( 0.0f, 0.1f ); | materials->DepthRange( 0.0f, 0.1f ); | ||
Line 155: | Line 152: | ||
} | } | ||
} | } | ||
Line 166: | Line 162: | ||
m_bShow = msg.ReadByte(); | m_bShow = msg.ReadByte(); | ||
} | } | ||
</ | </source> | ||
Then you need to declare your HUD element in <code>scripts/HudLayout.res</code> with the following lines: | Then you need to declare your HUD element in <code>scripts/HudLayout.res</code> with the following lines: | ||
< | |||
<source lang=cpp> | |||
HudScope | HudScope | ||
{ | { | ||
fieldName HudScope | |||
xpos 0 | |||
ypos 0 | |||
visible 1 | |||
enabled 1 | |||
PaintBackgroundType 0 | |||
// These screw up the scope for users using a non standard screen ratio (i.e. 16:9) (lodle) | // These screw up the scope for users using a non standard screen ratio (i.e. 16:9) (lodle) | ||
// | // wide 640 | ||
// | // tall 480 | ||
} | } | ||
</ | </source> | ||
This will make the element cover the entire screen. | This will make the element cover the entire screen. | ||
== Telling the HUD element when to show == | == Telling the HUD element when to show == | ||
For this, a HUD (aka User) message is used, which is called <code>ShowScope</code>. To enable this, add the following line to your user messages registration code (e.g. <code>hl2_usermessages.cpp</code> or similar), in the <code>RegisterUserMessages()</code> function: | For this, a HUD (aka User) message is used, which is called <code>ShowScope</code>. To enable this, add the following line to your user messages registration code (e.g. <code>hl2_usermessages.cpp</code> or similar), in the <code>RegisterUserMessages()</code> function: | ||
< | |||
usermessages->Register( | <source lang=cpp> | ||
</ | usermessages->Register( ShowScope, 1); // show the sniper scope | ||
</source> | |||
Then all you need to do is fire off this message with either a true or false value (1 or 0) to show or hide the scope. For example, change the <code>CWeaponCrossbow::ToggleZoom()</code> function to the following: | Then all you need to do is fire off this message with either a true or false value (1 or 0) to show or hide the scope. For example, change the <code>CWeaponCrossbow::ToggleZoom()</code> function to the following: | ||
< | <source lang=cpp> | ||
void CWeaponCrossbow::ToggleZoom( void ) | void CWeaponCrossbow::ToggleZoom( void ) | ||
{ | { | ||
Line 211: | Line 211: | ||
// Send a message to hide the scope | // Send a message to hide the scope | ||
CSingleUserRecipientFilter filter(pPlayer); | CSingleUserRecipientFilter filter(pPlayer); | ||
UserMessageBegin(filter, | UserMessageBegin(filter, ShowScope); | ||
WRITE_BYTE(0); | WRITE_BYTE(0); | ||
MessageEnd(); | MessageEnd(); | ||
Line 224: | Line 224: | ||
// Send a message to Show the scope | // Send a message to Show the scope | ||
CSingleUserRecipientFilter filter(pPlayer); | CSingleUserRecipientFilter filter(pPlayer); | ||
UserMessageBegin(filter, | UserMessageBegin(filter, ShowScope); | ||
WRITE_BYTE(1); | WRITE_BYTE(1); | ||
MessageEnd(); | MessageEnd(); | ||
Line 230: | Line 230: | ||
} | } | ||
} | } | ||
</ | </source> | ||
== Making a weapon zoom == | == Making a weapon zoom == | ||
Line 237: | Line 237: | ||
To a very basic weapon like the .357, you need to add one boolean field and 5 new functions to achieve the zoom function like the crossbow. Bear in mind that many weapons (like the pistol) already define one or more of these functions, and all you need to do is add to it. | To a very basic weapon like the .357, you need to add one boolean field and 5 new functions to achieve the zoom function like the crossbow. Bear in mind that many weapons (like the pistol) already define one or more of these functions, and all you need to do is add to it. | ||
< | <source lang=cpp> | ||
public: | public: | ||
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); // Required so that you know to un-zoom when switching weapons | bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); // Required so that you know to un-zoom when switching weapons | ||
Line 248: | Line 248: | ||
bool m_bInZoom; // Set to true when you are zooming, false when not | bool m_bInZoom; // Set to true when you are zooming, false when not | ||
</ | </source> | ||
You would then add the following definitions for those functions (again, just add the relevant code to functions if they already exist): | You would then add the following definitions for those functions (again, just add the relevant code to functions if they already exist): | ||
< | <source lang=cpp> | ||
/** | /** | ||
* Check for weapon being holstered so we can disable scope zoom | * Check for weapon being holstered so we can disable scope zoom | ||
Line 265: | Line 265: | ||
return BaseClass::Holster( pSwitchingTo ); | return BaseClass::Holster( pSwitchingTo ); | ||
} | } | ||
/** | /** | ||
Line 277: | Line 276: | ||
BaseClass::ItemPostFrame(); | BaseClass::ItemPostFrame(); | ||
} | } | ||
/** | /** | ||
Line 289: | Line 287: | ||
BaseClass::ItemBusyFrame(); | BaseClass::ItemBusyFrame(); | ||
} | } | ||
/** | /** | ||
Line 303: | Line 300: | ||
} | } | ||
} | } | ||
/** | /** | ||
Line 324: | Line 320: | ||
// Send a message to hide the scope | // Send a message to hide the scope | ||
CSingleUserRecipientFilter filter(pPlayer); | CSingleUserRecipientFilter filter(pPlayer); | ||
UserMessageBegin(filter, | UserMessageBegin(filter, ShowScope); | ||
WRITE_BYTE(0); | WRITE_BYTE(0); | ||
MessageEnd(); | MessageEnd(); | ||
Line 337: | Line 333: | ||
// Send a message to Show the scope | // Send a message to Show the scope | ||
CSingleUserRecipientFilter filter(pPlayer); | CSingleUserRecipientFilter filter(pPlayer); | ||
UserMessageBegin(filter, | UserMessageBegin(filter, ShowScope); | ||
WRITE_BYTE(1); | WRITE_BYTE(1); | ||
MessageEnd(); | MessageEnd(); | ||
Line 343: | Line 339: | ||
} | } | ||
} | } | ||
</ | </source> | ||
== Alternatives == | == Alternatives == | ||
Line 350: | Line 346: | ||
In <code>HL2_Player.cpp</code> comment out the following block of code (line 523): | In <code>HL2_Player.cpp</code> comment out the following block of code (line 523): | ||
< | <source lang=cpp> | ||
if ( m_nButtons & IN_ZOOM ) | if ( m_nButtons & IN_ZOOM ) | ||
{ | { | ||
Line 356: | Line 352: | ||
m_nButtons &= ~(IN_ATTACK|IN_ATTACK2); | m_nButtons &= ~(IN_ATTACK|IN_ATTACK2); | ||
} | } | ||
</ | </source> | ||
== Different screen ratios == | == Different screen ratios == | ||
Line 364: | Line 360: | ||
Add to the declaration of the scope under private: | Add to the declaration of the scope under private: | ||
< | <source lang=cpp> | ||
char* scope_src[3]; | char* scope_src[3]; | ||
int m_iRes; | int m_iRes; | ||
</ | </source> | ||
Under <code>CHudScope::CHudScope</code> add: | Under <code>CHudScope::CHudScope</code> add: | ||
< | <source lang=cpp> | ||
scope_src[0] = | scope_src[0] = scope1640; | ||
scope_src[1] = | scope_src[1] = scope11280; | ||
scope_src[2] = | scope_src[2] = scope1720; | ||
</ | </source> | ||
This is an array which lists the scope names for the different screen ratio (in <code>hud_textures.res</code>). Do not add underscores ( | This is an array which lists the scope names for the different screen ratio (in <code>hud_textures.res</code>). Do not add underscores (_) to your names otherwise it causes a freak error and crash your game. | ||
Change <code>ApplySchemeSettings()</code> to: | Change <code>ApplySchemeSettings()</code> to: | ||
< | <source lang=cpp> | ||
void CHudScope::ApplySchemeSettings( vgui::IScheme *scheme ) | void CHudScope::ApplySchemeSettings( vgui::IScheme *scheme ) | ||
{ | { | ||
Line 387: | Line 382: | ||
SetPaintBorderEnabled(false); | SetPaintBorderEnabled(false); | ||
} | } | ||
</ | </source> | ||
Change <code>Paint()</code> to: | |||
<source lang=cpp> | |||
void CHudScope::Paint( void ) | void CHudScope::Paint( void ) | ||
{ | { | ||
Line 425: | Line 420: | ||
if (m_pScope) | if (m_pScope) | ||
{ | { | ||
//Performing depth hack to prevent | //Performing depth hack to prevent clips by world | ||
materials->DepthRange( 0.0f, 0.1f ); | materials->DepthRange( 0.0f, 0.1f ); | ||
Line 443: | Line 438: | ||
} | } | ||
} | } | ||
</ | </source> | ||
Also, add this to <code>hud_texture.txt</code> for the scope textures: | Also, add this to <code>hud_texture.txt</code> for the scope textures: | ||
<source lang=ini> | |||
scope1640 | |||
{ | |||
file HUD\scopes\scope_1_640_480 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope11280 | |||
{ | |||
file HUD\scopes\scope_1_1280_720 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
</ | height 512 | ||
} | |||
scope1720 | |||
{ | |||
file HUD\scopes\scope_1_720_480 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
</source> | |||
== Multiple scopes == | == Multiple scopes == | ||
Line 478: | Line 474: | ||
Add this to the declarations: | Add this to the declarations: | ||
< | <source lang=cpp> | ||
int m_iType; | int m_iType; | ||
</ | </source> | ||
Under <code>CHudScope::CHudScope</code> add in your extra scopes to the array. For example: | Under <code>CHudScope::CHudScope</code> add in your extra scopes to the array. For example: | ||
< | <source lang=cpp> | ||
scope_src[0] = scope1640; | |||
scope_src[1] = scope11280; | |||
scope_src[2] = scope1720; | |||
scope_src[3] = scope2640; | |||
scope_src[4] = scope21280; | |||
scope_src[5] = scope2720; | |||
scope_src[6] = scope3640; | |||
scope_src[7] = scope31280; | |||
scope_src[8] = scope3720; | |||
</ | </source> | ||
{{note|Make sure you increase the array size in the declaration to amount of scopes + 1.}} | {{note|Make sure you increase the array size in the declaration to amount of scopes + 1.}} | ||
In <code>Paint()</code> change: | In <code>Paint()</code> change: | ||
< | <source lang=cpp> | ||
m_pScope = gHUD.GetIcon( scope_src[ m_iRes ] ); | m_pScope = gHUD.GetIcon( scope_src[ m_iRes ] ); | ||
</ | </source> | ||
to | to | ||
< | <source lang=cpp> | ||
m_pScope = gHUD.GetIcon( scope_src[ 3*(m_iType - 1)+m_iRes ] ); | m_pScope = gHUD.GetIcon( scope_src[ 3*(m_iType - 1)+m_iRes ] ); | ||
</ | </source> | ||
change MsgFunc_ShowScope() to: | change MsgFunc_ShowScope() to: | ||
< | <source lang=cpp> | ||
void CHudScope::MsgFunc_ShowScope(bf_read &msg) | void CHudScope::MsgFunc_ShowScope(bf_read &msg) | ||
{ | { | ||
Line 526: | Line 521: | ||
} | } | ||
</ | </source> | ||
Now to change the scope type by weapon (or how ever you call it) just change the byte it sends in the msg. | Now to change the scope type by weapon (or how ever you call it) just change the byte it sends in the msg. | ||
< | |||
<source lang=cpp> | |||
WRITE_BYTE(0); // no scope | WRITE_BYTE(0); // no scope | ||
WRITE_BYTE(1); // scope 1 | WRITE_BYTE(1); // scope 1 | ||
WRITE_BYTE(2); // scope 2 | WRITE_BYTE(2); // scope 2 | ||
WRITE_BYTE(3); // scope 3 | WRITE_BYTE(3); // scope 3 | ||
</ | </source> | ||
Also add this to <code>hud_texture.txt</code> for the scope textures: | Also add this to <code>hud_texture.txt</code> for the scope textures: | ||
< | <source lang=ini> | ||
scope1640 | |||
{ | |||
file HUD\scopes\scope_1_640_480 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope11280 | |||
{ | |||
file HUD\scopes\scope_1_1280_720 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope1720 | |||
{ | |||
file HUD\scopes\scope_1_720_480 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope2640 | |||
{ | |||
file HUD\scopes\scope_2_640_480 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope21280 | |||
{ | |||
file HUD\scopes\scope_2_1280_720 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope2720 | |||
{ | |||
file HUD\scopes\scope_2_720_480 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope3640 | |||
{ | |||
file HUD\scopes\scope_3_640_480 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope31280 | |||
{ | |||
file HUD\scopes\scope_3_1280_720 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
scope3720 | |||
{ | |||
file HUD\scopes\scope_3_720_480 | |||
x 0 | |||
y 0 | |||
width 1024 | |||
height 512 | |||
} | |||
</ | </source> | ||
[[Category: | [[Category:Weapons programming]] | ||
[[Category:Tutorials]] | [[Category:Tutorials]] |
Revision as of 09:23, 15 August 2009
The code and ideas presented in this tutorial can be used to add a HUD-based sniper scope to your game, for use by any weapon that you see fit. As an example, it has been added alongside the existing zoom functionality of the HL2 crossbow. However, it is possible to tell it to display from any part of your code.
Prerequisites
The only prerequisite for this tutorial is that you have already created a sniper scope texture (e.g. a big black square/rectangle with a transparent circle in the middle of it) which has an appropriate .VMT file. Here is an example texture:
This material must be setup to be loaded as a HUD texture. Do this by adding the following to scripts/hud_textures.txt
:
sprites/640_hud
{
TextureData
{
scope
{
file scope
x 0
y 0
width 512
height 512
}
...
}
}
The HUD element
This element displays your scope texture on the HUD. Just add the following code to hud_scope.cpp
, which you should create somewhere in your game's client project.
#include cbase.h
#include hudelement.h
#include hud_macros.h
#include iclientmode.h
#include c_basehlplayer.h
#include <vgui/IScheme.h>
#include <vgui_controls/Panel.h>
// memdbgon must be the last include file in a .cpp file!
#include tier0/memdbgon.h
/**
* Simple HUD element for displaying a sniper scope on screen
*/
class CHudScope : public vgui::Panel, public CHudElement
{
DECLARE_CLASS_SIMPLE( CHudScope, vgui::Panel );
public:
CHudScope( const char *pElementName );
void Init();
void MsgFunc_ShowScope( bf_read &msg );
protected:
virtual void ApplySchemeSettings(vgui::IScheme *scheme);
virtual void Paint( void );
private:
bool m_bShow;
CHudTexture* m_pScope;
};
DECLARE_HUDELEMENT( CHudScope );
DECLARE_HUD_MESSAGE( CHudScope, ShowScope );
using namespace vgui;
/**
* Constructor - generic HUD element initialization stuff. Make sure our 2 member variables
* are instantiated.
*/
CHudScope::CHudScope( const char *pElementName ) : CHudElement(pElementName), BaseClass(NULL, HudScope)
{
vgui::Panel *pParent = g_pClientMode->GetViewport();
SetParent( pParent );
m_bShow = false;
m_pScope = 0;
// Scope will not show when the player is dead
SetHiddenBits( HIDEHUD_PLAYERDEAD );
// fix for users with diffrent screen ratio (Lodle)
int screenWide, screenTall;
GetHudSize(screenWide, screenTall);
SetBounds(0, 0, screenWide, screenTall);
}
/**
* Hook up our HUD message, and make sure we are not showing the scope
*/
void CHudScope::Init()
{
HOOK_HUD_MESSAGE( CHudScope, ShowScope );
m_bShow = false;
}
/**
* Load in the scope material here
*/
void CHudScope::ApplySchemeSettings( vgui::IScheme *scheme )
{
BaseClass::ApplySchemeSettings(scheme);
SetPaintBackgroundEnabled(false);
SetPaintBorderEnabled(false);
if (!m_pScope)
{
m_pScope = gHUD.GetIcon(scope);
}
}
/**
* Simple - if we want to show the scope, draw it. Otherwise don't.
*/
void CHudScope::Paint( void )
{
C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
if (!pPlayer)
{
return;
}
if (m_bShow)
{
//Perform depth hack to prevent clips by world
materials->DepthRange( 0.0f, 0.1f );
// This will draw the scope at the origin of this HUD element, and
// stretch it to the width and height of the element. As long as the
// HUD element is set up to cover the entire screen, so will the scope
m_pScope->DrawSelf(0, 0, GetWide(), GetTall(), Color(255,255,255,255));
//Restore depth
materials->DepthRange( 0.0f, 1.0f );
// Hide the crosshair
pPlayer->m_Local.m_iHideHUD |= HIDEHUD_CROSSHAIR;
}
else if ((pPlayer->m_Local.m_iHideHUD & HIDEHUD_CROSSHAIR) != 0)
{
pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_CROSSHAIR;
}
}
/**
* Callback for our message - set the show variable to whatever
* boolean value is received in the message
*/
void CHudScope::MsgFunc_ShowScope(bf_read &msg)
{
m_bShow = msg.ReadByte();
}
Then you need to declare your HUD element in scripts/HudLayout.res
with the following lines:
HudScope
{
fieldName HudScope
xpos 0
ypos 0
visible 1
enabled 1
PaintBackgroundType 0
// These screw up the scope for users using a non standard screen ratio (i.e. 16:9) (lodle)
// wide 640
// tall 480
}
This will make the element cover the entire screen.
Telling the HUD element when to show
For this, a HUD (aka User) message is used, which is called ShowScope
. To enable this, add the following line to your user messages registration code (e.g. hl2_usermessages.cpp
or similar), in the RegisterUserMessages()
function:
usermessages->Register( ShowScope, 1); // show the sniper scope
Then all you need to do is fire off this message with either a true or false value (1 or 0) to show or hide the scope. For example, change the CWeaponCrossbow::ToggleZoom()
function to the following:
void CWeaponCrossbow::ToggleZoom( void )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer == NULL )
return;
if ( m_bInZoom )
{
if ( pPlayer->SetFOV( this, 0, 0.2f ) )
{
m_bInZoom = false;
// Send a message to hide the scope
CSingleUserRecipientFilter filter(pPlayer);
UserMessageBegin(filter, ShowScope);
WRITE_BYTE(0);
MessageEnd();
}
}
else
{
if ( pPlayer->SetFOV( this, 20, 0.1f ) )
{
m_bInZoom = true;
// Send a message to Show the scope
CSingleUserRecipientFilter filter(pPlayer);
UserMessageBegin(filter, ShowScope);
WRITE_BYTE(1);
MessageEnd();
}
}
}
Making a weapon zoom
This section deals with actually adding zoom functionality to a weapon in the first place. Here's what you need to do - most of this code is lifted straight from CWeaponCrossbow
. For the purpose of this tutorial, zoom is being added to the .357 Magnum as a secondary fire (like the crossbow). If you want to implement it in a weapon which already uses the IN_ATTACK2
key to perform a secondary attack, you'll need to define and use a separate key for this.
To a very basic weapon like the .357, you need to add one boolean field and 5 new functions to achieve the zoom function like the crossbow. Bear in mind that many weapons (like the pistol) already define one or more of these functions, and all you need to do is add to it.
public:
bool Holster( CBaseCombatWeapon *pSwitchingTo = NULL ); // Required so that you know to un-zoom when switching weapons
void ItemPostFrame( void ); // Called every frame during normal weapon idle
void ItemBusyFrame( void ); // Called when the weapon is 'busy' e.g. reloading
protected:
void ToggleZoom( void ); // If the weapon is zoomed, un-zoom and vice versa
void CheckZoomToggle( void ); // Check if the secondary attack button has been pressed
bool m_bInZoom; // Set to true when you are zooming, false when not
You would then add the following definitions for those functions (again, just add the relevant code to functions if they already exist):
/**
* Check for weapon being holstered so we can disable scope zoom
*/
bool CWeapon357::Holster(CBaseCombatWeapon *pSwitchingTo /* = NULL */)
{
if ( m_bInZoom )
{
ToggleZoom();
}
return BaseClass::Holster( pSwitchingTo );
}
/**
* Check the status of the zoom key every frame to see if player is still zoomed in
*/
void CWeapon357::ItemPostFrame()
{
// Allow zoom toggling
CheckZoomToggle();
BaseClass::ItemPostFrame();
}
/**
* Check the status of the zoom key every frame to see if player is still zoomed in
*/
void CWeapon357::ItemBusyFrame( void )
{
// Allow zoom toggling even when we're reloading
CheckZoomToggle();
BaseClass::ItemBusyFrame();
}
/**
* Check if the zoom key was pressed in the last input tick
*/
void CWeapon357::CheckZoomToggle( void )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer && (pPlayer->m_afButtonPressed & IN_ATTACK2))
{
ToggleZoom();
}
}
/**
* If we're zooming, stop. If we're not, start.
*/
void CWeapon357::ToggleZoom( void )
{
CBasePlayer *pPlayer = ToBasePlayer( GetOwner() );
if ( pPlayer == NULL )
return;
if ( m_bInZoom )
{
// Narrowing the Field Of View here is what gives us the zoomed effect
if ( pPlayer->SetFOV( this, 0, 0.2f ) )
{
m_bInZoom = false;
// Send a message to hide the scope
CSingleUserRecipientFilter filter(pPlayer);
UserMessageBegin(filter, ShowScope);
WRITE_BYTE(0);
MessageEnd();
}
}
else
{
if ( pPlayer->SetFOV( this, 20, 0.1f ) )
{
m_bInZoom = true;
// Send a message to Show the scope
CSingleUserRecipientFilter filter(pPlayer);
UserMessageBegin(filter, ShowScope);
WRITE_BYTE(1);
MessageEnd();
}
}
}
Alternatives
An alternative way to add a scope (in HL2) is to allow firing while zooming. Whilst it applies to all weapons, it is far easier to implement.
In HL2_Player.cpp
comment out the following block of code (line 523):
if ( m_nButtons & IN_ZOOM )
{
//FIXME: Held weapons like the grenade get sad when this happens
m_nButtons &= ~(IN_ATTACK|IN_ATTACK2);
}
Different screen ratios
Scopes go funky when used on different screen ratios. Here is a fix for it but you will need to re-make the scope for each ratio (4:3, 16:9 and 16:10) other wise people with different ratios will see a stretched scope and have an unfair advantage - have the same size scope area but with extra blackness on the sides for widescreen users.
Add to the declaration of the scope under private:
char* scope_src[3];
int m_iRes;
Under CHudScope::CHudScope
add:
scope_src[0] = scope1640;
scope_src[1] = scope11280;
scope_src[2] = scope1720;
This is an array which lists the scope names for the different screen ratio (in hud_textures.res
). Do not add underscores (_) to your names otherwise it causes a freak error and crash your game.
Change ApplySchemeSettings()
to:
void CHudScope::ApplySchemeSettings( vgui::IScheme *scheme )
{
BaseClass::ApplySchemeSettings(scheme);
SetPaintBackgroundEnabled(false);
SetPaintBorderEnabled(false);
}
Change Paint()
to:
void CHudScope::Paint( void )
{
C_BasePlayer* pPlayer = C_BasePlayer::GetLocalPlayer();
if (!pPlayer)
{
return;
}
if (m_bShow)
{
vgui::Panel *pParent = g_pClientMode->GetViewport();
int screenWide=pParent->GetWide();
int screenTall=pParent->GetTall();
float ratio = ((float)screenWide)/((float)screenTall);
if (ratio >= 1.59 && ratio <= 1.61 )
{
m_iRes = 2;
}
else if (ratio >= 1.76 && ratio <= 1.78 )
{
m_iRes = 1;
}
else
{
m_iRes = 0;
}
m_pScope = gHUD.GetIcon( scope_src[ m_iRes ] );
if (m_pScope)
{
//Performing depth hack to prevent clips by world
materials->DepthRange( 0.0f, 0.1f );
m_pScope->DrawSelf(0, 0, pParent->GetWide(), pParent->GetTall(), Color(255,255,255,255));
//Restore depth
materials->DepthRange( 0.0f, 1.0f );
// Hide the crosshair
pPlayer->m_Local.m_iHideHUD |= HIDEHUD_CROSSHAIR;
}
}
else if ((pPlayer->m_Local.m_iHideHUD & HIDEHUD_CROSSHAIR) != 0)
{
pPlayer->m_Local.m_iHideHUD &= ~HIDEHUD_CROSSHAIR;
}
}
Also, add this to hud_texture.txt
for the scope textures:
scope1640
{
file HUD\scopes\scope_1_640_480
x 0
y 0
width 1024
height 512
}
scope11280
{
file HUD\scopes\scope_1_1280_720
x 0
y 0
width 1024
height 512
}
scope1720
{
file HUD\scopes\scope_1_720_480
x 0
y 0
width 1024
height 512
}
Multiple scopes
It's quite easy to add multiple scopes if you use the code for different screen ratios above.
Add this to the declarations:
int m_iType;
Under CHudScope::CHudScope
add in your extra scopes to the array. For example:
scope_src[0] = scope1640;
scope_src[1] = scope11280;
scope_src[2] = scope1720;
scope_src[3] = scope2640;
scope_src[4] = scope21280;
scope_src[5] = scope2720;
scope_src[6] = scope3640;
scope_src[7] = scope31280;
scope_src[8] = scope3720;

In Paint()
change:
m_pScope = gHUD.GetIcon( scope_src[ m_iRes ] );
to
m_pScope = gHUD.GetIcon( scope_src[ 3*(m_iType - 1)+m_iRes ] );
change MsgFunc_ShowScope() to:
void CHudScope::MsgFunc_ShowScope(bf_read &msg)
{
m_iType = msg.ReadByte();
if (m_iType >= 1)
{
m_bShow = true;
}
else
{
m_bShow = false;
}
}
Now to change the scope type by weapon (or how ever you call it) just change the byte it sends in the msg.
WRITE_BYTE(0); // no scope
WRITE_BYTE(1); // scope 1
WRITE_BYTE(2); // scope 2
WRITE_BYTE(3); // scope 3
Also add this to hud_texture.txt
for the scope textures:
scope1640
{
file HUD\scopes\scope_1_640_480
x 0
y 0
width 1024
height 512
}
scope11280
{
file HUD\scopes\scope_1_1280_720
x 0
y 0
width 1024
height 512
}
scope1720
{
file HUD\scopes\scope_1_720_480
x 0
y 0
width 1024
height 512
}
scope2640
{
file HUD\scopes\scope_2_640_480
x 0
y 0
width 1024
height 512
}
scope21280
{
file HUD\scopes\scope_2_1280_720
x 0
y 0
width 1024
height 512
}
scope2720
{
file HUD\scopes\scope_2_720_480
x 0
y 0
width 1024
height 512
}
scope3640
{
file HUD\scopes\scope_3_640_480
x 0
y 0
width 1024
height 512
}
scope31280
{
file HUD\scopes\scope_3_1280_720
x 0
y 0
width 1024
height 512
}
scope3720
{
file HUD\scopes\scope_3_720_480
x 0
y 0
width 1024
height 512
}