From 8de011987bb6e991966eaa44ecd1c8878e72ee7a Mon Sep 17 00:00:00 2001 From: "ALLEN-PC\\acj30" Date: Mon, 7 Jul 2025 10:06:38 -0500 Subject: [PATCH] game_timer entity and HL2 generic timer HUD element --- sp/src/game/client/client_mapbase.vpc | 3 + .../game/client/mapbase/hud_generic_timer.cpp | 511 ++++++++++++++ sp/src/game/server/server_mapbase.vpc | 2 + sp/src/game/shared/mapbase/game_timer.cpp | 658 ++++++++++++++++++ sp/src/game/shared/mapbase/game_timer.h | 145 ++++ 5 files changed, 1319 insertions(+) create mode 100644 sp/src/game/client/mapbase/hud_generic_timer.cpp create mode 100644 sp/src/game/shared/mapbase/game_timer.cpp create mode 100644 sp/src/game/shared/mapbase/game_timer.h diff --git a/sp/src/game/client/client_mapbase.vpc b/sp/src/game/client/client_mapbase.vpc index 5aef63ce..08d38e4e 100644 --- a/sp/src/game/client/client_mapbase.vpc +++ b/sp/src/game/client/client_mapbase.vpc @@ -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" diff --git a/sp/src/game/client/mapbase/hud_generic_timer.cpp b/sp/src/game/client/mapbase/hud_generic_timer.cpp new file mode 100644 index 00000000..b3464d55 --- /dev/null +++ b/sp/src/game/client/mapbase/hud_generic_timer.cpp @@ -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 +#include +#include +#include +#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_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( 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 ); +} diff --git a/sp/src/game/server/server_mapbase.vpc b/sp/src/game/server/server_mapbase.vpc index 2d1113d2..b49ad158 100644 --- a/sp/src/game/server/server_mapbase.vpc +++ b/sp/src/game/server/server_mapbase.vpc @@ -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] diff --git a/sp/src/game/shared/mapbase/game_timer.cpp b/sp/src/game/shared/mapbase/game_timer.cpp new file mode 100644 index 00000000..67f73955 --- /dev/null +++ b/sp/src/game/shared/mapbase/game_timer.cpp @@ -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(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( 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( 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(gEntList.FindEntityByName( NULL, m_iszPlayerFilterName, this )); + } + else + { + m_hPlayerFilter = NULL; + } +} + + +#endif diff --git a/sp/src/game/shared/mapbase/game_timer.h b/sp/src/game/shared/mapbase/game_timer.h new file mode 100644 index 00000000..02afb822 --- /dev/null +++ b/sp/src/game/shared/mapbase/game_timer.h @@ -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 m_hPlayerFilter; + + // Outputs + COutputEvent m_OnFinished; + COutputEvent m_OnPaused; + COutputEvent m_OnResumed; + COutputEvent m_OnWarned; + COutputInt m_OnTick; + COutputFloat m_OnGetTimeRemaining; +#endif +};