game_timer entity and HL2 generic timer HUD element

This commit is contained in:
ALLEN-PC\acj30 2025-07-07 10:06:38 -05:00 committed by Blixibon
parent 793d8df44e
commit 8de011987b
5 changed files with 1319 additions and 0 deletions

View File

@ -51,6 +51,8 @@ $Project
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.h"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.cpp"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.h"
$File "$SRCDIR\game\shared\mapbase\game_timer.cpp"
$File "$SRCDIR\game\shared\mapbase\game_timer.h"
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT]
@ -73,6 +75,7 @@ $Project
$File "mapbase\c_vgui_text_display.cpp"
$File "mapbase\c_weapon_custom_hl2.cpp"
$File "mapbase\mapbase_autocubemap.cpp"
$File "mapbase\hud_generic_timer.cpp"
}
$Folder "HL2 DLL"

View File

@ -0,0 +1,511 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: Generic timer HUD element for HL2-based mods
//
// Author: Blixibon
//
//=============================================================================//
#include "cbase.h"
#include "hud.h"
#include "hud_macros.h"
#include "hudelement.h"
#include "hud_numericdisplay.h"
#include "iclientmode.h"
#include <vgui/ILocalize.h>
#include <vgui/ISurface.h>
#include <vgui_controls/AnimationController.h>
#include <vgui_controls/EditablePanel.h>
#include "baseviewport.h"
#include "mapbase/game_timer.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern bool g_bAnyGameTimerActive;
ConVar hud_timer_max_bars( "hud_timer_max_bars", "15" );
//-----------------------------------------------------------------------------
// Purpose: Timer panel
//-----------------------------------------------------------------------------
class CHudGenericGameTimer : public CHudElement, public vgui::Panel
{
DECLARE_CLASS_SIMPLE( CHudGenericGameTimer, vgui::Panel );
public:
CHudGenericGameTimer( const char *pElementName );
virtual void Init( void );
virtual void VidInit( void );
virtual void Reset( void );
virtual void OnThink();
virtual void ApplySchemeSettings( vgui::IScheme *pScheme );
virtual bool ShouldDraw( void );
virtual void Paint();
C_GameTimer *GetTimer();
bool FindTimer();
void SetDisplayValue( int value ) { m_iValue = value; }
void SetLabelText( const wchar_t *text );
virtual void PaintNumbers( vgui::HFont font, int xpos, int ypos );
virtual void PaintBars( int xpos, int ypos, int wide );
int GetTimerIndex() const { return m_iTimerIndex; }
private:
void SetTimerLabel( void );
private:
int m_iTimerIndex;
bool m_bTimerDisplayed;
bool m_bPlayingFadeout;
float m_flShutoffTime;
bool m_bTimerPaused;
bool m_bTimerWarned;
int m_iValue;
int m_iTimerMaxLength;
bool m_bShowTimeRemaining;
int m_nProgressBarMax;
int m_nProgressBarOverride;
float m_flOverrideX, m_flOverrideY;
wchar_t m_LabelText[32];
int m_nLabelWidth, m_nLabelHeight;
int m_nTimerWidth, m_nTimerHeight;
CPanelAnimationVarAliasType( float, m_flMinWidth, "MinWidth", "100", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flBorder, "Border", "24", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flLabelTimerSpacing, "LabelTimerSpacing", "2", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flBarHeight, "BarHeight", "5", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flBarChunkGap, "BarChunkGap", "3", "proportional_float" );
CPanelAnimationVarAliasType( float, m_flBarVerticalGap, "BarVerticalGap", "8", "proportional_float" );
CPanelAnimationVar( int, m_iBarDisabledAlpha, "BarDisabledAlpha", "70" );
CPanelAnimationVar( float, m_flBlur, "Blur", "0" );
CPanelAnimationVar( vgui::HFont, m_hNumberFont, "NumberFont", "HudHintTextLarge" );
CPanelAnimationVar( vgui::HFont, m_hNumberGlowFont, "NumberGlowFont", "HudHintTextLarge" );
CPanelAnimationVar( vgui::HFont, m_hTextFont, "TextFont", "HudHintTextSmall" );
};
DECLARE_HUDELEMENT( CHudGenericGameTimer );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
CHudGenericGameTimer::CHudGenericGameTimer( const char *pElementName )
: CHudElement( pElementName ),
BaseClass( NULL, "HudGenericGameTimer" )
{
Panel *pParent = g_pClientMode->GetViewport();
SetParent( pParent );
SetHiddenBits( HIDEHUD_MISCSTATUS );
m_iTimerIndex = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::Init()
{
Reset();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::Reset()
{
SetDisplayValue( 0 );
SetAlpha( 0 );
m_flShutoffTime = 0.0f;
m_bTimerDisplayed = false;
m_iTimerIndex = 0;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::VidInit()
{
Reset();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::OnThink()
{
C_BasePlayer *local = C_BasePlayer::GetLocalPlayer();
if ( !local )
{
// Not ready to init!
return;
}
// If our timer has been disabled, close the menu
C_GameTimer *pTimer = GetTimer();
if ( !pTimer || pTimer->IsDisabled() || pTimer->IsMarkedForDeletion() )
{
if ( m_flShutoffTime == 0.0f )
{
m_flShutoffTime = gpGlobals->curtime + 1.0f;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerClose" );
}
m_iTimerIndex = 0;
return;
}
// Check pause state
if ( m_bTimerPaused && !pTimer->IsTimerPaused() )
{
m_bTimerPaused = false;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerPulse" );
}
else if ( pTimer->IsTimerPaused() )
{
m_bTimerPaused = true;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerPulseOnce" );
}
float flTimeRemaining = floorf( pTimer->GetTimeRemaining() );
// Check warn state
float flWarnTime = pTimer->GetWarnTime();
if (flWarnTime > 0.0f)
{
if ( m_bTimerWarned && flTimeRemaining > flWarnTime )
{
// Turn back to normal
m_bTimerWarned = false;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerUnwarn" );
}
else if ( flTimeRemaining <= flWarnTime )
{
// Turn red
m_bTimerWarned = true;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( "GenericGameTimerWarn" );
}
}
SetDisplayValue( flTimeRemaining );
m_iTimerMaxLength = pTimer->GetTimerMaxLength();
m_bShowTimeRemaining = pTimer->ShowTimeRemaining();
m_nProgressBarMax = pTimer->GetProgressBarMaxSegments();
if (m_nProgressBarMax == -1)
{
// Default to timer max length
m_nProgressBarMax = min( m_iTimerMaxLength, hud_timer_max_bars.GetInt() );
}
m_nProgressBarOverride = pTimer->GetProgressBarOverride();
pTimer->GetPositionOverride( m_flOverrideX, m_flOverrideY );
}
//-----------------------------------------------------------------------------
// Purpose: hud scheme settings
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
SetPaintBackgroundEnabled( false );
// set our size
int screenWide, screenTall;
int x, y;
GetPos( x, y );
GetHudSize( screenWide, screenTall );
SetBounds( 0, y, screenWide, screenTall - y );
// Start with a 0:00 timer
m_nTimerWidth = (vgui::surface()->GetCharacterWidth( m_hNumberFont, '0' ) * 3);
m_nTimerWidth += vgui::surface()->GetCharacterWidth( m_hNumberFont, ':' );
m_nTimerHeight = vgui::surface()->GetFontTall( m_hNumberFont );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CHudGenericGameTimer::ShouldDraw( void )
{
if ( !CHudElement::ShouldDraw() )
return false;
if ( !m_bTimerDisplayed )
{
// Check if we should find a new timer
if ( g_bAnyGameTimerActive )
{
if ( FindTimer() )
return true;
}
return false;
}
// check for if menu is set to disappear
if ( m_flShutoffTime > 0 && m_flShutoffTime <= gpGlobals->curtime )
{
// times up, shutoff
m_bTimerDisplayed = false;
return false;
}
if ( m_flOverrideX == -1.0f && m_flOverrideY == -1.0f )
{
// Don't overlap with the weapon selection HUD
vgui::Panel *pWeaponSelection = GetParent()->FindChildByName( "HudWeaponSelection" );
if ( pWeaponSelection && pWeaponSelection->IsVisible() )
return false;
}
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
inline C_GameTimer *CHudGenericGameTimer::GetTimer()
{
if (m_iTimerIndex <= 0)
return NULL;
// Need to do a dynamic_cast because this entity index could've been replaced since it was last used
return dynamic_cast<C_GameTimer*>(C_BaseEntity::Instance( m_iTimerIndex ));
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool CHudGenericGameTimer::FindTimer()
{
// Find a new timer
for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ )
{
C_GameTimer *pTimer = static_cast<C_GameTimer *>( IGameTimerAutoList::AutoList()[i] );
if ( !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() )
{
m_iTimerIndex = pTimer->entindex();
break;
}
}
C_GameTimer *pTimer = GetTimer();
if ( pTimer )
{
// New timer selected, set the caption
wchar_t wszLabelText[128];
const wchar_t *wLocalizedItem = g_pVGuiLocalize->Find( pTimer->GetTimerCaption() );
if (wLocalizedItem)
{
V_wcsncpy( wszLabelText, wLocalizedItem, sizeof( wszLabelText ) );
}
else
{
g_pVGuiLocalize->ConvertANSIToUnicode( pTimer->GetTimerCaption(), wszLabelText, sizeof( wszLabelText ) );
}
SetLabelText( wszLabelText );
m_flShutoffTime = 0;
m_bTimerDisplayed = true;
m_bTimerPaused = pTimer->IsTimerPaused();
m_bTimerWarned = false;
g_pClientMode->GetViewportAnimationController()->StartAnimationSequence( m_bTimerPaused ? "GenericGameTimerShow" : "GenericGameTimerShowFlash" );
SetDisplayValue( ceil( pTimer->GetTimeRemaining() ) );
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::SetLabelText( const wchar_t *text )
{
wcsncpy( m_LabelText, text, sizeof( m_LabelText ) / sizeof( wchar_t ) );
m_LabelText[(sizeof( m_LabelText ) / sizeof( wchar_t )) - 1] = 0;
m_nLabelWidth = 0;
m_nLabelHeight = 0;
if (m_LabelText[0] != '\0')
{
int nLabelLen = V_wcslen( m_LabelText );
for (int ch = 0; ch < nLabelLen; ch++)
{
m_nLabelWidth += vgui::surface()->GetCharacterWidth( m_hTextFont, m_LabelText[ch] );
}
m_nLabelHeight = vgui::surface()->GetFontTall( m_hTextFont );
m_nLabelHeight += m_flLabelTimerSpacing;
}
}
//-----------------------------------------------------------------------------
// Purpose: paints a number at the specified position
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::PaintNumbers( vgui::HFont font, int xpos, int ypos )
{
vgui::surface()->DrawSetTextFont(font);
int nTimeToDisplay = m_iValue;
if ( !m_bShowTimeRemaining )
{
nTimeToDisplay = m_iTimerMaxLength - nTimeToDisplay;
}
int nMinutes = 0;
int nSeconds = 0;
wchar_t unicode[6];
if (nTimeToDisplay <= 0)
{
nMinutes = 0;
nSeconds = 0;
}
else
{
nMinutes = nTimeToDisplay / 60;
nSeconds = nTimeToDisplay % 60;
}
V_snwprintf( unicode, ARRAYSIZE(unicode), L"%d:%02d", nMinutes, nSeconds );
vgui::surface()->DrawSetTextPos( xpos, ypos );
vgui::surface()->DrawUnicodeString( unicode );
// draw the overbright blur
for (float fl = m_flBlur; fl > 0.0f; fl -= 1.0f)
{
if (fl >= 1.0f)
{
vgui::surface()->DrawSetTextPos( xpos, ypos );
vgui::surface()->DrawUnicodeString( unicode );
}
else
{
// draw a percentage of the last one
Color col = GetFgColor();
col[3] *= fl;
vgui::surface()->DrawSetTextColor( col );
vgui::surface()->DrawSetTextPos( xpos, ypos );
vgui::surface()->DrawUnicodeString( unicode );
}
}
}
//-----------------------------------------------------------------------------
// Purpose: paints bars at the specified position
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::PaintBars( int xpos, int ypos, int wide )
{
// get bar chunks
int barChunkWidth = (wide / m_nProgressBarMax) - m_flBarChunkGap;
int enabledChunks;
if (m_nProgressBarOverride > -1)
enabledChunks = m_nProgressBarOverride;
else
{
enabledChunks = (int)floorf( ((float)m_iValue / (float)m_iTimerMaxLength) * (float)m_nProgressBarMax );
if (!m_bShowTimeRemaining)
{
enabledChunks = m_nProgressBarMax - enabledChunks;
}
}
// draw the suit power bar
vgui::surface()->DrawSetColor( GetFgColor() );
for (int i = 0; i < enabledChunks; i++)
{
vgui::surface()->DrawFilledRect( xpos, ypos, xpos + barChunkWidth, ypos + m_flBarHeight );
xpos += (barChunkWidth + m_flBarChunkGap);
}
// draw the exhausted portion of the bar.
Color clrExhausted = GetFgColor();
clrExhausted[3] = ((float)clrExhausted[3] / 255.0f) * m_iBarDisabledAlpha;
vgui::surface()->DrawSetColor( clrExhausted );
for (int i = enabledChunks; i < m_nProgressBarMax; i++)
{
vgui::surface()->DrawFilledRect( xpos, ypos, xpos + barChunkWidth, ypos + m_flBarHeight );
xpos += (barChunkWidth + m_flBarChunkGap);
}
}
//-----------------------------------------------------------------------------
// Purpose: renders the vgui panel
//-----------------------------------------------------------------------------
void CHudGenericGameTimer::Paint()
{
// Check for 00:00 timer
int nTimerWidth = m_nTimerWidth;
if (m_iValue > 600)
nTimerWidth += vgui::surface()->GetCharacterWidth( m_hNumberFont, '0' );
// draw the background
int wide = max( nTimerWidth, m_nLabelWidth ) + m_flBorder;
if (m_flMinWidth > wide)
wide = m_flMinWidth;
int tall = m_nTimerHeight + m_nLabelHeight + (m_flBorder/2);
if (m_nProgressBarMax > 0)
tall += m_flBarHeight + m_flBarVerticalGap;
else
tall += (m_flBorder/2);
int screenW, screenH;
GetHudSize( screenW, screenH );
float flScalarX = (m_flOverrideX != -1.0f ? m_flOverrideX : 0.5f);
float flScalarY = (m_flOverrideY != -1.0f ? m_flOverrideY : 0.05f);
int x = (screenW - wide) * flScalarX;
int y = (screenH - tall) * flScalarY;
DrawBox( x, y, wide, tall, GetBgColor(), 1.0f);
y += (m_flBorder/2);
// draw our bars
if (m_nProgressBarMax > 0)
{
int barX = x + (m_flBorder/2);
PaintBars( barX, y, wide - m_flBorder );
y += m_flBarVerticalGap;
}
if (m_nLabelHeight > 0)
{
vgui::surface()->DrawSetTextFont( m_hTextFont );
vgui::surface()->DrawSetTextColor( GetFgColor() );
vgui::surface()->DrawSetTextPos( x + ((wide - m_nLabelWidth) * 0.5f), y );
vgui::surface()->DrawUnicodeString( m_LabelText );
y += m_nLabelHeight;
}
// draw our numbers
vgui::surface()->DrawSetTextColor( GetFgColor() );
int digitX = x + ((wide - nTimerWidth) * 0.5f);
int digitY = y;
PaintNumbers( m_hNumberFont, digitX, digitY );
}

View File

@ -50,6 +50,8 @@ $Project
$File "$SRCDIR\game\shared\mapbase\mapbase_playeranimstate.h"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.cpp"
$File "$SRCDIR\game\shared\mapbase\protagonist_system.h"
$File "$SRCDIR\game\shared\mapbase\game_timer.cpp"
$File "$SRCDIR\game\shared\mapbase\game_timer.h"
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.cpp" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_funcs_shared.h" [$MAPBASE_VSCRIPT]
$File "$SRCDIR\game\shared\mapbase\vscript_singletons.cpp" [$MAPBASE_VSCRIPT]

View File

@ -0,0 +1,658 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: Generic custom timer entity
//
// Author: Blixibon
//
//=============================================================================//
#include "cbase.h"
#include "game_timer.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
extern bool IsInCommentaryMode( void );
ConVar sv_game_menu_default_warn_frac( "sv_game_menu_default_warn_frac", "0.25", FCVAR_REPLICATED );
LINK_ENTITY_TO_CLASS( game_timer, CGameTimer );
IMPLEMENT_NETWORKCLASS_ALIASED( GameTimer, DT_GameTimer )
BEGIN_NETWORK_TABLE_NOBASE( CGameTimer, DT_GameTimer )
#ifdef CLIENT_DLL
RecvPropBool( RECVINFO( m_bTimerPaused ) ),
RecvPropTime( RECVINFO( m_flTimerInitialLength ) ),
RecvPropTime( RECVINFO( m_flTimerMaxLength ) ),
RecvPropTime( RECVINFO( m_flTimeRemaining ) ),
RecvPropTime( RECVINFO( m_flTimerEndTime ) ),
RecvPropTime( RECVINFO( m_flWarnTime ) ),
RecvPropBool( RECVINFO( m_bIsDisabled ) ),
RecvPropBool( RECVINFO( m_bStartPaused ) ),
RecvPropBool( RECVINFO( m_bShowTimeRemaining ) ),
RecvPropInt( RECVINFO( m_nProgressBarMaxSegments ) ),
RecvPropInt( RECVINFO( m_nProgressBarOverride ) ),
RecvPropFloat( RECVINFO( m_flOverrideX ) ),
RecvPropFloat( RECVINFO( m_flOverrideY ) ),
RecvPropString( RECVINFO( m_szTimerCaption ) ),
RecvPropInt( RECVINFO( m_iTeamNum ) ),
#else
SendPropBool( SENDINFO( m_bTimerPaused ) ),
SendPropTime( SENDINFO( m_flTimerInitialLength ) ),
SendPropTime( SENDINFO( m_flTimerMaxLength ) ),
SendPropTime( SENDINFO( m_flTimeRemaining ) ),
SendPropTime( SENDINFO( m_flTimerEndTime ) ),
SendPropTime( SENDINFO( m_flWarnTime ) ),
SendPropBool( SENDINFO( m_bIsDisabled ) ),
SendPropBool( SENDINFO( m_bStartPaused ) ),
SendPropBool( SENDINFO( m_bShowTimeRemaining ) ),
SendPropInt( SENDINFO( m_nProgressBarMaxSegments ) ),
SendPropInt( SENDINFO( m_nProgressBarOverride ) ),
SendPropFloat( SENDINFO( m_flOverrideX ) ),
SendPropFloat( SENDINFO( m_flOverrideY ) ),
SendPropString( SENDINFO( m_szTimerCaption ) ),
SendPropInt( SENDINFO( m_iTeamNum ), TEAMNUM_NUM_BITS, 0 ),
#endif
END_NETWORK_TABLE()
#ifndef CLIENT_DLL
BEGIN_DATADESC( CGameTimer )
DEFINE_KEYFIELD( m_flTimerInitialLength, FIELD_FLOAT, "timer_length" ),
DEFINE_KEYFIELD( m_flTimerMaxLength, FIELD_FLOAT, "max_length" ),
DEFINE_KEYFIELD( m_flWarnTime, FIELD_FLOAT, "warn_time" ),
DEFINE_KEYFIELD( m_bIsDisabled, FIELD_BOOLEAN, "StartDisabled" ),
DEFINE_KEYFIELD( m_bStartPaused, FIELD_BOOLEAN, "start_paused" ),
DEFINE_KEYFIELD( m_bShowTimeRemaining, FIELD_BOOLEAN, "show_time_remaining" ),
DEFINE_KEYFIELD( m_bDisableOnFinish, FIELD_BOOLEAN, "disable_on_finish" ),
DEFINE_KEYFIELD( m_bShowInHUD, FIELD_BOOLEAN, "show_in_hud" ),
DEFINE_KEYFIELD( m_nProgressBarMaxSegments, FIELD_INTEGER, "progress_bar_max" ),
DEFINE_KEYFIELD( m_nProgressBarOverride, FIELD_INTEGER, "progress_bar_override" ),
DEFINE_KEYFIELD( m_flOverrideX, FIELD_FLOAT, "x" ),
DEFINE_KEYFIELD( m_flOverrideY, FIELD_FLOAT, "y" ),
DEFINE_AUTO_ARRAY( m_szTimerCaption, FIELD_CHARACTER ),
DEFINE_KEYFIELD( m_iszPlayerFilterName, FIELD_STRING, "PlayerFilter" ),
DEFINE_FIELD( m_hPlayerFilter, FIELD_EHANDLE ),
DEFINE_FIELD( m_flTimerEndTime, FIELD_TIME ),
DEFINE_FIELD( m_flTimeRemaining, FIELD_FLOAT ),
DEFINE_FIELD( m_bTimerPaused, FIELD_BOOLEAN ),
DEFINE_FIELD( m_bStartedWarn, FIELD_BOOLEAN ),
DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
DEFINE_INPUTFUNC( FIELD_VOID, "Pause", InputPause ),
DEFINE_INPUTFUNC( FIELD_VOID, "Resume", InputResume ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetTime", InputSetTime ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "AddTime", InputAddTime ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "RemoveTime", InputRemoveTime ),
DEFINE_INPUTFUNC( FIELD_VOID, "Restart", InputRestart ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetMaxTime", InputSetMaxTime ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetTimerCaption", InputSetTimerCaption ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetProgressBarMaxSegments", InputSetProgressBarMaxSegments ),
DEFINE_INPUTFUNC( FIELD_INTEGER, "SetProgressBarOverride", InputSetProgressBarOverride ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetX", InputSetX ),
DEFINE_INPUTFUNC( FIELD_FLOAT, "SetY", InputSetY ),
DEFINE_INPUTFUNC( FIELD_VOID, "GetTimeRemaining", InputGetTimeRemaining ),
DEFINE_INPUTFUNC( FIELD_STRING, "SetPlayerFilter", InputSetPlayerFilter ),
DEFINE_OUTPUT( m_OnFinished, "OnFinished" ),
DEFINE_OUTPUT( m_OnPaused, "OnPaused" ),
DEFINE_OUTPUT( m_OnResumed, "OnResumed" ),
DEFINE_OUTPUT( m_OnWarned, "OnWarned" ),
DEFINE_OUTPUT( m_OnTick, "OnTick" ),
DEFINE_OUTPUT( m_OnGetTimeRemaining, "OnGetTimeRemaining" ),
DEFINE_THINKFUNC( TimerThink ),
END_DATADESC();
#endif
#ifdef CLIENT_DLL
IMPLEMENT_AUTO_LIST( IGameTimerAutoList );
#else
#define TIMER_THINK "GameTimerThink"
#endif
//-----------------------------------------------------------------------------
// Purpose: constructor
//-----------------------------------------------------------------------------
CGameTimer::CGameTimer( void )
{
m_bIsDisabled = true;
m_bTimerPaused = true;
m_flTimeRemaining = 0;
m_flTimerEndTime = 0;
m_flWarnTime = -1.0f;
m_bStartPaused = false;
m_bShowTimeRemaining = true;
m_flTimerInitialLength = 0;
m_flTimerMaxLength = 0;
m_nProgressBarMaxSegments = -1;
m_nProgressBarOverride = -1;
m_flOverrideX = m_flOverrideY = -1.0f;
#ifndef CLIENT_DLL
m_bDisableOnFinish = true;
m_bShowInHUD = true;
#endif
}
//-----------------------------------------------------------------------------
// Purpose: destructor
//-----------------------------------------------------------------------------
CGameTimer::~CGameTimer( void )
{
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::Spawn( void )
{
#ifndef CLIENT_DLL
if ( IsDisabled() ) // we need to get the data initialized before actually become disabled
{
m_bIsDisabled = false;
SetTimeRemaining( m_flTimerInitialLength );
m_bIsDisabled = true;
}
else
{
SetTimeRemaining( m_flTimerInitialLength );
if ( !m_bStartPaused )
ResumeTimer();
}
if ( m_iszPlayerFilterName != NULL_STRING )
{
m_hPlayerFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this ));
}
#endif
BaseClass::Spawn();
}
#ifndef CLIENT_DLL
bool CGameTimer::KeyValue( const char *szKeyName, const char *szValue )
{
if ( FStrEq( szKeyName, "timer_caption" ) )
{
Q_strncpy( m_szTimerCaption.GetForModify(), szValue, MAX_GAME_TIMER_CAPTION );
}
else
{
return BaseClass::KeyValue( szKeyName, szValue );
}
return true;
}
bool CGameTimer::GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen )
{
if ( FStrEq( szKeyName, "timer_caption" ) )
{
Q_snprintf( szValue, iMaxLen, "%s", m_szTimerCaption.Get() );
return true;
}
return BaseClass::GetKeyValue( szKeyName, szValue, iMaxLen );
}
#endif
//-----------------------------------------------------------------------------
// Purpose: Gets the seconds left on the timer, paused or not.
//-----------------------------------------------------------------------------
float CGameTimer::GetTimeRemaining( void )
{
float flSecondsRemaining;
if ( m_bTimerPaused )
{
flSecondsRemaining = m_flTimeRemaining;
}
else
{
flSecondsRemaining = m_flTimerEndTime - gpGlobals->curtime;
}
if ( flSecondsRemaining < 0 )
{
flSecondsRemaining = 0.0f;
}
return flSecondsRemaining;
}
//-----------------------------------------------------------------------------
// Purpose: Gets the timer's warning time
//-----------------------------------------------------------------------------
float CGameTimer::GetWarnTime( void )
{
if ( m_flWarnTime < 0 )
{
// TODO: All of the default warning stuff is on the client!!!
return GetTimerMaxLength() * sv_game_menu_default_warn_frac.GetFloat();
}
return m_flWarnTime;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
float CGameTimer::GetTimerMaxLength( void )
{
if ( m_flTimerMaxLength )
return m_flTimerMaxLength;
return m_flTimerInitialLength;
}
#ifdef CLIENT_DLL
bool g_bAnyGameTimerActive = false;
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_GameTimer::OnDataChanged( DataUpdateType_t updateType )
{
BaseClass::OnDataChanged( updateType );
if ( !m_bIsDisabled )
{
g_bAnyGameTimerActive = true;
}
else if ( !m_bOldDisabled )
{
// Go through all of the timers and mark when one is active
g_bAnyGameTimerActive = false;
for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ )
{
C_GameTimer *pTimer = static_cast<C_GameTimer *>( IGameTimerAutoList::AutoList()[i] );
if ( !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() )
{
g_bAnyGameTimerActive = true;
break;
}
}
}
m_bOldDisabled = m_bIsDisabled;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void C_GameTimer::UpdateOnRemove( void )
{
BaseClass::UpdateOnRemove();
// Update timer presence state
g_bAnyGameTimerActive = false;
for ( int i = 0; i < IGameTimerAutoList::AutoList().Count(); i++ )
{
C_GameTimer *pTimer = static_cast<C_GameTimer *>( IGameTimerAutoList::AutoList()[i] );
if ( pTimer != this && !pTimer->IsDisabled() && !pTimer->IsMarkedForDeletion() )
{
g_bAnyGameTimerActive = true;
break;
}
}
}
#else
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::TimerThink( void )
{
if ( IsDisabled() /*|| IsInCommentaryMode() || gpGlobals->eLoadType == MapLoad_Background*/ )
{
SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK );
return;
}
float flTime = GetTimeRemaining();
int nTick = (int)floorf( flTime );
if (nTick != m_OnTick.Get())
{
m_OnTick.Set( nTick, this, this );
}
if ( flTime <= 0.0f )
{
OnTimerFinished();
PauseTimer();
if (m_bDisableOnFinish)
m_bIsDisabled = true;
return;
}
else if ( flTime <= GetWarnTime() && !m_bStartedWarn)
{
OnTimerWarned();
m_bStartedWarn = true;
}
SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK );
}
//-----------------------------------------------------------------------------
// Purpose: To set the initial timer duration
//-----------------------------------------------------------------------------
void CGameTimer::SetTimeRemaining( float flTime )
{
// make sure we don't go over our max length
flTime = m_flTimerMaxLength > 0 ? MIN( flTime, m_flTimerMaxLength ) : flTime;
m_flTimeRemaining = flTime;
m_flTimerEndTime = gpGlobals->curtime + m_flTimeRemaining;
if ( m_flTimeRemaining > m_flWarnTime )
{
m_bStartedWarn = false;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::OnTimerFinished()
{
m_OnFinished.FireOutput( this, this );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::OnTimerWarned()
{
m_OnWarned.FireOutput( this, this );
}
//-----------------------------------------------------------------------------
// Purpose: Timer is paused at round end, stops the countdown
//-----------------------------------------------------------------------------
void CGameTimer::PauseTimer( CBaseEntity *pActivator )
{
if ( IsDisabled() )
return;
if ( m_bTimerPaused == false )
{
m_bTimerPaused = true;
m_flTimeRemaining = m_flTimerEndTime - gpGlobals->curtime;
m_OnPaused.FireOutput( pActivator ? pActivator : this, this );
SetContextThink( NULL, TICK_NEVER_THINK, TIMER_THINK );
}
}
//-----------------------------------------------------------------------------
// Purpose: To start or re-start the timer after a pause
//-----------------------------------------------------------------------------
void CGameTimer::ResumeTimer( CBaseEntity *pActivator )
{
if ( IsDisabled() )
return;
if ( m_bTimerPaused == true )
{
m_bTimerPaused = false;
m_flTimerEndTime = gpGlobals->curtime + m_flTimeRemaining;
m_OnResumed.FireOutput( pActivator ? pActivator : this, this );
TimerThink();
SetContextThink( &CGameTimer::TimerThink, gpGlobals->curtime + 0.05, TIMER_THINK );
}
}
//-----------------------------------------------------------------------------
// Purpose: Add seconds to the timer while it is running or paused
//-----------------------------------------------------------------------------
void CGameTimer::AddTimerSeconds( float flTimeToAdd )
{
if ( IsDisabled() )
return;
if ( m_flTimerMaxLength > 0 )
{
// will adding this many seconds push us over our max length?
if ( GetTimeRemaining() + flTimeToAdd > m_flTimerMaxLength)
{
// adjust to only add up to our max length
flTimeToAdd = m_flTimerMaxLength - GetTimeRemaining();
}
}
if ( m_bTimerPaused )
{
m_flTimeRemaining += flTimeToAdd;
}
else
{
m_flTimerEndTime += flTimeToAdd;
}
}
//-----------------------------------------------------------------------------
// Purpose: Should we transmit it to the client?
//-----------------------------------------------------------------------------
int CGameTimer::UpdateTransmitState()
{
if ( !m_bShowInHUD )
{
return SetTransmitState( FL_EDICT_DONTSEND );
}
if ( m_hPlayerFilter || GetTeamNumber() > TEAM_UNASSIGNED )
{
return SetTransmitState( FL_EDICT_FULLCHECK );
}
return SetTransmitState( FL_EDICT_ALWAYS );
}
//-----------------------------------------------------------------------------
// Purpose: Which clients should we be transmitting to?
//-----------------------------------------------------------------------------
int CGameTimer::ShouldTransmit( const CCheckTransmitInfo *pInfo )
{
int result = BaseClass::ShouldTransmit( pInfo );
if ( result != FL_EDICT_DONTSEND )
{
CBaseEntity *pClient = (CBaseEntity *)(pInfo->m_pClientEnt->GetUnknown());
if ( pClient )
{
if ( m_hPlayerFilter )
{
// Don't send to players who don't pass our filter
if ( !m_hPlayerFilter->PassesFilter( this, pClient ) )
return FL_EDICT_DONTSEND;
}
else if ( GetTeamNumber() > TEAM_UNASSIGNED )
{
// If we don't have an explicit filter, then just check if it's on the same team as us
if ( pClient->GetTeamNumber() != GetTeamNumber() )
return FL_EDICT_DONTSEND;
}
return FL_EDICT_ALWAYS;
}
}
return result;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputPause( inputdata_t &input )
{
PauseTimer( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputResume( inputdata_t &input )
{
ResumeTimer( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetTime( inputdata_t &input )
{
float flTime = input.value.Float();
SetTimeRemaining( flTime );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputAddTime( inputdata_t &input )
{
float flTime = input.value.Float();
AddTimerSeconds( flTime );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputRemoveTime( inputdata_t &input )
{
float flTime = input.value.Float();
AddTimerSeconds( -flTime );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetMaxTime( inputdata_t &input )
{
float flTime = input.value.Float();
m_flTimerMaxLength = flTime;
if (m_flTimerMaxLength > 0)
{
// make sure our current time is not above the max length
if (GetTimeRemaining() > m_flTimerMaxLength)
{
SetTimeRemaining( m_flTimerMaxLength );
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetTimerCaption( inputdata_t &input )
{
Q_strncpy( m_szTimerCaption.GetForModify(), input.value.String(), MAX_GAME_TIMER_CAPTION );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetProgressBarMaxSegments( inputdata_t &input )
{
m_nProgressBarMaxSegments = input.value.Int();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetProgressBarOverride( inputdata_t &input )
{
m_nProgressBarOverride = input.value.Int();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetX( inputdata_t &input )
{
m_flOverrideX = input.value.Float();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetY( inputdata_t &input )
{
m_flOverrideY = input.value.Float();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputRestart( inputdata_t &input )
{
SetTimeRemaining( m_flTimerInitialLength );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputGetTimeRemaining( inputdata_t &input )
{
m_OnGetTimeRemaining.Set( GetTimeRemaining(), input.pActivator, this);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputEnable( inputdata_t &input )
{
if (GetTimeRemaining() == 0.0f)
SetTimeRemaining( m_flTimerInitialLength );
m_bIsDisabled = false;
if ( !m_bStartPaused )
ResumeTimer( input.pActivator );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputDisable( inputdata_t &input )
{
PauseTimer( input.pActivator );
m_bIsDisabled = true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CGameTimer::InputSetPlayerFilter( inputdata_t &inputdata )
{
m_iszPlayerFilterName = inputdata.value.StringID();
if ( m_iszPlayerFilterName != NULL_STRING )
{
m_hPlayerFilter = dynamic_cast<CBaseFilter *>(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this ));
}
else
{
m_hPlayerFilter = NULL;
}
}
#endif

View File

@ -0,0 +1,145 @@
//========= Mapbase - https://github.com/mapbase-source/source-sdk-2013 ============//
//
// Purpose: Generic custom timer entity
//
// Author: Blixibon
//
//=============================================================================//
#include "cbase.h"
#ifdef CLIENT_DLL
#include "c_baseentity.h"
#else
#include "filters.h"
#endif
#ifdef CLIENT_DLL
DECLARE_AUTO_LIST( IGameTimerAutoList );
#define CGameTimer C_GameTimer
#endif
#define MAX_GAME_TIMER_CAPTION 32
//-----------------------------------------------------------------------------
// Purpose: Displays a custom timer
//-----------------------------------------------------------------------------
class CGameTimer : public CBaseEntity
#ifdef CLIENT_DLL
, public IGameTimerAutoList
#endif
{
public:
DECLARE_CLASS( CGameTimer, CBaseEntity );
DECLARE_NETWORKCLASS();
#ifndef CLIENT_DLL
DECLARE_DATADESC();
#endif
CGameTimer();
~CGameTimer();
virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() & ~FCAP_ACROSS_TRANSITION; }
void Spawn();
#ifndef CLIENT_DLL
bool KeyValue( const char *szKeyName, const char *szValue );
virtual bool GetKeyValue( const char *szKeyName, char *szValue, int iMaxLen );
#endif
// Based on teamplay_round_timer
virtual float GetTimeRemaining( void );
virtual float GetTimerMaxLength( void );
virtual bool StartPaused( void ) { return m_bStartPaused; }
bool ShowTimeRemaining( void ) { return m_bShowTimeRemaining; }
float GetWarnTime( void );
const char *GetTimerCaption( void ) { return m_szTimerCaption.Get(); }
int GetProgressBarMaxSegments( void ) { return m_nProgressBarMaxSegments; }
int GetProgressBarOverride( void ) { return m_nProgressBarOverride; }
bool OverridesPosition( void ) { return m_flOverrideX != -1.0f || m_flOverrideY != -1.0f; }
void GetPositionOverride( float &flX, float &flY ) { flX = m_flOverrideX; flY = m_flOverrideY; }
bool IsDisabled( void ) { return m_bIsDisabled; }
bool IsTimerPaused( void ) { return m_bTimerPaused; }
#ifndef CLIENT_DLL
virtual void SetTimeRemaining( float flTime ); // Set the initial length of the timer
virtual void AddTimerSeconds( float flTimeToAdd ); // Add time to an already running ( or paused ) timer
virtual void OnTimerFinished();
virtual void OnTimerWarned();
virtual void PauseTimer( CBaseEntity *pActivator = NULL );
virtual void ResumeTimer( CBaseEntity *pActivator = NULL );
void SetProgressBarMaxSegments( int nSegments ) { m_nProgressBarMaxSegments = nSegments; }
void SetProgressBarOverride( int nSegments ) { m_nProgressBarOverride = nSegments; }
int UpdateTransmitState();
// Inputs
void InputEnable( inputdata_t &input );
void InputDisable( inputdata_t &input );
void InputPause( inputdata_t &input );
void InputResume( inputdata_t &input );
void InputSetTime( inputdata_t &input );
void InputAddTime( inputdata_t &input );
void InputRemoveTime( inputdata_t &input );
void InputRestart( inputdata_t &input );
void InputSetMaxTime( inputdata_t &input );
void InputSetTimerCaption( inputdata_t &input );
void InputSetProgressBarMaxSegments( inputdata_t &input );
void InputSetProgressBarOverride( inputdata_t &input );
void InputSetX( inputdata_t &input );
void InputSetY( inputdata_t &input );
void InputGetTimeRemaining( inputdata_t &input );
void InputSetPlayerFilter( inputdata_t &inputdata );
#endif
private:
#ifdef CLIENT_DLL
void OnDataChanged( DataUpdateType_t updateType );
void UpdateOnRemove( void );
#else
int ShouldTransmit( const CCheckTransmitInfo *pInfo );
void TimerThink( void );
#endif
private:
CNetworkVar( bool, m_bIsDisabled );
CNetworkVar( float, m_flTimerInitialLength );
CNetworkVar( float, m_flTimerMaxLength );
CNetworkVar( float, m_flTimerEndTime );
CNetworkVar( float, m_flTimeRemaining );
CNetworkVar( float, m_flWarnTime ); // Time at which timer turns red, starts ticking loudly, etc.
CNetworkVar( int, m_nProgressBarMaxSegments ); // Overrides maximum segments in progress bar if greater than -1
CNetworkVar( int, m_nProgressBarOverride ); // Overrides progress bar value if greater than -1
CNetworkVar( float, m_flOverrideX );
CNetworkVar( float, m_flOverrideY );
CNetworkVar( bool, m_bTimerPaused );
CNetworkVar( bool, m_bStartPaused );
CNetworkVar( bool, m_bShowTimeRemaining );
CNetworkString( m_szTimerCaption, MAX_GAME_TIMER_CAPTION );
#ifdef CLIENT_DLL
bool m_bOldDisabled;
#else
bool m_bStartedWarn;
bool m_bDisableOnFinish;
bool m_bShowInHUD; // TODO: ShowInHUD input? Would require client to know it shouldn't show the timer anymore
string_t m_iszPlayerFilterName;
CHandle<CBaseFilter> m_hPlayerFilter;
// Outputs
COutputEvent m_OnFinished;
COutputEvent m_OnPaused;
COutputEvent m_OnResumed;
COutputEvent m_OnWarned;
COutputInt m_OnTick;
COutputFloat m_OnGetTimeRemaining;
#endif
};