summaryrefslogtreecommitdiff
path: root/game/server/dod
diff options
context:
space:
mode:
authorFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
committerFluorescentCIAAfricanAmerican <[email protected]>2020-04-22 12:56:21 -0400
commit3bf9df6b2785fa6d951086978a3e66f49427166a (patch)
tree2c0f1f0c63c4832882bc93814ebd2c2b1c6224e5 /game/server/dod
downloadarchived-source-engine-2018-hl2-src-master.tar.xz
archived-source-engine-2018-hl2-src-master.zip
Diffstat (limited to 'game/server/dod')
-rw-r--r--game/server/dod/dod_ammo_box.cpp141
-rw-r--r--game/server/dod/dod_ammo_box.h43
-rw-r--r--game/server/dod/dod_area_capture.cpp590
-rw-r--r--game/server/dod/dod_area_capture.h104
-rw-r--r--game/server/dod/dod_basegrenade.cpp526
-rw-r--r--game/server/dod/dod_basegrenade.h106
-rw-r--r--game/server/dod/dod_baserocket.cpp294
-rw-r--r--game/server/dod/dod_baserocket.h67
-rw-r--r--game/server/dod/dod_bombdispenser.cpp144
-rw-r--r--game/server/dod/dod_bombtarget.cpp767
-rw-r--r--game/server/dod/dod_bombtarget.h154
-rw-r--r--game/server/dod/dod_bot_temp.cpp453
-rw-r--r--game/server/dod/dod_bot_temp.h19
-rw-r--r--game/server/dod/dod_client.cpp236
-rw-r--r--game/server/dod/dod_control_point.cpp738
-rw-r--r--game/server/dod/dod_control_point.h164
-rw-r--r--game/server/dod/dod_control_point_master.cpp587
-rw-r--r--game/server/dod/dod_control_point_master.h222
-rw-r--r--game/server/dod/dod_cvars.cpp90
-rw-r--r--game/server/dod/dod_cvars.h42
-rw-r--r--game/server/dod/dod_eventlog.cpp547
-rw-r--r--game/server/dod/dod_gameinterface.cpp30
-rw-r--r--game/server/dod/dod_gameinterface.h15
-rw-r--r--game/server/dod/dod_gamestats.cpp150
-rw-r--r--game/server/dod/dod_gamestats.h175
-rw-r--r--game/server/dod/dod_handgrenade.cpp76
-rw-r--r--game/server/dod/dod_handgrenade.h48
-rw-r--r--game/server/dod/dod_hltvdirector.cpp140
-rw-r--r--game/server/dod/dod_location.cpp29
-rw-r--r--game/server/dod/dod_location.h38
-rw-r--r--game/server/dod/dod_objective_resource.cpp226
-rw-r--r--game/server/dod/dod_objective_resource.h86
-rw-r--r--game/server/dod/dod_player.cpp4936
-rw-r--r--game/server/dod/dod_player.h661
-rw-r--r--game/server/dod/dod_player_resource.cpp63
-rw-r--r--game/server/dod/dod_player_resource.h32
-rw-r--r--game/server/dod/dod_playermove.cpp80
-rw-r--r--game/server/dod/dod_riflegrenade_ger.cpp72
-rw-r--r--game/server/dod/dod_riflegrenade_ger.h49
-rw-r--r--game/server/dod/dod_riflegrenade_us.cpp72
-rw-r--r--game/server/dod/dod_riflegrenade_us.h49
-rw-r--r--game/server/dod/dod_smokegrenade.cpp121
-rw-r--r--game/server/dod/dod_smokegrenade.h42
-rw-r--r--game/server/dod/dod_smokegrenade_ger.cpp53
-rw-r--r--game/server/dod/dod_smokegrenade_ger.h38
-rw-r--r--game/server/dod/dod_smokegrenade_us.cpp53
-rw-r--r--game/server/dod/dod_smokegrenade_us.h38
-rw-r--r--game/server/dod/dod_statmgr.cpp82
-rw-r--r--game/server/dod/dod_statmgr.h78
-rw-r--r--game/server/dod/dod_stickgrenade.cpp78
-rw-r--r--game/server/dod/dod_stickgrenade.h50
-rw-r--r--game/server/dod/dod_team.cpp168
-rw-r--r--game/server/dod/dod_team.h60
-rw-r--r--game/server/dod/grenadetrail.cpp109
-rw-r--r--game/server/dod/grenadetrail.h31
-rw-r--r--game/server/dod/holiday_gift.cpp123
-rw-r--r--game/server/dod/holiday_gift.h36
-rw-r--r--game/server/dod/rocket_bazooka.cpp30
-rw-r--r--game/server/dod/rocket_bazooka.h36
-rw-r--r--game/server/dod/rocket_pschreck.cpp30
-rw-r--r--game/server/dod/rocket_pschreck.h36
-rw-r--r--game/server/dod/te_firebullets.cpp114
-rw-r--r--game/server/dod/te_firebullets.h25
-rw-r--r--game/server/dod/unisignals.h76
64 files changed, 14568 insertions, 0 deletions
diff --git a/game/server/dod/dod_ammo_box.cpp b/game/server/dod/dod_ammo_box.cpp
new file mode 100644
index 0000000..19cc22d
--- /dev/null
+++ b/game/server/dod/dod_ammo_box.cpp
@@ -0,0 +1,141 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_ammo_box.h"
+#include "dod_player.h"
+#include "dod_gamerules.h"
+
+BEGIN_DATADESC( CAmmoBox )
+ DEFINE_THINKFUNC( FlyThink ),
+ DEFINE_ENTITYFUNC( BoxTouch ),
+END_DATADESC();
+
+LINK_ENTITY_TO_CLASS( dod_ammo_box, CAmmoBox );
+
+void CAmmoBox::Spawn( void )
+{
+ Precache( );
+ SetModel( "models/ammo/ammo_us.mdl" );
+ BaseClass::Spawn();
+
+ SetNextThink( gpGlobals->curtime + 0.75f );
+ SetThink( &CAmmoBox::FlyThink );
+
+ SetTouch( &CAmmoBox::BoxTouch );
+
+ m_hOldOwner = GetOwnerEntity();
+}
+
+void CAmmoBox::Precache( void )
+{
+ PrecacheModel( "models/ammo/ammo_axis.mdl" );
+ PrecacheModel( "models/ammo/ammo_us.mdl" );
+}
+
+CAmmoBox *CAmmoBox::Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, int team )
+{
+ CAmmoBox *p = static_cast<CAmmoBox *> ( CBaseAnimating::Create( "dod_ammo_box", vecOrigin, vecAngles, pOwner ) );
+
+ p->SetAmmoTeam( team );
+
+ return p;
+}
+
+void CAmmoBox::SetAmmoTeam( int team )
+{
+ switch( team )
+ {
+ case TEAM_ALLIES:
+ {
+ SetModel( "models/ammo/ammo_us.mdl" );
+ }
+ break;
+ case TEAM_AXIS:
+ {
+ SetModel( "models/ammo/ammo_axis.mdl" );
+ }
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+
+ m_iAmmoTeam = team;
+}
+
+void CAmmoBox::FlyThink( void )
+{
+ SetOwnerEntity( NULL ); //so our owner can pick it back up
+}
+
+void CAmmoBox::BoxTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+
+ if( !pOther->IsPlayer() )
+ return;
+
+ if( !pOther->IsAlive() )
+ return;
+
+ //Don't let the person who threw this ammo pick it up until it hits the ground.
+ //This way we can throw ammo to people, but not touch it as soon as we throw it ourselves
+ if( GetOwnerEntity() == pOther )
+ return;
+
+ CDODPlayer *pPlayer = ToDODPlayer( pOther );
+
+ Assert( pPlayer );
+
+ if( pPlayer->GetTeamNumber() != m_iAmmoTeam )
+ return;
+
+ if( pPlayer == m_hOldOwner )
+ {
+ //don't give ammo, just give him his drop again
+ pPlayer->ReturnGenericAmmo();
+ UTIL_Remove(this);
+ }
+ else
+ {
+ //See if they can use some ammo, if so, remove the box
+ if( pPlayer->GiveGenericAmmo() )
+ UTIL_Remove(this);
+ }
+}
+
+bool CAmmoBox::MyTouch( CBasePlayer *pBasePlayer )
+{
+ if ( !pBasePlayer )
+ {
+ Assert( false );
+ return false;
+ }
+
+ if( !pBasePlayer->IsAlive() )
+ return false;
+
+ if( pBasePlayer->GetTeamNumber() != m_iAmmoTeam )
+ return false;
+
+ CDODPlayer *pPlayer = ToDODPlayer( pBasePlayer );
+
+ if( pPlayer == m_hOldOwner )
+ {
+ //don't give ammo, just give him his drop again
+ pPlayer->ReturnGenericAmmo();
+ UTIL_Remove(this);
+ }
+ else
+ {
+ //See if they can use some ammo, if so, remove the box
+ if( pPlayer->GiveGenericAmmo() )
+ UTIL_Remove(this);
+ }
+
+ return true;
+} \ No newline at end of file
diff --git a/game/server/dod/dod_ammo_box.h b/game/server/dod/dod_ammo_box.h
new file mode 100644
index 0000000..d126b58
--- /dev/null
+++ b/game/server/dod/dod_ammo_box.h
@@ -0,0 +1,43 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef AMMO_BOX_H
+#define AMMO_BOX_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "items.h"
+
+class CAmmoBox : public CItem
+{
+public:
+ DECLARE_CLASS( CAmmoBox, CItem );
+
+ CAmmoBox() {}
+
+ virtual void Spawn();
+ virtual void Precache();
+
+ void EXPORT FlyThink( void );
+ void EXPORT BoxTouch( CBaseEntity *pOther );
+ bool MyTouch( CBasePlayer *pBasePlayer );
+
+ static CAmmoBox *Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner, int team );
+
+ void SetAmmoTeam( int team );
+
+private:
+ EHANDLE m_hOldOwner;
+ int m_iAmmoTeam;
+
+private:
+ CAmmoBox( const CAmmoBox & );
+
+ DECLARE_DATADESC();
+};
+
+#endif //GENERIC_AMMO_H \ No newline at end of file
diff --git a/game/server/dod/dod_area_capture.cpp b/game/server/dod/dod_area_capture.cpp
new file mode 100644
index 0000000..cd9e4e9
--- /dev/null
+++ b/game/server/dod/dod_area_capture.cpp
@@ -0,0 +1,590 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_area_capture.h"
+#include "dod_player.h"
+#include "dod_gamerules.h"
+#include "dod_control_point.h"
+#include "dod_team.h"
+#include "dod_objective_resource.h"
+
+
+//-------------------------------------------------------------------
+// CAreaCapture
+// An area entity that players must remain in in order to active another entity
+// Triggers are fired on start of capture, on end of capture and on broken capture
+// Can either be capped by both teams at once, or just by one
+// Time to capture and number of people required to capture are both passed by the mapper
+
+
+
+BEGIN_DATADESC(CAreaCapture)
+
+ // Touch functions
+ DEFINE_FUNCTION( AreaTouch ),
+
+ // Think functions
+ DEFINE_THINKFUNC( Think ),
+
+ // Keyfields
+ DEFINE_KEYFIELD( m_iszCapPointName, FIELD_STRING, "area_cap_point" ),
+
+ DEFINE_KEYFIELD( m_nAlliesNumCap, FIELD_INTEGER, "area_allies_numcap" ),
+ DEFINE_KEYFIELD( m_nAxisNumCap, FIELD_INTEGER, "area_axis_numcap" ),
+ DEFINE_KEYFIELD( m_flCapTime, FIELD_FLOAT, "area_time_to_cap" ),
+
+ DEFINE_KEYFIELD( m_bAlliesCanCap, FIELD_BOOLEAN, "area_allies_cancap" ),
+ DEFINE_KEYFIELD( m_bAxisCanCap, FIELD_BOOLEAN, "area_axis_cancap" ),
+
+ DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "RoundInit", InputRoundInit ),
+
+ // Outputs
+ DEFINE_OUTPUT( m_AlliesStartOutput, "OnAlliesStartCap" ),
+ DEFINE_OUTPUT( m_AlliesBreakOutput, "OnAlliesBreakCap" ),
+ DEFINE_OUTPUT( m_AlliesCapOutput, "OnAlliesEndCap" ),
+
+ DEFINE_OUTPUT( m_AxisStartOutput, "OnAxisStartCap" ),
+ DEFINE_OUTPUT( m_AxisBreakOutput, "OnAxisBreakCap" ),
+ DEFINE_OUTPUT( m_AxisCapOutput, "OnAxisEndCap" ),
+
+ DEFINE_OUTPUT( m_StartOutput, "OnStartCap" ),
+ DEFINE_OUTPUT( m_BreakOutput, "OnBreakCap" ),
+ DEFINE_OUTPUT( m_CapOutput, "OnEndCap" ),
+
+// DEFINE_OUTPUT( m_OnStartCap, "OnStartCap" );
+// DEFINE_OUTPUT( m_OnBreakCap,
+
+// DEFINE_OUTPUT( m_OnEnterNoObj, "OnEnterNoObj" );
+
+END_DATADESC();
+
+LINK_ENTITY_TO_CLASS( dod_capture_area, CAreaCapture );
+
+void CAreaCapture::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ InitTrigger();
+
+ Precache();
+
+ m_iAreaIndex = -1;
+
+ SetTouch ( &CAreaCapture::AreaTouch );
+
+ m_bCapturing = false;
+ m_nCapturingTeam = TEAM_UNASSIGNED;
+ m_nOwningTeam = TEAM_UNASSIGNED;
+ m_fTimeRemaining = 0.0f;
+
+ SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );
+
+ if( m_nAlliesNumCap < 1 )
+ m_nAlliesNumCap = 1;
+
+ if( m_nAxisNumCap < 1 )
+ m_nAxisNumCap = 1;
+
+ m_bDisabled = false;
+
+ m_iCapAttemptNumber = 0;
+}
+
+void CAreaCapture::Precache( void )
+{
+}
+
+bool CAreaCapture::KeyValue( const char *szKeyName, const char *szValue )
+{
+ return BaseClass::KeyValue( szKeyName, szValue );
+}
+
+//sends to all players at the start of the round
+//needed?
+void CAreaCapture::area_SetIndex( int index )
+{
+ m_iAreaIndex = index;
+}
+
+bool CAreaCapture::IsActive( void )
+{
+ return !m_bDisabled;
+}
+
+void CAreaCapture::AreaTouch( CBaseEntity *pOther )
+{
+ //if they are touching, set their SIGNAL flag on, and their m_iCapAreaNum to ours
+ //then in think do all the scoring
+
+ if( !IsActive() )
+ return;
+
+ //Don't cap areas unless the round is running
+ if( DODGameRules()->State_Get() != STATE_RND_RUNNING || DODGameRules()->IsInWarmup() )
+ return;
+
+ Assert( m_iAreaIndex != -1 );
+
+ if( m_pPoint )
+ {
+ m_nOwningTeam = m_pPoint->GetOwner();
+ }
+
+ //dont touch for non-alive or non-players
+ if( !pOther->IsPlayer() )
+ return;
+
+ if( !pOther->IsAlive() )
+ return;
+
+ CDODPlayer *pPlayer = ToDODPlayer(pOther);
+
+ ASSERT( pPlayer );
+
+ if ( pPlayer->GetTeamNumber() != m_nOwningTeam )
+ {
+ bool bAbleToCap = ( pPlayer->GetTeamNumber() == TEAM_ALLIES && m_bAlliesCanCap ) ||
+ ( pPlayer->GetTeamNumber() == TEAM_AXIS && m_bAxisCanCap );
+
+ if ( bAbleToCap )
+ pPlayer->HintMessage( HINT_IN_AREA_CAP );
+ }
+
+ pPlayer->m_signals.Signal( SIGNAL_CAPTUREAREA );
+
+ //add them to this area
+ pPlayer->SetCapAreaIndex( m_iAreaIndex );
+
+ if ( m_pPoint )
+ {
+ pPlayer->SetCPIndex( m_pPoint->GetPointIndex() );
+ }
+}
+
+/* three ways to be capturing a cap area
+ * 1) have the required number of people in the area
+ * 2) have less than the required number on your team, new required num is everyone
+ * 3) have less than the required number alive, new required is numAlive, but time is lengthened
+ */
+
+ConVar dod_simulatemultiplecappers( "dod_simulatemultiplecappers", "1", FCVAR_CHEAT );
+
+void CAreaCapture::Think( void )
+{
+ SetNextThink( gpGlobals->curtime + AREA_THINK_TIME );
+
+ if( DODGameRules()->State_Get() != STATE_RND_RUNNING )
+ {
+ // If we were being capped, cancel it
+ if( m_nNumAllies > 0 || m_nNumAxis > 0 )
+ {
+ m_nNumAllies = 0;
+ m_nNumAxis = 0;
+ SendNumPlayers();
+
+ if( m_pPoint )
+ {
+ g_pObjectiveResource->SetCappingTeam( m_pPoint->GetPointIndex(), TEAM_UNASSIGNED );
+ }
+ }
+ return;
+ }
+
+ // go through our list of players
+
+ int iNumAllies = 0;
+ int iNumAxis = 0;
+
+ CDODPlayer *pFirstAlliedTouching = NULL;
+ CDODPlayer *pFirstAxisTouching = NULL;
+
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CBaseEntity *ent = UTIL_PlayerByIndex( i );
+ if ( ent )
+ {
+ CDODPlayer *pPlayer = ToDODPlayer(ent);
+
+ //First check if the player is in fact in this area
+ if ( ( pPlayer->m_signals.GetState() & SIGNAL_CAPTUREAREA ) &&
+ pPlayer->GetCapAreaIndex() == m_iAreaIndex &&
+ pPlayer->IsAlive() ) // alive check is kinda unnecessary, but there is some
+ // case where non-present people are messing up this count
+ {
+ if ( pPlayer->GetTeamNumber() == TEAM_ALLIES )
+ {
+ if ( iNumAllies == 0 )
+ pFirstAlliedTouching = pPlayer;
+
+ iNumAllies++;
+ }
+ else if ( pPlayer->GetTeamNumber() == TEAM_AXIS )
+ {
+ if ( iNumAxis == 0 )
+ pFirstAxisTouching = pPlayer;
+
+ iNumAxis++;
+ }
+ }
+ }
+ }
+
+ iNumAllies *= dod_simulatemultiplecappers.GetInt();
+ iNumAxis *= dod_simulatemultiplecappers.GetInt();
+
+ if( iNumAllies != m_nNumAllies || iNumAxis != m_nNumAxis )
+ {
+ m_nNumAllies = iNumAllies;
+ m_nNumAxis = iNumAxis;
+ SendNumPlayers();
+ }
+
+ // when a player blocks, tell them the cap index and attempt number
+ // only give successive blocks to them if the attempt number is different
+
+ if( m_bCapturing )
+ {
+ //its a regular cap
+ //Subtract some time from the cap
+ m_fTimeRemaining -= AREA_THINK_TIME;
+
+ //if both teams are in the area
+ if( iNumAllies > 0 && iNumAxis > 0 )
+ {
+ // See if anyone gets credit for the block
+ float flPercentToGo = m_fTimeRemaining / m_flCapTime;
+ if ( flPercentToGo <= 0.5 && m_pPoint )
+ {
+ // find the first player that is not on the capturing team
+ // they have just broken a cap and should be rewarded
+ // tell the player the capture attempt number, for checking later
+ CDODPlayer *pBlockingPlayer = ( m_nCapturingTeam == TEAM_ALLIES ) ? pFirstAxisTouching : pFirstAlliedTouching;
+
+ if ( pBlockingPlayer )
+ {
+ if ( pBlockingPlayer->GetCapAreaIndex() == m_iAreaIndex &&
+ pBlockingPlayer->GetLastBlockCapAttempt() == m_iCapAttemptNumber )
+ {
+ // this is a repeat block on the same cap, ignore it
+ NULL;
+ }
+ else
+ {
+ m_pPoint->CaptureBlocked( pBlockingPlayer );
+ pBlockingPlayer->StoreCaptureBlock( m_iAreaIndex, m_iCapAttemptNumber );
+ }
+ }
+ }
+
+ BreakCapture( false );
+ return;
+ }
+
+ //if no-one is in the area
+ if( iNumAllies == 0 && iNumAxis == 0 )
+ {
+ BreakCapture( true );
+ return;
+ }
+
+ if( m_nCapturingTeam == TEAM_ALLIES )
+ {
+ if( iNumAllies < m_nAlliesNumCap )
+ {
+ BreakCapture( true );
+ }
+ }
+ else if( m_nCapturingTeam == TEAM_AXIS )
+ {
+ if( iNumAxis < m_nAxisNumCap )
+ {
+ BreakCapture( true );
+ }
+ }
+
+ //if the cap is done
+ if( m_fTimeRemaining <= 0 )
+ {
+ EndCapture( m_nCapturingTeam );
+ return; //we're done
+ }
+ }
+ else //not capturing yet
+ {
+ bool bStarted = false;
+
+ if( iNumAllies > 0 && iNumAxis <= 0 && m_bAlliesCanCap && m_nOwningTeam != TEAM_ALLIES )
+ {
+ if( iNumAllies >= m_nAlliesNumCap )
+ {
+ m_iCappingRequired = m_nAlliesNumCap;
+ m_iCappingPlayers = iNumAllies;
+ StartCapture( TEAM_ALLIES, CAPTURE_NORMAL );
+ bStarted = true;
+ }
+ }
+ else if( iNumAxis > 0 && iNumAllies <= 0 && m_bAxisCanCap && m_nOwningTeam != TEAM_AXIS )
+ {
+ if( iNumAxis >= m_nAxisNumCap )
+ {
+ m_iCappingRequired = m_nAxisNumCap;
+ m_iCappingPlayers = iNumAxis;
+ StartCapture( TEAM_AXIS, CAPTURE_NORMAL );
+ bStarted = true;
+ }
+ }
+ }
+}
+
+void CAreaCapture::SetOwner( int team )
+{
+ //break any current capturing
+ BreakCapture( false );
+
+ //set the owner to the passed value
+ m_nOwningTeam = team;
+ g_pObjectiveResource->SetOwningTeam( m_pPoint->GetPointIndex(), m_nOwningTeam );
+}
+
+void CAreaCapture::SendNumPlayers( CBasePlayer *pPlayer )
+{
+ if( !m_pPoint )
+ return;
+
+ int index = m_pPoint->GetPointIndex();
+
+ g_pObjectiveResource->SetNumPlayers( index, TEAM_ALLIES, m_nNumAllies );
+ g_pObjectiveResource->SetNumPlayers( index, TEAM_AXIS, m_nNumAxis );
+}
+
+void CAreaCapture::StartCapture( int team, int capmode )
+{
+ int iNumCappers = 0;
+
+ //trigger start
+ if( team == TEAM_ALLIES )
+ {
+ m_AlliesStartOutput.FireOutput(this,this);
+ iNumCappers = m_nAlliesNumCap;
+ }
+ else if( team == TEAM_AXIS )
+ {
+ m_AxisStartOutput.FireOutput(this,this);
+ iNumCappers = m_nAxisNumCap;
+ }
+
+ m_StartOutput.FireOutput(this,this);
+
+ m_nCapturingTeam = team;
+ m_fTimeRemaining = m_flCapTime;
+ m_bCapturing = true;
+ m_iCapMode = capmode;
+
+ if( m_pPoint )
+ {
+ //send a message that we're starting to cap this area
+ g_pObjectiveResource->SetCappingTeam( m_pPoint->GetPointIndex(), m_nCapturingTeam );
+ }
+}
+
+#define MAX_AREA_CAPPERS 9
+
+void CAreaCapture::EndCapture( int team )
+{
+ m_iCapAttemptNumber++;
+
+ //do the triggering
+ if( team == TEAM_ALLIES )
+ {
+ m_AlliesCapOutput.FireOutput(this,this);
+ }
+ else if( team == TEAM_AXIS )
+ {
+ m_AxisCapOutput.FireOutput(this,this);
+ }
+
+ m_CapOutput.FireOutput(this,this);
+
+ m_iCappingRequired = 0;
+ m_iCappingPlayers = 0;
+
+ int numcappers = 0;
+ int cappingplayers[MAX_AREA_CAPPERS];
+
+ CDODPlayer *pCappingPlayer = NULL;
+
+ CDODTeam *pTeam = GetGlobalDODTeam(team);
+ if ( pTeam )
+ {
+ int iCount = pTeam->GetNumPlayers();
+
+ for ( int i=0;i<iCount;i++ )
+ {
+ CDODPlayer *pPlayer = ToDODPlayer( pTeam->GetPlayer(i) );
+ if ( pPlayer )
+ {
+ if( ( pPlayer->m_signals.GetState() & SIGNAL_CAPTUREAREA ) &&
+ pPlayer->GetCapAreaIndex() == m_iAreaIndex &&
+ pPlayer->IsAlive() )
+ {
+ if( pCappingPlayer == NULL )
+ pCappingPlayer = pPlayer;
+
+ if ( numcappers < MAX_AREA_CAPPERS-1 )
+ {
+ cappingplayers[numcappers] = pPlayer->entindex();
+ numcappers++;
+ }
+ }
+ }
+ }
+ }
+
+ if ( numcappers < MAX_AREA_CAPPERS )
+ {
+ cappingplayers[numcappers] = 0; //null terminate :)
+ }
+
+ m_nOwningTeam = team;
+ m_bCapturing = false;
+ m_fTimeRemaining = 0.0f;
+
+ //there may have been more than one capper, but only report this one.
+ //he hasnt gotten points yet, and his name will go in the cap string if its needed
+ //first capper gets name sent and points given by flag.
+ //other cappers get points manually above, no name in message
+
+ //send the player in the cap string
+ if( m_pPoint )
+ {
+ DODGameRules()->CapEvent( CAP_EVENT_FLAG, m_nOwningTeam );
+
+ g_pObjectiveResource->SetOwningTeam( m_pPoint->GetPointIndex(), m_nOwningTeam );
+ m_pPoint->SetOwner( m_nOwningTeam, true, numcappers, cappingplayers );
+ }
+}
+
+void CAreaCapture::BreakCapture( bool bNotEnoughPlayers )
+{
+ if( m_bCapturing )
+ {
+ m_iCappingRequired = 0;
+ m_iCappingPlayers = 0;
+
+ if( m_nCapturingTeam == TEAM_ALLIES )
+ m_AlliesBreakOutput.FireOutput(this,this);
+
+ else if( m_nCapturingTeam == TEAM_AXIS )
+ m_AxisBreakOutput.FireOutput(this,this);
+
+ m_BreakOutput.FireOutput(this,this);
+
+ m_bCapturing = false;
+
+ if( m_pPoint )
+ {
+ g_pObjectiveResource->SetCappingTeam( m_pPoint->GetPointIndex(), TEAM_UNASSIGNED );
+ }
+
+ if ( bNotEnoughPlayers )
+ {
+ m_iCapAttemptNumber++;
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAreaCapture::InputDisable( inputdata_t &inputdata )
+{
+ m_bDisabled = true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CAreaCapture::InputEnable( inputdata_t &inputdata )
+{
+ m_bDisabled = false;
+}
+
+void CAreaCapture::InputRoundInit( inputdata_t &inputdata )
+{
+ // find the flag we're linked to
+ if( !m_pPoint )
+ {
+ m_pPoint = dynamic_cast<CControlPoint*>( gEntList.FindEntityByName(NULL, STRING(m_iszCapPointName) ) );
+
+ if ( m_pPoint )
+ {
+ m_pPoint->SetNumCappersRequired( m_nAlliesNumCap, m_nAxisNumCap );
+ g_pObjectiveResource->SetCPRequiredCappers( m_pPoint->GetPointIndex(), m_nAlliesNumCap, m_nAxisNumCap );
+ g_pObjectiveResource->SetCPCapTime( m_pPoint->GetPointIndex(), m_flCapTime, m_flCapTime );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check if this player's death causes a block
+// return FALSE if the player is not in this area
+// return TRUE otherwise ( eg player is in area, but his death does not cause break )
+//-----------------------------------------------------------------------------
+bool CAreaCapture::CheckIfDeathCausesBlock( CDODPlayer *pVictim, CDODPlayer *pKiller )
+{
+ // This shouldn't happen
+ if ( !pVictim || !pKiller )
+ {
+ Assert( !"Why are null players getting here?" );
+ return false;
+ }
+
+ // make sure this player is in this area
+ if ( pVictim->GetCapAreaIndex() != m_iAreaIndex )
+ return false;
+
+ // Teamkills shouldn't give a block reward
+ if ( pVictim->GetTeamNumber() == pKiller->GetTeamNumber() )
+ return true;
+
+ // return if the area is not being capped
+ if ( !m_bCapturing )
+ return true;
+
+ int iTeam = pVictim->GetTeamNumber();
+
+ // return if this player's team is not capping the area
+ if ( iTeam != m_nCapturingTeam )
+ return true;
+
+ bool bBlocked = false;
+
+ if ( m_nCapturingTeam == TEAM_ALLIES )
+ {
+ if ( m_nNumAllies-1 < m_nAlliesNumCap )
+ bBlocked = true;
+ }
+ else if ( m_nCapturingTeam == TEAM_AXIS )
+ {
+ if ( m_nNumAxis-1 < m_nAxisNumCap )
+ bBlocked = true;
+ }
+
+ // break early incase we kill multiple people in the same frame
+ if ( bBlocked )
+ {
+ m_pPoint->CaptureBlocked( pKiller );
+ BreakCapture( false );
+ }
+
+ return true;
+}
diff --git a/game/server/dod/dod_area_capture.h b/game/server/dod/dod_area_capture.h
new file mode 100644
index 0000000..6fb57dd
--- /dev/null
+++ b/game/server/dod/dod_area_capture.h
@@ -0,0 +1,104 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_AREA_CAPTURE_H
+#define DOD_AREA_CAPTURE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "triggers.h"
+#include "dod_control_point.h"
+
+#define AREA_ATTEND_TIME 0.7f
+
+#define AREA_THINK_TIME 0.1f
+
+#define CAPTURE_NORMAL 0
+#define CAPTURE_CATCHUP_ALIVEPLAYERS 1
+
+#define MAX_CLIENT_AREAS 128
+
+class CAreaCapture : public CBaseTrigger
+{
+public:
+ DECLARE_CLASS( CAreaCapture, CBaseTrigger );
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+ virtual bool KeyValue( const char *szKeyName, const char *szValue );
+
+ void area_SetIndex( int index );
+
+ bool IsActive( void );
+
+ bool CheckIfDeathCausesBlock( CDODPlayer *pVictim, CDODPlayer *pKiller );
+
+private:
+ void EXPORT AreaTouch( CBaseEntity *pOther );
+ void Think( void );
+
+ void StartCapture( int team, int capmode );
+ void EndCapture( int team );
+ void BreakCapture( bool bNotEnoughPlayers );
+ void SwitchCapture( int team );
+ void SendNumPlayers( CBasePlayer *pPlayer = NULL );
+
+ void SetOwner( int team ); //sets the owner of this point - useful for resetting all to -1
+
+ void InputEnable( inputdata_t &inputdata );
+ void InputDisable( inputdata_t &inputdata );
+ void InputRoundInit( inputdata_t &inputdata );
+
+private:
+ int m_iCapMode; //which capture mode we're in
+ int m_bCapturing;
+ int m_nCapturingTeam; //the team that is capturing this point
+ int m_nOwningTeam; //the team that has captured this point
+ float m_flCapTime; //the total time it takes to capture the area, in seconds
+ float m_fTimeRemaining; //the time left in the capture
+
+ int m_nAlliesNumCap; //number of allies required to cap
+ int m_nAxisNumCap; //number of axis required to cap
+
+ int m_nNumAllies;
+ int m_nNumAxis;
+
+ bool m_bAlliesCanCap;
+ bool m_bAxisCanCap;
+
+ //used for catchup capping
+ int m_iCappingRequired; //how many players are currently capping
+ int m_iCappingPlayers; //how many are required?
+
+ bool m_bActive;
+
+ COutputEvent m_AlliesStartOutput;
+ COutputEvent m_AlliesBreakOutput;
+ COutputEvent m_AlliesCapOutput;
+
+ COutputEvent m_AxisStartOutput;
+ COutputEvent m_AxisBreakOutput;
+ COutputEvent m_AxisCapOutput;
+
+ COutputEvent m_StartOutput;
+ COutputEvent m_BreakOutput;
+ COutputEvent m_CapOutput;
+
+ int m_iAreaIndex; //index of this area among all other areas
+
+ CControlPoint *m_pPoint; //the capture point that we are linked to!
+
+ bool m_bRequiresObject;
+
+ string_t m_iszCapPointName; //name of the cap point that we're linked to
+
+ int m_iCapAttemptNumber; // number used to keep track of discrete cap attempts, for block tracking
+
+ DECLARE_DATADESC();
+};
+
+#endif //DOD_AREA_CAPTURE_H \ No newline at end of file
diff --git a/game/server/dod/dod_basegrenade.cpp b/game/server/dod/dod_basegrenade.cpp
new file mode 100644
index 0000000..9420a08
--- /dev/null
+++ b/game/server/dod/dod_basegrenade.cpp
@@ -0,0 +1,526 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_basegrenade.h"
+#include "dod_player.h"
+#include "dod_gamerules.h"
+#include "func_break.h"
+#include "physics_saverestore.h"
+#include "grenadetrail.h"
+#include "fx_dod_shared.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+float GetCurrentGravity( void );
+
+ConVar dod_grenadegravity( "dod_grenadegravity", "-420", FCVAR_CHEAT, "gravity applied to grenades", true, -2000, true, -300 );
+extern ConVar dod_bonusround;
+
+IMotionEvent::simresult_e CGrenadeController::Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular )
+{
+ linear.x = linear.y = 0;
+ linear.z = dod_grenadegravity.GetFloat();
+
+ angular.x = angular.y = angular.z = 0;
+
+ return SIM_GLOBAL_ACCELERATION;
+}
+
+BEGIN_SIMPLE_DATADESC( CGrenadeController )
+END_DATADESC()
+
+BEGIN_DATADESC( CDODBaseGrenade )
+
+ DEFINE_THINKFUNC( DetonateThink ),
+
+ DEFINE_EMBEDDED( m_GrenadeController ),
+ DEFINE_PHYSPTR( m_pMotionController ), // probably not necessary
+
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST( CDODBaseGrenade, DT_DODBaseGrenade )
+ SendPropVector( SENDINFO( m_vInitialVelocity ),
+ 20, // nbits
+ 0, // flags
+ -3000, // low value
+ 3000 // high value
+ )
+END_SEND_TABLE()
+
+CDODBaseGrenade::CDODBaseGrenade()
+{
+}
+
+CDODBaseGrenade::~CDODBaseGrenade( void )
+{
+ if ( m_pMotionController != NULL )
+ {
+ physenv->DestroyMotionController( m_pMotionController );
+ m_pMotionController = NULL;
+ }
+}
+
+void CDODBaseGrenade::Spawn( void )
+{
+ m_bUseVPhysics = true;
+
+ BaseClass::Spawn();
+
+ SetSolid( SOLID_BBOX ); // So it will collide with physics props!
+
+ UTIL_SetSize( this, Vector(-4,-4,-4), Vector(4,4,4) );
+
+ if( m_bUseVPhysics )
+ {
+ SetCollisionGroup( COLLISION_GROUP_WEAPON );
+ IPhysicsObject *pPhysicsObject = VPhysicsInitNormal( SOLID_BBOX, 0, false );
+
+ if ( pPhysicsObject )
+ {
+ m_pMotionController = physenv->CreateMotionController( &m_GrenadeController );
+ m_pMotionController->AttachObject( pPhysicsObject, true );
+
+ pPhysicsObject->EnableGravity( false );
+ }
+
+ m_takedamage = DAMAGE_EVENTS_ONLY;
+ }
+ else
+ {
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+ m_takedamage = DAMAGE_NO;
+ }
+
+ AddSolidFlags( FSOLID_NOT_STANDABLE );
+
+ m_iHealth = 1;
+
+ SetFriction( GetGrenadeFriction() );
+ SetElasticity( GetGrenadeElasticity() );
+
+ // Remember our owner's team
+ ChangeTeam( GetThrower()->GetTeamNumber() );
+
+ m_flDamage = 150;
+ m_DmgRadius = m_flDamage * 2.5f;
+
+ // Don't collide with players on the owner's team for the first bit of our life
+ m_flCollideWithTeammatesTime = gpGlobals->curtime + 0.25;
+ m_bCollideWithTeammates = false;
+
+ SetThink( &CDODBaseGrenade::DetonateThink );
+ SetNextThink( gpGlobals->curtime + 0.1 );
+}
+
+void CDODBaseGrenade::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheParticleSystem( "grenadetrail" );
+ PrecacheParticleSystem( "riflegrenadetrail" );
+ PrecacheParticleSystem( "explosioncore_midair" );
+ PrecacheParticleSystem( "explosioncore_floor" );
+}
+
+void CDODBaseGrenade::DetonateThink( void )
+{
+ if (!IsInWorld())
+ {
+ Remove( );
+ return;
+ }
+
+ if ( gpGlobals->curtime > m_flCollideWithTeammatesTime && m_bCollideWithTeammates == false )
+ {
+ m_bCollideWithTeammates = true;
+ }
+
+ Vector foo;
+ AngularImpulse a;
+
+ VPhysicsGetObject()->GetVelocity( &foo, &a );
+
+ if( gpGlobals->curtime > m_flDetonateTime )
+ {
+ Detonate();
+ return;
+ }
+
+ if (GetWaterLevel() != 0)
+ {
+ SetAbsVelocity( GetAbsVelocity() * 0.5 );
+ }
+
+ SetNextThink( gpGlobals->curtime + 0.2 );
+}
+
+//Sets the time at which the grenade will explode
+void CDODBaseGrenade::SetDetonateTimerLength( float timer )
+{
+ m_flDetonateTime = gpGlobals->curtime + timer;
+}
+
+void CDODBaseGrenade::ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity )
+{
+ //Assume all surfaces have the same elasticity
+ float flSurfaceElasticity = 1.0;
+
+ //Don't bounce off of players with perfect elasticity
+ if( trace.m_pEnt && trace.m_pEnt->IsPlayer() )
+ {
+ flSurfaceElasticity = 0.3;
+ }
+
+ float flTotalElasticity = GetElasticity() * flSurfaceElasticity;
+ flTotalElasticity = clamp( flTotalElasticity, 0.0f, 0.9f );
+
+ // NOTE: A backoff of 2.0f is a reflection
+ Vector vecAbsVelocity;
+ PhysicsClipVelocity( GetAbsVelocity(), trace.plane.normal, vecAbsVelocity, 2.0f );
+ vecAbsVelocity *= flTotalElasticity;
+
+ // Get the total velocity (player + conveyors, etc.)
+ VectorAdd( vecAbsVelocity, GetBaseVelocity(), vecVelocity );
+ float flSpeedSqr = DotProduct( vecVelocity, vecVelocity );
+
+ // Stop if on ground.
+ if ( trace.plane.normal.z > 0.7f ) // Floor
+ {
+ // Verify that we have an entity.
+ CBaseEntity *pEntity = trace.m_pEnt;
+ Assert( pEntity );
+
+ // Are we on the ground?
+ if ( vecVelocity.z < ( GetCurrentGravity() * gpGlobals->frametime ) )
+ {
+ if ( pEntity->IsStandable() )
+ {
+ SetGroundEntity( pEntity );
+ }
+
+ vecAbsVelocity.z = 0.0f;
+ }
+ SetAbsVelocity( vecAbsVelocity );
+
+ if ( flSpeedSqr < ( 30 * 30 ) )
+ {
+ if ( pEntity->IsStandable() )
+ {
+ SetGroundEntity( pEntity );
+ }
+
+ // Reset velocities.
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+ }
+ else
+ {
+ Vector vecDelta = GetBaseVelocity() - vecAbsVelocity;
+ Vector vecBaseDir = GetBaseVelocity();
+ VectorNormalize( vecBaseDir );
+ float flScale = vecDelta.Dot( vecBaseDir );
+
+ VectorScale( vecAbsVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, vecVelocity );
+ VectorMA( vecVelocity, ( 1.0f - trace.fraction ) * gpGlobals->frametime, GetBaseVelocity() * flScale, vecVelocity );
+ PhysicsPushEntity( vecVelocity, &trace );
+ }
+ }
+ else
+ {
+ // If we get *too* slow, we'll stick without ever coming to rest because
+ // we'll get pushed down by gravity faster than we can escape from the wall.
+ if ( flSpeedSqr < ( 30 * 30 ) )
+ {
+ // Reset velocities.
+ SetAbsVelocity( vec3_origin );
+ SetLocalAngularVelocity( vec3_angle );
+ }
+ else
+ {
+ SetAbsVelocity( vecAbsVelocity );
+ }
+ }
+
+ BounceSound();
+}
+
+char *CDODBaseGrenade::GetExplodingClassname( void )
+{
+ Assert( !"Baseclass must implement this" );
+ return NULL;
+}
+
+void CDODBaseGrenade::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ if ( !CanBePickedUp() )
+ return;
+
+ if ( !pActivator->IsPlayer() )
+ return;
+
+ CDODPlayer *pPlayer = ToDODPlayer( pActivator );
+
+ //Don't pick up grenades while deployed
+ CBaseCombatWeapon *pWpn = pPlayer->GetActiveWeapon();
+ if ( pWpn && !pWpn->CanHolster() )
+ {
+ return;
+ }
+
+ DODRoundState state = DODGameRules()->State_Get();
+
+ if ( dod_bonusround.GetBool() )
+ {
+ int team = pPlayer->GetTeamNumber();
+
+ // if its after the round and bonus round is on, we can only pick it up if we are winners
+
+ if ( team == TEAM_ALLIES && state == STATE_AXIS_WIN )
+ return;
+
+ if ( team == TEAM_AXIS && state == STATE_ALLIES_WIN )
+ return;
+ }
+ else
+ {
+ // if its after the round, and bonus round is off, don't allow anyone to pick it up
+ if ( state != STATE_RND_RUNNING )
+ return;
+ }
+
+ OnPickedUp();
+
+ char *szClsName = GetExplodingClassname();
+
+ Assert( szClsName );
+
+ CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>( pPlayer->GiveNamedItem( szClsName ) );
+
+ Assert( pWeapon && "Wpn pointer has to be valid for us to pick up this grenade" );
+
+ if( pWeapon )
+ {
+ variant_t flDetTime;
+ flDetTime.SetFloat( m_flDetonateTime );
+ pWeapon->AcceptInput( "DetonateTime", this, this, flDetTime, 0 );
+
+#ifdef DBGFLAG_ASSERT
+ bool bSuccess =
+#endif
+ pPlayer->Weapon_Switch( pWeapon );
+
+ Assert( bSuccess );
+
+ //Remove the one we picked up
+ SetThink( NULL );
+ UTIL_Remove( this );
+ }
+}
+
+int CDODBaseGrenade::OnTakeDamage( const CTakeDamageInfo &info )
+{
+ if( info.GetDamageType() & DMG_BULLET )
+ {
+ // Don't allow players to shoot grenades
+ return 0;
+ }
+ else if( info.GetDamageType() & DMG_BLAST )
+ {
+ // Don't allow explosion force to move grenades
+ return 0;
+ }
+
+ VPhysicsTakeDamage( info );
+
+ return BaseClass::OnTakeDamage( info );
+}
+
+void CDODBaseGrenade::Detonate()
+{
+ // Don't explode after the round has ended
+ if ( dod_bonusround.GetBool() == false && DODGameRules()->State_Get() != STATE_RND_RUNNING )
+ {
+ SetDamage( 0 );
+ }
+
+ // stun players in a radius
+ const float flStunDamage = 100;
+
+ CTakeDamageInfo info( this, GetThrower(), GetBlastForce(), GetAbsOrigin(), flStunDamage, DMG_STUN );
+ DODGameRules()->RadiusStun( info, GetAbsOrigin(), m_DmgRadius );
+
+ BaseClass::Detonate();
+}
+
+bool CDODBaseGrenade::CreateVPhysics()
+{
+ // Create the object in the physics system
+ VPhysicsInitNormal( SOLID_BBOX, 0, false );
+ return true;
+}
+
+void CDODBaseGrenade::Explode( trace_t *pTrace, int bitsDamageType )
+{
+ SetModelName( NULL_STRING );//invisible
+ AddSolidFlags( FSOLID_NOT_SOLID );
+
+ m_takedamage = DAMAGE_NO;
+
+ // Pull out of the wall a bit
+ if ( pTrace->fraction != 1.0 )
+ {
+ SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
+ }
+
+ // Explosion effect on client
+ Vector vecOrigin = GetAbsOrigin();
+ CPVSFilter filter( vecOrigin );
+ TE_DODExplosion( filter, 0.0f, vecOrigin, pTrace->plane.normal );
+
+ // Use the thrower's position as the reported position
+ Vector vecReported = GetThrower() ? GetThrower()->GetAbsOrigin() : vec3_origin;
+
+ CTakeDamageInfo info( this, GetThrower(), GetBlastForce(), GetAbsOrigin(), m_flDamage, bitsDamageType, 0, &vecReported );
+
+ RadiusDamage( info, vecOrigin, GetDamageRadius(), CLASS_NONE, NULL );
+
+ // Don't decal players with scorch.
+ if ( pTrace->m_pEnt && !pTrace->m_pEnt->IsPlayer() )
+ {
+ UTIL_DecalTrace( pTrace, "Scorch" );
+ }
+
+ SetThink( &CBaseGrenade::SUB_Remove );
+ SetTouch( NULL );
+
+ AddEffects( EF_NODRAW );
+ SetAbsVelocity( vec3_origin );
+ SetNextThink( gpGlobals->curtime );
+}
+
+// this will hit only things that are in newCollisionGroup, but NOT in collisionGroupAlreadyChecked
+class CTraceFilterCollisionGroupDelta : public CTraceFilterEntitiesOnly
+{
+public:
+ // It does have a base, but we'll never network anything below here..
+ DECLARE_CLASS_NOBASE( CTraceFilterCollisionGroupDelta );
+
+ CTraceFilterCollisionGroupDelta( const IHandleEntity *passentity, int collisionGroupAlreadyChecked, int newCollisionGroup )
+ : m_pPassEnt(passentity), m_collisionGroupAlreadyChecked( collisionGroupAlreadyChecked ), m_newCollisionGroup( newCollisionGroup )
+ {
+ }
+
+ virtual bool ShouldHitEntity( IHandleEntity *pHandleEntity, int contentsMask )
+ {
+ if ( !PassServerEntityFilter( pHandleEntity, m_pPassEnt ) )
+ return false;
+ CBaseEntity *pEntity = EntityFromEntityHandle( pHandleEntity );
+
+ if ( pEntity )
+ {
+ if ( g_pGameRules->ShouldCollide( m_collisionGroupAlreadyChecked, pEntity->GetCollisionGroup() ) )
+ return false;
+ if ( g_pGameRules->ShouldCollide( m_newCollisionGroup, pEntity->GetCollisionGroup() ) )
+ return true;
+ }
+
+ return false;
+ }
+
+protected:
+ const IHandleEntity *m_pPassEnt;
+ int m_collisionGroupAlreadyChecked;
+ int m_newCollisionGroup;
+};
+
+
+const float GRENADE_COEFFICIENT_OF_RESTITUTION = 0.2f;
+
+void CDODBaseGrenade::VPhysicsUpdate( IPhysicsObject *pPhysics )
+{
+ BaseClass::VPhysicsUpdate( pPhysics );
+ Vector vel;
+ AngularImpulse angVel;
+ pPhysics->GetVelocity( &vel, &angVel );
+
+ Vector start = GetAbsOrigin();
+ // find all entities that my collision group wouldn't hit, but COLLISION_GROUP_NONE would and bounce off of them as a ray cast
+ CTraceFilterCollisionGroupDelta filter( this, GetCollisionGroup(), COLLISION_GROUP_NONE );
+ trace_t tr;
+
+ UTIL_TraceLine( start, start + vel * gpGlobals->frametime, CONTENTS_HITBOX|CONTENTS_MONSTER|CONTENTS_SOLID, &filter, &tr );
+
+ bool bHitTeammate = false;
+
+ if ( m_bCollideWithTeammates == false && tr.m_pEnt && tr.m_pEnt->IsPlayer() && tr.m_pEnt->GetTeamNumber() == GetTeamNumber() )
+ {
+ bHitTeammate = true;
+ }
+
+ if ( tr.startsolid )
+ {
+ if ( m_bInSolid == false && bHitTeammate == false )
+ {
+ // UNDONE: Do a better contact solution that uses relative velocity?
+ vel *= -GRENADE_COEFFICIENT_OF_RESTITUTION; // bounce backwards
+ pPhysics->SetVelocity( &vel, NULL );
+ }
+ m_bInSolid = true;
+ return;
+ }
+ m_bInSolid = false;
+ if ( tr.DidHit() && bHitTeammate == false )
+ {
+ Vector dir = vel;
+ VectorNormalize(dir);
+
+ float flPercent = vel.Length() / 2000;
+ float flDmg = 5 * flPercent;
+
+ // send a tiny amount of damage so the character will react to getting bonked
+ CTakeDamageInfo info( this, GetThrower(), pPhysics->GetMass() * vel, GetAbsOrigin(), flDmg, DMG_CRUSH );
+ tr.m_pEnt->DispatchTraceAttack( info, dir, &tr );
+ ApplyMultiDamage();
+
+ if ( vel.Length() > 1000 )
+ {
+ CTakeDamageInfo stunInfo( this, GetThrower(), vec3_origin, GetAbsOrigin(), flDmg, DMG_STUN );
+ tr.m_pEnt->TakeDamage( stunInfo );
+ }
+
+ // reflect velocity around normal
+ vel = -2.0f * tr.plane.normal * DotProduct(vel,tr.plane.normal) + vel;
+
+ // absorb 80% in impact
+ vel *= GetElasticity();
+ angVel *= -0.5f;
+ pPhysics->SetVelocity( &vel, &angVel );
+ }
+}
+
+float CDODBaseGrenade::GetElasticity( void )
+{
+ return GRENADE_COEFFICIENT_OF_RESTITUTION;
+}
+
+const float DOD_GRENADE_WINDOW_BREAK_DAMPING_AMOUNT = 0.5f;
+
+// If we hit a window, let it break and continue on our way
+// with a damped speed
+void CDODBaseGrenade::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent )
+{
+ CBreakable *pBreakable = dynamic_cast<CBreakable*>( pEvent->pEntities[!index] );
+ if ( pBreakable && pBreakable->GetMaterialType() == matGlass && VPhysicsGetObject() )
+ {
+ // don't stop, go through this entity after breaking it
+ Vector dampedVelocity = DOD_GRENADE_WINDOW_BREAK_DAMPING_AMOUNT * pEvent->preVelocity[index];
+ VPhysicsGetObject()->SetVelocity( &dampedVelocity, &pEvent->preAngularVelocity[index] );
+ }
+ else
+ BaseClass::VPhysicsCollision( index, pEvent );
+}
diff --git a/game/server/dod/dod_basegrenade.h b/game/server/dod/dod_basegrenade.h
new file mode 100644
index 0000000..2af0d7a
--- /dev/null
+++ b/game/server/dod/dod_basegrenade.h
@@ -0,0 +1,106 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_BASEGRENADE_H
+#define DOD_BASEGRENADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "weapon_dodbase.h"
+#include "basegrenade_shared.h"
+
+class CGrenadeTrail;
+
+class CGrenadeController : public IMotionEvent
+{
+ DECLARE_SIMPLE_DATADESC();
+public:
+ virtual simresult_e Simulate( IPhysicsMotionController *pController, IPhysicsObject *pObject, float deltaTime, Vector &linear, AngularImpulse &angular );
+};
+
+class CDODBaseGrenade : public CBaseGrenade
+{
+public:
+ DECLARE_CLASS( CDODBaseGrenade, CBaseGrenade );
+
+ DECLARE_DATADESC();
+
+ DECLARE_SERVERCLASS();
+
+ CDODBaseGrenade();
+ virtual ~CDODBaseGrenade();
+
+ virtual void Spawn();
+ virtual void Precache( void );
+
+ static inline float GetGrenadeGravity() { return 0.4f; }
+ static inline const float GetGrenadeFriction() { return 0.5f; }
+ static inline const float GetGrenadeElasticity() { return 0.2f; }
+
+ void DetonateThink( void );
+
+ virtual void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+ virtual char *GetExplodingClassname( void );
+
+ virtual int ObjectCaps( void ) { return BaseClass::ObjectCaps() | FCAP_IMPULSE_USE | FCAP_USE_IN_RADIUS; }
+
+ virtual int OnTakeDamage( const CTakeDamageInfo &info );
+
+ virtual void Detonate();
+
+ bool CreateVPhysics();
+ virtual void VPhysicsUpdate( IPhysicsObject *pPhysics );
+ virtual void VPhysicsCollision( int index, gamevcollisionevent_t *pEvent );
+
+ virtual void Explode( trace_t *pTrace, int bitsDamageType );
+
+ void SetupInitialTransmittedGrenadeVelocity( const Vector &velocity )
+ {
+ m_vInitialVelocity = velocity;
+ }
+
+ CGrenadeController m_GrenadeController;
+ IPhysicsMotionController *m_pMotionController;
+
+ virtual bool CanBePickedUp( void ) { return true; }
+
+ virtual void OnPickedUp( void ) {}
+
+ virtual float GetElasticity();
+
+ virtual DODWeaponID GetEmitterWeaponID() { return m_EmitterWeaponID; }
+
+protected:
+
+ //Set the time to detonate ( now + timer )
+ virtual void SetDetonateTimerLength( float timer );
+ float m_flDetonateTime;
+
+ //Custom collision to allow for constant elasticity on hit surfaces
+ virtual void ResolveFlyCollisionCustom( trace_t &trace, Vector &vecVelocity );
+
+ bool m_bUseVPhysics;
+
+ DODWeaponID m_EmitterWeaponID;
+
+private:
+
+ CDODBaseGrenade( const CDODBaseGrenade & );
+
+ bool m_bInSolid;
+
+ // This gets sent to the client and placed in the client's interpolation history
+ // so the projectile starts out moving right off the bat.
+ CNetworkVector( m_vInitialVelocity );
+
+ float m_flCollideWithTeammatesTime;
+ bool m_bCollideWithTeammates;
+};
+
+
+#endif // DOD_BASEGRENADE_H
diff --git a/game/server/dod/dod_baserocket.cpp b/game/server/dod/dod_baserocket.cpp
new file mode 100644
index 0000000..c04f27c
--- /dev/null
+++ b/game/server/dod/dod_baserocket.cpp
@@ -0,0 +1,294 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_baserocket.h"
+#include "explode.h"
+#include "dod_shareddefs.h"
+#include "dod_gamerules.h"
+#include "fx_dod_shared.h"
+
+
+BEGIN_DATADESC( CDODBaseRocket )
+
+ // Function Pointers
+ DEFINE_FUNCTION( RocketTouch ),
+
+ DEFINE_THINKFUNC( FlyThink ),
+
+END_DATADESC()
+
+
+IMPLEMENT_SERVERCLASS_ST( CDODBaseRocket, DT_DODBaseRocket )
+ SendPropVector( SENDINFO( m_vInitialVelocity ),
+ 20, // nbits
+ 0, // flags
+ -3000, // low value
+ 3000 // high value
+ )
+END_NETWORK_TABLE()
+
+
+LINK_ENTITY_TO_CLASS( base_rocket, CDODBaseRocket );
+
+
+//-----------------------------------------------------------------------------
+// Constructor
+//-----------------------------------------------------------------------------
+CDODBaseRocket::CDODBaseRocket()
+{
+}
+
+CDODBaseRocket::~CDODBaseRocket()
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBaseRocket::Precache( void )
+{
+ PrecacheScriptSound( "Weapon_Bazooka.Shoot" );
+ PrecacheParticleSystem( "rockettrail" );
+}
+
+ConVar mp_rocketdamage( "mp_rocketdamage", "150", FCVAR_GAMEDLL | FCVAR_CHEAT );
+ConVar mp_rocketradius( "mp_rocketradius", "200", FCVAR_GAMEDLL | FCVAR_CHEAT );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBaseRocket::Spawn( void )
+{
+ Precache();
+
+ SetSolid( SOLID_BBOX );
+
+ Assert( GetModel() ); //derived classes must have set model
+
+ UTIL_SetSize( this, -Vector(2,2,2), Vector(2,2,2) );
+
+ SetTouch( &CDODBaseRocket::RocketTouch );
+
+ SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_CUSTOM );
+
+ m_takedamage = DAMAGE_NO;
+ SetGravity( 0.1 );
+ SetDamage( mp_rocketdamage.GetFloat() );
+
+ AddFlag( FL_OBJECT );
+
+ SetCollisionGroup( COLLISION_GROUP_PROJECTILE );
+
+ EmitSound( "Weapon_Bazooka.Shoot" );
+
+ m_flCollideWithTeammatesTime = gpGlobals->curtime + 0.25;
+ m_bCollideWithTeammates = false;
+
+ SetThink( &CDODBaseRocket::FlyThink );
+ SetNextThink( gpGlobals->curtime );
+}
+
+unsigned int CDODBaseRocket::PhysicsSolidMaskForEntity( void ) const
+{
+ int teamContents = 0;
+
+ if ( m_bCollideWithTeammates == false )
+ {
+ // Only collide with the other team
+ teamContents = ( GetTeamNumber() == TEAM_ALLIES ) ? CONTENTS_TEAM1 : CONTENTS_TEAM2;
+ }
+ else
+ {
+ // Collide with both teams
+ teamContents = CONTENTS_TEAM1 | CONTENTS_TEAM2;
+ }
+
+ return BaseClass::PhysicsSolidMaskForEntity() | CONTENTS_HITBOX | teamContents;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Stops any kind of tracking and shoots dumb
+//-----------------------------------------------------------------------------
+void CDODBaseRocket::Fire( void )
+{
+ SetThink( NULL );
+ SetMoveType( MOVETYPE_FLY );
+
+ SetModel("models/weapons/w_missile.mdl");
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+
+ EmitSound( "Weapon_Bazooka.Shoot" );
+}
+
+//-----------------------------------------------------------------------------
+// The actual explosion
+//-----------------------------------------------------------------------------
+void CDODBaseRocket::DoExplosion( trace_t *pTrace )
+{
+/*
+ // Explode
+ ExplosionCreate(
+ GetAbsOrigin(), //DMG_ROCKET
+ GetAbsAngles(),
+ GetOwnerEntity(),
+ GetDamage(), //magnitude
+ mp_rocketradius.GetFloat(), //radius
+ SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSMOKE,
+ 0.0f, //explosion force
+ this); //inflictor
+*/
+
+ // Pull out of the wall a bit
+ if ( pTrace->fraction != 1.0 )
+ {
+ SetAbsOrigin( pTrace->endpos + (pTrace->plane.normal * 0.6) );
+ }
+
+ // Explosion effect on client
+ Vector vecOrigin = GetAbsOrigin();
+ CPVSFilter filter( vecOrigin );
+ TE_DODExplosion( filter, 0.0f, vecOrigin, pTrace->plane.normal );
+
+ CTakeDamageInfo info( this, GetOwnerEntity(), vec3_origin, GetAbsOrigin(), GetDamage(), DMG_BLAST, 0 );
+ RadiusDamage( info, vecOrigin, mp_rocketradius.GetFloat() /* GetDamageRadius() */, CLASS_NONE, NULL );
+
+ // stun players in a radius
+ const float flStunDamage = 75;
+ const float flRadius = 150;
+
+ CTakeDamageInfo stunInfo( this, GetOwnerEntity(), vec3_origin, GetAbsOrigin(), flStunDamage, DMG_STUN );
+ DODGameRules()->RadiusStun( stunInfo, GetAbsOrigin(), flRadius );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBaseRocket::Explode( void )
+{
+ // Don't explode against the skybox. Just pretend that
+ // the missile flies off into the distance.
+ const trace_t &tr = CBaseEntity::GetTouchTrace();
+ const trace_t *p = &tr;
+ trace_t *newTrace = const_cast<trace_t*>(p);
+
+ DoExplosion( newTrace );
+
+ if ( newTrace->m_pEnt && !newTrace->m_pEnt->IsPlayer() )
+ UTIL_DecalTrace( newTrace, "Scorch" );
+
+ StopSound( "Weapon_Bazooka.Shoot" );
+ UTIL_Remove( this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *pOther -
+//-----------------------------------------------------------------------------
+void CDODBaseRocket::RocketTouch( CBaseEntity *pOther )
+{
+ Assert( pOther );
+ if ( !pOther->IsSolid() || pOther->IsSolidFlagSet(FSOLID_VOLUME_CONTENTS) )
+ return;
+
+ if ( pOther->GetCollisionGroup() == COLLISION_GROUP_WEAPON )
+ return;
+
+ // if we hit the skybox, just disappear
+ const trace_t &tr = CBaseEntity::GetTouchTrace();
+
+ const trace_t *p = &tr;
+ trace_t *newTrace = const_cast<trace_t*>(p);
+
+ if( tr.surface.flags & SURF_SKY )
+ {
+ UTIL_Remove( this );
+ return;
+ }
+
+ if( !pOther->IsPlayer() && pOther->m_takedamage == DAMAGE_YES )
+ {
+ CTakeDamageInfo info;
+ info.SetAttacker( this );
+ info.SetInflictor( this );
+ info.SetDamage( 50 );
+ info.SetDamageForce( vec3_origin ); // don't worry about this not having a damage force.
+ // It will explode on touch and impart its own forces
+ info.SetDamageType( DMG_CLUB );
+
+ Vector dir;
+ AngleVectors( GetAbsAngles(), &dir );
+
+ pOther->DispatchTraceAttack( info, dir, newTrace );
+ ApplyMultiDamage();
+
+ if( pOther->IsAlive() )
+ {
+ Explode();
+ }
+
+ // if it's not alive, continue flying
+ }
+ else
+ {
+ Explode();
+ }
+}
+
+void CDODBaseRocket::FlyThink( void )
+{
+ QAngle angles;
+
+ VectorAngles( GetAbsVelocity(), angles );
+
+ SetAbsAngles( angles );
+
+ if ( gpGlobals->curtime > m_flCollideWithTeammatesTime && m_bCollideWithTeammates == false )
+ {
+ m_bCollideWithTeammates = true;
+ }
+
+ SetNextThink( gpGlobals->curtime + 0.1f );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//
+// Input : &vecOrigin -
+// &vecAngles -
+// NULL -
+//
+// Output : CDODBaseRocket
+//-----------------------------------------------------------------------------
+CDODBaseRocket *CDODBaseRocket::Create( const char *szClassname, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner = NULL )
+{
+ CDODBaseRocket *pMissile = (CDODBaseRocket *) CBaseEntity::Create( szClassname, vecOrigin, vecAngles, pOwner );
+ pMissile->SetOwnerEntity( pOwner );
+ pMissile->Spawn();
+
+ Vector vecForward;
+ AngleVectors( vecAngles, &vecForward );
+
+ Vector vRocket = vecForward * 1300;
+
+ pMissile->SetAbsVelocity( vRocket );
+ pMissile->SetupInitialTransmittedGrenadeVelocity( vRocket );
+
+ pMissile->SetAbsAngles( vecAngles );
+
+ // remember what team we should be on
+ pMissile->ChangeTeam( pOwner->GetTeamNumber() );
+
+ return pMissile;
+}
+
+void CDODBaseRocket::SetupInitialTransmittedGrenadeVelocity( const Vector &velocity )
+{
+ m_vInitialVelocity = velocity;
+}
diff --git a/game/server/dod/dod_baserocket.h b/game/server/dod/dod_baserocket.h
new file mode 100644
index 0000000..6e1e636
--- /dev/null
+++ b/game/server/dod/dod_baserocket.h
@@ -0,0 +1,67 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DOD_BASEROCKET_H
+#define DOD_BASEROCKET_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "cbase.h"
+#include "baseanimating.h"
+#include "smoke_trail.h"
+#include "weapon_dodbase.h"
+
+class RocketTrail;
+
+//================================================
+// CDODBaseRocket
+//================================================
+class CDODBaseRocket : public CBaseAnimating
+{
+ DECLARE_CLASS( CDODBaseRocket, CBaseAnimating );
+
+public:
+ CDODBaseRocket();
+ ~CDODBaseRocket();
+
+ void Spawn( void );
+ void Precache( void );
+ void RocketTouch( CBaseEntity *pOther );
+ void Explode( void );
+ void Fire( void );
+
+ virtual float GetDamage() { return m_flDamage; }
+ virtual void SetDamage(float flDamage) { m_flDamage = flDamage; }
+
+ unsigned int PhysicsSolidMaskForEntity( void ) const;
+
+ static CDODBaseRocket *Create( const char *szClassname, const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner );
+
+ void SetupInitialTransmittedGrenadeVelocity( const Vector &velocity );
+
+ virtual DODWeaponID GetEmitterWeaponID() { return WEAPON_NONE; Assert(0); }
+
+protected:
+ virtual void DoExplosion( trace_t *pTrace );
+
+ void FlyThink( void );
+
+ float m_flDamage;
+
+ CNetworkVector( m_vInitialVelocity );
+
+private:
+ DECLARE_DATADESC();
+ DECLARE_SERVERCLASS();
+
+ float m_flCollideWithTeammatesTime;
+ bool m_bCollideWithTeammates;
+};
+
+#endif // DOD_BASEROCKET_H \ No newline at end of file
diff --git a/game/server/dod/dod_bombdispenser.cpp b/game/server/dod/dod_bombdispenser.cpp
new file mode 100644
index 0000000..828230d
--- /dev/null
+++ b/game/server/dod/dod_bombdispenser.cpp
@@ -0,0 +1,144 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_player.h"
+#include "dod_bombtarget.h"
+#include "triggers.h"
+
+class CDODBombDispenserMapIcon;
+
+class CDODBombDispenser : public CBaseTrigger
+{
+public:
+ DECLARE_CLASS( CDODBombDispenser, CBaseTrigger );
+ DECLARE_DATADESC();
+
+ virtual void Spawn( void );
+ void EXPORT Touch( CBaseEntity *pOther );
+
+ bool IsActive( void ) { return !m_bDisabled; }
+
+private:
+
+ void InputEnable( inputdata_t &inputdata );
+ void InputDisable( inputdata_t &inputdata );
+
+ // Which team to give bombs to. TEAM_UNASSIGNED gives to both
+ int m_iDispenseToTeam;
+
+ // Is this area giving out bombs?
+ bool m_bActive;
+};
+
+BEGIN_DATADESC(CDODBombDispenser)
+
+ // Touch functions
+ DEFINE_FUNCTION( Touch ),
+
+ // Inputs
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+
+ DEFINE_KEYFIELD( m_iDispenseToTeam, FIELD_INTEGER, "dispense_team" ),
+ DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
+
+END_DATADESC();
+
+LINK_ENTITY_TO_CLASS( dod_bomb_dispenser, CDODBombDispenser );
+
+
+void CDODBombDispenser::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ InitTrigger();
+
+ SetTouch( &CDODBombDispenser::Touch );
+
+ m_bDisabled = false;
+
+ // make our map icon entity
+#ifdef DBGFLAG_ASSERT
+ CBaseEntity *pIcon =
+#endif
+ CBaseEntity::Create( "dod_bomb_dispenser_icon", WorldSpaceCenter(), GetAbsAngles(), this );
+
+ Assert( pIcon );
+}
+
+void CDODBombDispenser::Touch( CBaseEntity *pOther )
+{
+ if ( m_bDisabled )
+ return;
+
+ if( !pOther->IsPlayer() )
+ return;
+
+ if( !pOther->IsAlive() )
+ return;
+
+ if ( m_iDispenseToTeam != TEAM_UNASSIGNED && pOther->GetTeamNumber() != m_iDispenseToTeam )
+ return;
+
+ CDODPlayer *pPlayer = ToDODPlayer( pOther );
+
+ pPlayer->HintMessage( HINT_BOMB_PICKUP );
+
+ switch( pPlayer->GetTeamNumber() )
+ {
+ case TEAM_ALLIES:
+ case TEAM_AXIS:
+ {
+ if ( pPlayer->Weapon_OwnsThisType( "weapon_basebomb" ) == NULL )
+ {
+ pPlayer->GiveNamedItem( "weapon_basebomb" );
+
+ CPASFilter filter( pPlayer->WorldSpaceCenter() );
+ pPlayer->EmitSound( filter, pPlayer->entindex(), "Weapon_C4.PickUp" );
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+}
+
+void CDODBombDispenser::InputEnable( inputdata_t &inputdata )
+{
+ m_bDisabled = false;
+}
+
+void CDODBombDispenser::InputDisable( inputdata_t &inputdata )
+{
+ m_bDisabled = true;
+}
+
+class CDODBombDispenserMapIcon : public CBaseEntity
+{
+public:
+ DECLARE_CLASS( CDODBombDispenserMapIcon, CBaseEntity );
+
+ DECLARE_NETWORKCLASS();
+
+ virtual int UpdateTransmitState( void )
+ {
+ if ( (( CDODBombDispenser * )GetOwnerEntity())->IsActive() )
+ {
+ return SetTransmitState( FL_EDICT_ALWAYS );
+ }
+ else
+ {
+ return SetTransmitState( FL_EDICT_DONTSEND );
+ }
+ }
+};
+
+IMPLEMENT_SERVERCLASS_ST(CDODBombDispenserMapIcon, DT_DODBombDispenserMapIcon)
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( dod_bomb_dispenser_icon, CDODBombDispenserMapIcon );
diff --git a/game/server/dod/dod_bombtarget.cpp b/game/server/dod/dod_bombtarget.cpp
new file mode 100644
index 0000000..6956717
--- /dev/null
+++ b/game/server/dod/dod_bombtarget.cpp
@@ -0,0 +1,767 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_player.h"
+#include "dod_gamerules.h"
+#include "dod_shareddefs.h"
+#include "dod_bombtarget.h"
+#include "basecombatweapon.h"
+#include "weapon_dodbasebomb.h"
+#include "dod_team.h"
+#include "dod_shareddefs.h"
+#include "dod_objective_resource.h"
+
+BEGIN_DATADESC(CDODBombTarget)
+
+ DEFINE_KEYFIELD( m_iszCapPointName, FIELD_STRING, "target_control_point" ),
+
+ DEFINE_KEYFIELD( m_iBombingTeam, FIELD_INTEGER, "bombing_team" ),
+
+ DEFINE_KEYFIELD( m_iTimerAddSeconds, FIELD_INTEGER, "add_timer_seconds" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+
+ DEFINE_OUTPUT( m_OnBecomeActive, "OnBombTargetActivated" ),
+ DEFINE_OUTPUT( m_OnBecomeInactive, "OnBombTargetDeactivated" ),
+ DEFINE_OUTPUT( m_OnBombPlanted, "OnBombPlanted" ),
+ DEFINE_OUTPUT( m_OnBombExploded, "OnBombExploded" ),
+ DEFINE_OUTPUT( m_OnBombDisarmed, "OnBombDefused" ),
+
+ DEFINE_KEYFIELD( m_bStartDisabled, FIELD_INTEGER, "StartDisabled" ),
+
+ DEFINE_USEFUNC( State_Use ),
+ DEFINE_THINKFUNC( State_Think ),
+
+END_DATADESC();
+
+IMPLEMENT_SERVERCLASS_ST(CDODBombTarget, DT_DODBombTarget)
+ SendPropInt( SENDINFO(m_iState), 3 ),
+ SendPropInt( SENDINFO(m_iBombingTeam), 3 ),
+
+ SendPropModelIndex( SENDINFO(m_iTargetModel) ),
+ SendPropModelIndex( SENDINFO(m_iUnavailableModel) ),
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( dod_bomb_target, CDODBombTarget );
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::Spawn( void )
+{
+ m_pControlPoint = NULL;
+
+ Precache();
+
+ SetTouch( NULL );
+ SetUse( &CDODBombTarget::State_Use );
+ SetThink( &CDODBombTarget::State_Think );
+ SetNextThink( gpGlobals->curtime + 0.1 );
+
+ m_pCurStateInfo = NULL;
+ if ( m_bStartDisabled )
+ State_Transition( BOMB_TARGET_INACTIVE );
+ else
+ State_Transition( BOMB_TARGET_ACTIVE );
+
+ BaseClass::Spawn();
+
+ // incase we have any animating bomb models
+ SetPlaybackRate( 1.0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::Precache( void )
+{
+ BaseClass::Precache();
+
+ PrecacheModel( DOD_BOMB_TARGET_MODEL_ARMED );
+ m_iTargetModel = PrecacheModel( DOD_BOMB_TARGET_MODEL_TARGET );
+ m_iUnavailableModel = PrecacheModel( DOD_BOMB_TARGET_MODEL_UNAVAILABLE );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: change use flags based on state
+//-----------------------------------------------------------------------------
+int CDODBombTarget::ObjectCaps()
+{
+ int caps = BaseClass::ObjectCaps();
+
+ if ( State_Get() != BOMB_TARGET_INACTIVE )
+ {
+ caps |= (FCAP_CONTINUOUS_USE | FCAP_USE_IN_RADIUS);
+ }
+
+ return caps;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: timer length accessor
+//-----------------------------------------------------------------------------
+float CDODBombTarget::GetBombTimerLength( void )
+{
+ return DOD_BOMB_TIMER_LENGTH;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Transition( BombTargetState newState )
+{
+ State_Leave();
+ State_Enter( newState );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Enter( BombTargetState newState )
+{
+ m_pCurStateInfo = State_LookupInfo( newState );
+
+ if ( 0 )
+ {
+ if ( m_pCurStateInfo )
+ Msg( "DODRoundState: entering '%s'\n", m_pCurStateInfo->m_pStateName );
+ else
+ Msg( "DODRoundState: entering #%d\n", newState );
+ }
+
+ // Initialize the new state.
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
+ (this->*m_pCurStateInfo->pfnEnterState)();
+
+ m_iState = (int)newState;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Leave()
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
+ {
+ (this->*m_pCurStateInfo->pfnLeaveState)();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Think()
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnThink )
+ {
+ (this->*m_pCurStateInfo->pfnThink)();
+ }
+
+ SetNextThink( gpGlobals->curtime + 0.1 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnUse )
+ {
+ (this->*m_pCurStateInfo->pfnUse)( pActivator, pCaller, useType, value );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CDODBombTargetStateInfo* CDODBombTarget::State_LookupInfo( BombTargetState state )
+{
+ static CDODBombTargetStateInfo bombTargetStateInfos[] =
+ {
+ { BOMB_TARGET_INACTIVE, "BOMB_TARGET_INACTIVE", &CDODBombTarget::State_Enter_INACTIVE, NULL, NULL, NULL },
+ { BOMB_TARGET_ACTIVE, "BOMB_TARGET_ACTIVE", &CDODBombTarget::State_Enter_ACTIVE, NULL, NULL, &CDODBombTarget::State_Use_ACTIVE },
+ { BOMB_TARGET_ARMED, "BOMB_TARGET_ARMED", &CDODBombTarget::State_Enter_ARMED, &CDODBombTarget::State_Leave_Armed, &CDODBombTarget::State_Think_ARMED, &CDODBombTarget::State_Use_ARMED }
+ };
+
+ for ( int i=0; i < ARRAYSIZE( bombTargetStateInfos ); i++ )
+ {
+ if ( bombTargetStateInfos[i].m_iState == state )
+ return &bombTargetStateInfos[i];
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Enter_INACTIVE( void )
+{
+ // go invisible
+ AddEffects( EF_NODRAW );
+
+ m_OnBecomeInactive.FireOutput( this, this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Enter_ACTIVE( void )
+{
+ RemoveEffects( EF_NODRAW );
+
+ // set transparent model
+ SetModel( DOD_BOMB_TARGET_MODEL_TARGET );
+
+ m_OnBecomeActive.FireOutput( this, this );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Enter_ARMED( void )
+{
+ RemoveEffects( EF_NODRAW );
+
+ // set solid model
+ SetModel( DOD_BOMB_TARGET_MODEL_ARMED );
+
+ // start count down
+ m_flExplodeTime = gpGlobals->curtime + GetBombTimerLength();
+
+ m_OnBombPlanted.FireOutput( this, this );
+
+ // tell CP our time until detonation
+ CControlPoint *pCP = GetControlPoint();
+ if ( pCP )
+ {
+ pCP->BombPlanted( GetBombTimerLength(), m_pPlantingPlayer );
+ }
+
+ EmitSound( "Weapon_C4.Fuse" );
+
+ static int iWickSeq = LookupSequence( "w_tnt_wick" );
+ ResetSequence( iWickSeq );
+}
+
+void CDODBombTarget::State_Leave_Armed( void )
+{
+ StopSound( "Weapon_C4.Fuse" );
+}
+
+void CDODBombTarget::ResetDefuse( int index )
+{
+ DefusingPlayer *pRec = &m_DefusingPlayers[index];
+
+ Assert( pRec );
+
+ if ( pRec && pRec->m_pPlayer )
+ {
+ pRec->m_pPlayer->SetDefusing( NULL );
+
+ //cancel the progress bar
+ pRec->m_pPlayer->SetProgressBarTime( 0 );
+ }
+
+ m_DefusingPlayers.Remove( index );
+
+ // if noone else is defusing, set objective resource to not be defusing
+ if ( m_DefusingPlayers.Count() <= 0 )
+ {
+ CControlPoint *pCP = GetControlPoint();
+ if ( pCP )
+ {
+ g_pObjectiveResource->SetBombBeingDefused( pCP->GetPointIndex(), false );
+ }
+ }
+}
+
+#include "IEffects.h"
+void TE_Sparks( IRecipientFilter& filter, float delay,
+ const Vector *pos, int nMagnitude, int nTrailLength, const Vector *pDir );
+
+extern short g_sModelIndexFireball;
+
+void CDODBombTarget::Explode( void )
+{
+ // output the explosion
+ EmitSound( "Weapon_C4.Explode" );
+
+ Vector origin = GetAbsOrigin();
+
+ CPASFilter filter( origin );
+
+ te->Explosion( filter, -1.0, // don't apply cl_interp delay
+ &origin,
+ g_sModelIndexFireball,
+ 20, //scale
+ 25,
+ TE_EXPLFLAG_NONE,
+ 0,
+ 0 );
+
+ float flDamage = 200;
+ float flDmgRadius = flDamage * 2.5;
+ // do a separate radius damage that ignores the world for added damage!
+ CTakeDamageInfo dmgInfo( this, m_pPlantingPlayer, vec3_origin, origin, flDamage, DMG_BLAST | DMG_BOMB );
+ DODGameRules()->RadiusDamage( dmgInfo, origin, flDmgRadius, CLASS_NONE, NULL, true );
+
+ // stun players in a radius
+ const float flStunDamage = 100;
+
+ CTakeDamageInfo stunInfo( this, this, vec3_origin, GetAbsOrigin(), flStunDamage, DMG_STUN );
+ DODGameRules()->RadiusStun( stunInfo, GetAbsOrigin(), flDmgRadius );
+
+ State_Transition( BOMB_TARGET_INACTIVE );
+
+ m_OnBombExploded.FireOutput( this, this );
+
+ // tell CP bomb is no longer active
+ CControlPoint *pCP = GetControlPoint();
+ if ( pCP )
+ {
+ CDODPlayer *pPlayer = m_pPlantingPlayer;
+
+ if ( !pPlayer || !pPlayer->IsConnected() )
+ {
+ // pick a random player from the bombing team
+
+ // hax - if we are debug and team is 0, use allies
+ if ( m_iBombingTeam == TEAM_UNASSIGNED )
+ m_iBombingTeam = TEAM_ALLIES;
+
+ CDODTeam *pTeam = GetGlobalDODTeam( m_iBombingTeam );
+
+ pPlayer = NULL;
+
+ if ( pTeam->GetNumPlayers() > 0 )
+ {
+ pPlayer = ToDODPlayer( pTeam->GetPlayer( 0 ) );
+
+ if ( !pPlayer || !pPlayer->IsConnected() )
+ {
+ // bad situation, abandon here
+ pPlayer = NULL;
+ }
+ }
+ }
+
+ pCP->BombExploded( pPlayer, m_iBombingTeam );
+
+ g_pObjectiveResource->SetBombBeingDefused( pCP->GetPointIndex(), false );
+ }
+
+ // If we add time to the round timer, tell Gamerules
+ // Don't do this if this bomb ended the game
+ if ( m_iTimerAddSeconds > 0 )
+ {
+ if ( m_pPlantingPlayer && FStrEq( STRING(gpGlobals->mapname), "dod_jagd" ) )
+ {
+ // if the timer is 0:00 or less, achievement time
+ if ( DODGameRules()->GetTimerSeconds() <= 0 )
+ {
+ m_pPlantingPlayer->AwardAchievement( ACHIEVEMENT_DOD_JAGD_OVERTIME_CAP );
+ }
+ }
+
+ if ( DODGameRules()->State_Get() == STATE_RND_RUNNING )
+ {
+ DODGameRules()->AddTimerSeconds( m_iTimerAddSeconds );
+ }
+ }
+}
+
+void CDODBombTarget::BombDefused( CDODPlayer *pDefuser )
+{
+ // tell CP bomb is no longer active
+ CControlPoint *pCP = GetControlPoint();
+ if ( pCP )
+ {
+ pCP->BombDisarmed( pDefuser );
+ }
+
+ if ( pDefuser )
+ {
+ pDefuser->StatEvent_BombDefused();
+ }
+
+ State_Transition( BOMB_TARGET_ACTIVE );
+
+ m_OnBombDisarmed.FireOutput( this, this );
+}
+
+bool CDODBombTarget::CanPlayerStartDefuse( CDODPlayer *pPlayer )
+{
+ if ( !pPlayer || !pPlayer->IsAlive() || !pPlayer->IsConnected() )
+ {
+ // if the defuser is not alive or has disconnected, reset
+ return false;
+ }
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ {
+ // they are not on the ground, remove them
+ pPlayer->HintMessage( HINT_BOMB_DEFUSE_ONGROUND );
+
+ return false;
+ }
+
+ Vector vecDist = ( pPlayer->GetAbsOrigin() - GetAbsOrigin() );
+ float flDist = vecDist.Length();
+
+ if ( flDist > DOD_BOMB_DEFUSE_MAXDIST ) // PLAYER_USE_RADIUS is not actually used by the playerUse code!!
+ {
+ // they are too far away to continue ( or start ) defusing
+ return false;
+ }
+
+ return true;
+}
+
+bool CDODBombTarget::CanPlayerContinueDefusing( CDODPlayer *pPlayer, DefusingPlayer *pDefuseRecord )
+{
+ if ( !pDefuseRecord || pDefuseRecord->m_flDefuseTimeoutTime < gpGlobals->curtime )
+ {
+ // they have not updated their +use in a while, assume they stopped
+ return false;
+ }
+
+ return CanPlayerStartDefuse( pPlayer );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Think_ARMED( void )
+{
+ // count down
+
+ if ( DODGameRules()->State_Get() != STATE_RND_RUNNING )
+ {
+ State_Transition( BOMB_TARGET_ACTIVE );
+
+ // reset the timer
+ CControlPoint *pCP = GetControlPoint();
+ if ( pCP )
+ {
+ pCP->CancelBombPlanted();
+ }
+ }
+
+ // manually advance frame so that it matches bomb timer length.
+ float flTimerLength = GetBombTimerLength();
+ float flTimeLeft = m_flExplodeTime - gpGlobals->curtime;
+ SetCycle( clamp( 1.0 - ( flTimeLeft / flTimerLength ), 0.0, 1.0 ) );
+
+ static int iAttachment = LookupAttachment( "wick" );//ed awesome
+
+ Vector pos;
+ QAngle ang;
+ GetAttachment( iAttachment, pos, ang );
+
+ Vector forward;
+ AngleVectors( ang, &forward );
+
+ CPVSFilter filter( pos );
+ TE_Sparks( filter, 0.0, &pos, 1, 1, &forward );
+
+ // So long as we have valid defusers, we will not explode
+
+ if ( m_DefusingPlayers.Count() > 0 )
+ {
+ // remove the undesirables
+ // make sure they are on ground still
+ // see if they have completed the defuse
+
+ for ( int i=m_DefusingPlayers.Count()-1;i>=0;i-- )
+ {
+ DefusingPlayer *pDefuseRecord = &m_DefusingPlayers[i];
+
+ CDODPlayer *pPlayer = pDefuseRecord->m_pPlayer;
+
+ if ( !CanPlayerContinueDefusing( pPlayer, pDefuseRecord ) )
+ {
+ ResetDefuse( i );
+ }
+ else
+ {
+ // they are still a valid defuser
+
+ // if their defuse complete time has passed
+ if ( pDefuseRecord->m_flDefuseCompleteTime < gpGlobals->curtime )
+ {
+ // Defuse Complete
+ BombDefused( pPlayer );
+
+ // remove everyone from the list
+ for ( int j=m_DefusingPlayers.Count()-1;j>=0;j-- )
+ {
+ ResetDefuse( j );
+ }
+
+ // break out of this loop
+ i = -1;
+ }
+ }
+ }
+ }
+ else if ( gpGlobals->curtime > m_flExplodeTime )
+ {
+ // no defusers, time is up
+ Explode();
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Use_ACTIVE( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ CDODPlayer *pPlayer = ToDODPlayer( pActivator );
+
+ if ( !CanPlantHere( pPlayer ) )
+ return;
+
+ Vector pos = pPlayer->WorldSpaceCenter();
+
+ float flDist = ( pos - GetAbsOrigin() ).Length();
+
+ if ( flDist > DOD_BOMB_PLANT_RADIUS )
+ {
+ return;
+ }
+
+ if ( !FBitSet( pPlayer->GetFlags(), FL_ONGROUND ) )
+ {
+ pPlayer->HintMessage( HINT_BOMB_DEFUSE_ONGROUND, true );
+ return;
+ }
+
+ CBaseCombatWeapon *pWeapon = NULL;
+
+ if ( ( pWeapon = pPlayer->Weapon_OwnsThisType( "weapon_basebomb" ) ) != NULL )
+ {
+ CDODBaseBombWeapon *pBomb = dynamic_cast<CDODBaseBombWeapon *>( pWeapon );
+
+ if ( pBomb )
+ {
+ // switch to their bomb, they will have to hit primary attack
+ pPlayer->Weapon_Switch( pBomb );
+
+ if ( pBomb == pPlayer->GetActiveWeapon() )
+ {
+ pBomb->PrimaryAttack();
+ }
+ }
+ }
+ else
+ {
+ // they don't have a bomb - play hint message
+ pPlayer->HintMessage( HINT_NEED_BOMB );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::CompletePlanting( CDODPlayer *pPlantingPlayer )
+{
+ if ( pPlantingPlayer && ( pPlantingPlayer->GetTeamNumber() == m_iBombingTeam || m_iBombingTeam == TEAM_UNASSIGNED ) )
+ {
+ m_pPlantingPlayer = pPlantingPlayer;
+
+ State_Transition( BOMB_TARGET_ARMED );
+ }
+}
+
+DefusingPlayer *CDODBombTarget::FindDefusingPlayer( CDODPlayer *pPlayer )
+{
+ DefusingPlayer *pRec = NULL;
+
+ for ( int i=0;i<m_DefusingPlayers.Count();i++ )
+ {
+ if ( m_DefusingPlayers[i].m_pPlayer == pPlayer )
+ {
+ pRec = &m_DefusingPlayers[i];
+ break;
+ }
+ }
+
+ return pRec;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::State_Use_ARMED( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value )
+{
+ // check for people disarming
+ CDODPlayer *pPlayer = ToDODPlayer( pActivator );
+
+ if ( !pPlayer || pPlayer->GetTeamNumber() == m_iBombingTeam || pPlayer->GetTeamNumber() == TEAM_SPECTATOR )
+ return;
+
+ // check for distance, offground etc
+ if ( CanPlayerStartDefuse( pPlayer ) == false )
+ {
+ return;
+ }
+
+ DefusingPlayer *pDefusingPlayerRecord = FindDefusingPlayer( pPlayer );
+
+ // See if we already added this player to the defusing list
+ if ( pDefusingPlayerRecord )
+ {
+ // They are still defusing for the next 0.2 seconds
+ pDefusingPlayerRecord->m_flDefuseTimeoutTime = gpGlobals->curtime + 0.2;
+ }
+ else
+ {
+ // add player to the list
+ DefusingPlayer defusingPlayer;
+
+ defusingPlayer.m_pPlayer = pPlayer;
+ defusingPlayer.m_flDefuseCompleteTime = gpGlobals->curtime + DOD_BOMB_DEFUSE_TIME;
+ defusingPlayer.m_flDefuseTimeoutTime = gpGlobals->curtime + 0.2;
+
+ m_DefusingPlayers.AddToTail( defusingPlayer );
+
+ // tell the player they are defusing
+ pPlayer->SetDefusing( this );
+ pPlayer->SetProgressBarTime( DOD_BOMB_DEFUSE_TIME );
+
+ CControlPoint *pCP = GetControlPoint();
+ if ( pCP )
+ {
+ g_pObjectiveResource->SetBombBeingDefused( pCP->GetPointIndex(), true );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::InputEnable( inputdata_t &inputdata )
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->m_iState == BOMB_TARGET_INACTIVE )
+ {
+ State_Transition( BOMB_TARGET_ACTIVE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::InputDisable( inputdata_t &inputdata )
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->m_iState != BOMB_TARGET_INACTIVE )
+ {
+ if ( m_pCurStateInfo->m_iState == BOMB_TARGET_ARMED )
+ {
+ // if planting, tell our cp we're not planting anymore
+ CControlPoint *pCP = GetControlPoint();
+ if ( pCP )
+ {
+ pCP->CancelBombPlanted();
+ }
+ }
+
+ State_Transition( BOMB_TARGET_INACTIVE );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CControlPoint *CDODBombTarget::GetControlPoint( void )
+{
+ if ( !m_pControlPoint )
+ {
+ if ( m_iszCapPointName == NULL_STRING )
+ return NULL;
+
+ // try to find it
+ m_pControlPoint = dynamic_cast<CControlPoint *>( gEntList.FindEntityByName( NULL, STRING(m_iszCapPointName) ) );
+
+ if ( !m_pControlPoint )
+ Warning( "Could not find dod_control_point named \"%s\" for dod_bomb_target \"%s\"\n", STRING(m_iszCapPointName), STRING( GetEntityName() ) );
+ }
+
+ return m_pControlPoint;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CDODBombTarget::CanPlantHere( CDODPlayer *pPlayer )
+{
+ if ( !m_pCurStateInfo )
+ return false;
+
+ if ( m_pCurStateInfo->m_iState != BOMB_TARGET_ACTIVE )
+ return false;
+
+ if ( pPlayer->GetTeamNumber() != m_iBombingTeam && m_iBombingTeam != TEAM_UNASSIGNED )
+ return false;
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODBombTarget::DefuseBlocked( CDODPlayer *pAttacker )
+{
+ CControlPoint *pPoint = GetControlPoint();
+
+ if ( !pPoint )
+ return;
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_capture_blocked" );
+
+ if ( event )
+ {
+ event->SetInt( "cp", pPoint->GetPointIndex() );
+ event->SetString( "cpname", pPoint->GetName() );
+ event->SetInt( "blocker", pAttacker->entindex() );
+ event->SetInt( "priority", 9 );
+ event->SetBool( "bomb", true );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ pAttacker->AddScore( PLAYER_POINTS_FOR_BLOCK );
+
+ pAttacker->Stats_AreaDefended();
+
+}
+
+void CDODBombTarget::PlantBlocked( CDODPlayer *pAttacker )
+{
+ CControlPoint *pPoint = GetControlPoint();
+
+ if ( !pPoint )
+ return;
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_capture_blocked" );
+
+ if ( event )
+ {
+ event->SetInt( "cp", pPoint->GetPointIndex() );
+ event->SetString( "cpname", pPoint->GetName() );
+ event->SetInt( "blocker", pAttacker->entindex() );
+ event->SetInt( "priority", 9 );
+ event->SetBool( "bomb", true );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ pAttacker->AddScore( PLAYER_POINTS_FOR_BLOCK );
+
+ pAttacker->Stats_AreaDefended();
+}
diff --git a/game/server/dod/dod_bombtarget.h b/game/server/dod/dod_bombtarget.h
new file mode 100644
index 0000000..316ca80
--- /dev/null
+++ b/game/server/dod/dod_bombtarget.h
@@ -0,0 +1,154 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DOD_BOMBTARGET_H
+#define DOD_BOMBTARGET_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "baseanimating.h"
+#include "dod_control_point.h"
+
+#define DOD_BOMB_TARGET_MODEL_ARMED "models/weapons/w_tnt.mdl"
+#define DOD_BOMB_TARGET_MODEL_TARGET "models/weapons/w_tnt_red.mdl"
+#define DOD_BOMB_TARGET_MODEL_UNAVAILABLE "models/weapons/w_tnt_grey.mdl"
+
+class CDODBombTarget;
+
+class CDODBombTargetStateInfo
+{
+public:
+ BombTargetState m_iState;
+ const char *m_pStateName;
+
+ void (CDODBombTarget::*pfnEnterState)(); // Init and deinit the state.
+ void (CDODBombTarget::*pfnLeaveState)();
+ void (CDODBombTarget::*pfnThink)(); // Do a PreThink() in this state.
+ void (CDODBombTarget::*pfnUse)( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+};
+
+
+struct DefusingPlayer
+{
+ CHandle<CDODPlayer> m_pPlayer;
+
+ float m_flDefuseTimeoutTime;
+ float m_flDefuseCompleteTime;
+};
+
+class CDODBombTarget : public CBaseAnimating
+{
+public:
+ DECLARE_CLASS( CDODBombTarget, CBaseAnimating );
+ DECLARE_DATADESC();
+
+ DECLARE_NETWORKCLASS();
+
+ CDODBombTarget() {}
+
+ virtual void Spawn( void );
+ virtual void Precache( void );
+
+ // Set these flags so players can use the bomb target ( for planting or defusing )
+ virtual int ObjectCaps( void );
+
+ // Inputs
+ void InputEnable( inputdata_t &inputdata );
+ void InputDisable( inputdata_t &inputdata );
+
+ // State Functions
+ void State_Transition( BombTargetState newState );
+ void State_Enter( BombTargetState newState );
+ void State_Leave();
+ void State_Think();
+ void State_Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+ int State_Get( void ) { return m_iState; }
+
+ CDODBombTargetStateInfo* State_LookupInfo( BombTargetState state );
+
+ // Enter
+ void State_Enter_INACTIVE( void );
+ void State_Enter_ACTIVE( void );
+ void State_Enter_ARMED( void );
+
+ // Leave
+ void State_Leave_Armed( void );
+
+ // Think
+ void State_Think_ARMED( void );
+
+ // Use
+ void State_Use_ACTIVE( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+ void State_Use_ARMED( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
+
+ float GetBombTimerLength( void );
+
+ // Get the dod_control_point that we're linked to
+ CControlPoint *GetControlPoint( void );
+
+ bool CanPlantHere( CDODPlayer *pPlayer );
+ void CompletePlanting( CDODPlayer *pPlantingPlayer );
+
+ void DefuseBlocked( CDODPlayer *pAttacker );
+ void PlantBlocked( CDODPlayer *pAttacker );
+
+ int GetTimerAddSeconds( void ) { return m_iTimerAddSeconds; }
+
+ int GetBombingTeam( void ) { return m_iBombingTeam; }
+
+ DefusingPlayer *FindDefusingPlayer( CDODPlayer *pPlayer );
+ void Explode( void );
+ void BombDefused( CDODPlayer *pDefuser );
+ void ResetDefuse( int index );
+
+ bool CanPlayerStartDefuse( CDODPlayer *pPlayer );
+ bool CanPlayerContinueDefusing( CDODPlayer *pPlayer, DefusingPlayer *pDefuseRecord );
+private:
+
+ // Control Point we're linked to
+ string_t m_iszCapPointName;
+ CControlPoint *m_pControlPoint;
+
+ // Outputs
+ COutputEvent m_OnBecomeActive;
+ COutputEvent m_OnBecomeInactive;
+ COutputEvent m_OnBombPlanted;
+ COutputEvent m_OnBombExploded;
+ COutputEvent m_OnBombDisarmed;
+
+ // state
+ CNetworkVar( int, m_iState );
+ CDODBombTargetStateInfo *m_pCurStateInfo;
+ bool m_bStartDisabled;
+
+ // timers for armed state
+ float m_flExplodeTime;
+ float m_flNextAnnounce;
+
+ // player that planted this bomb
+ CHandle<CDODPlayer> m_pPlantingPlayer;
+
+ // The team that is allowed to plant bombs here
+ CNetworkVar( int, m_iBombingTeam );
+
+ // network the model indices of active bomb so we can change per-team
+ CNetworkVar( int, m_iTargetModel );
+ CNetworkVar( int, m_iUnavailableModel );
+
+ int m_iTimerAddSeconds;
+
+ // List of defusing players and time until completion
+ CUtlVector< DefusingPlayer > m_DefusingPlayers;
+
+private:
+ CDODBombTarget( const CDODBombTarget & );
+};
+
+#endif //DOD_BOMBTARGET_H \ No newline at end of file
diff --git a/game/server/dod/dod_bot_temp.cpp b/game/server/dod/dod_bot_temp.cpp
new file mode 100644
index 0000000..414770d
--- /dev/null
+++ b/game/server/dod/dod_bot_temp.cpp
@@ -0,0 +1,453 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Basic BOT handling.
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "player.h"
+#include "dod_player.h"
+#include "in_buttons.h"
+#include "movehelper_server.h"
+
+void ClientPutInServer( edict_t *pEdict, const char *playername );
+void Bot_Think( CDODPlayer *pBot );
+
+ConVar bot_forcefireweapon( "bot_forcefireweapon", "", 0, "Force bots with the specified weapon to fire." );
+ConVar bot_forceattack2( "bot_forceattack2", "0", 0, "When firing, use attack2." );
+ConVar bot_forceattackon( "bot_forceattackon", "0", 0, "When firing, don't tap fire, hold it down." );
+ConVar bot_flipout( "bot_flipout", "0", 0, "When on, all bots fire their guns." );
+ConVar bot_defend( "bot_defend", "0", 0, "Set to a team number, and that team will all keep their combat shields raised." );
+ConVar bot_changeclass( "bot_changeclass", "0", 0, "Force all bots to change to the specified class." );
+ConVar bot_zombie( "bot_zombie", "0", 0, "Brraaaaaiiiins." );
+static ConVar bot_mimic( "bot_mimic", "0", 0, "Bot uses usercmd of player by index." );
+static ConVar bot_mimic_yaw_offset( "bot_mimic_yaw_offset", "0", 0, "Offsets the bot yaw." );
+ConVar bot_attack( "bot_attack", "0", 0, "Shoot!" );
+
+ConVar bot_sendcmd( "bot_sendcmd", "", 0, "Forces bots to send the specified command." );
+
+ConVar bot_crouch( "bot_crouch", "0", 0, "Bot crouches" );
+
+static int BotNumber = 1;
+static int g_iNextBotTeam = -1;
+static int g_iNextBotClass = -1;
+
+typedef struct
+{
+ bool backwards;
+
+ float nextturntime;
+ bool lastturntoright;
+
+ float nextstrafetime;
+ float sidemove;
+
+ QAngle forwardAngle;
+ QAngle lastAngles;
+
+ float m_flJoinTeamTime;
+ int m_WantedTeam;
+ int m_WantedClass;
+} botdata_t;
+
+static botdata_t g_BotData[ MAX_PLAYERS ];
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Create a new Bot and put it in the game.
+// Output : Pointer to the new Bot, or NULL if there's no free clients.
+//-----------------------------------------------------------------------------
+CBasePlayer *BotPutInServer( bool bFrozen, int iTeam, int iClass )
+{
+ g_iNextBotTeam = iTeam;
+ g_iNextBotClass = iClass;
+
+ char botname[ 64 ];
+ Q_snprintf( botname, sizeof( botname ), "Bot%02i", BotNumber );
+
+ // This is an evil hack, but we use it to prevent sv_autojointeam from kicking in.
+ edict_t *pEdict = engine->CreateFakeClient( botname );
+
+ if (!pEdict)
+ {
+ Msg( "Failed to create Bot.\n");
+ return NULL;
+ }
+
+ // Allocate a CBasePlayer for the bot, and call spawn
+ //ClientPutInServer( pEdict, botname );
+ CDODPlayer *pPlayer = ((CDODPlayer *)CBaseEntity::Instance( pEdict ));
+ pPlayer->ClearFlags();
+ pPlayer->AddFlag( FL_CLIENT | FL_FAKECLIENT );
+
+ if ( bFrozen )
+ pPlayer->AddEFlags( EFL_BOT_FROZEN );
+
+ BotNumber++;
+
+ g_BotData[pPlayer->entindex()-1].m_WantedTeam = iTeam;
+ g_BotData[pPlayer->entindex()-1].m_WantedClass = iClass;
+ g_BotData[pPlayer->entindex()-1].m_flJoinTeamTime = gpGlobals->curtime + 0.3;
+
+ return pPlayer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Run through all the Bots in the game and let them think.
+//-----------------------------------------------------------------------------
+void Bot_RunAll( void )
+{
+ for ( int i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) );
+
+ if ( pPlayer && (pPlayer->GetFlags() & FL_FAKECLIENT) )
+ {
+ Bot_Think( pPlayer );
+ }
+ }
+}
+
+bool RunMimicCommand( CUserCmd& cmd )
+{
+ if ( bot_mimic.GetInt() <= 0 )
+ return false;
+
+ if ( bot_mimic.GetInt() > gpGlobals->maxClients )
+ return false;
+
+
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( bot_mimic.GetInt() );
+ if ( !pPlayer )
+ return false;
+
+ if ( !pPlayer->GetLastUserCommand() )
+ return false;
+
+ cmd = *pPlayer->GetLastUserCommand();
+ cmd.viewangles[YAW] += bot_mimic_yaw_offset.GetFloat();
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Simulates a single frame of movement for a player
+// Input : *fakeclient -
+// *viewangles -
+// forwardmove -
+// sidemove -
+// upmove -
+// buttons -
+// impulse -
+// msec -
+// Output : virtual void
+//-----------------------------------------------------------------------------
+static void RunPlayerMove( CDODPlayer *fakeclient, const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
+{
+ if ( !fakeclient )
+ return;
+
+ CUserCmd cmd;
+
+ // Store off the globals.. they're gonna get whacked
+ float flOldFrametime = gpGlobals->frametime;
+ float flOldCurtime = gpGlobals->curtime;
+
+ float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
+ fakeclient->SetTimeBase( flTimeBase );
+
+ Q_memset( &cmd, 0, sizeof( cmd ) );
+
+ if ( !RunMimicCommand( cmd ) && !bot_zombie.GetBool() )
+ {
+ VectorCopy( viewangles, cmd.viewangles );
+ cmd.forwardmove = forwardmove;
+ cmd.sidemove = sidemove;
+ cmd.upmove = upmove;
+ cmd.buttons = buttons;
+ cmd.impulse = impulse;
+ cmd.random_seed = random->RandomInt( 0, 0x7fffffff );
+ }
+
+ if( bot_crouch.GetInt() )
+ cmd.buttons |= IN_DUCK;
+
+ if ( bot_attack.GetBool() )
+ cmd.buttons |= IN_ATTACK;
+
+ MoveHelperServer()->SetHost( fakeclient );
+ fakeclient->PlayerRunCommand( &cmd, MoveHelperServer() );
+
+ // save off the last good usercmd
+ fakeclient->SetLastUserCommand( cmd );
+
+ // Clear out any fixangle that has been set
+ fakeclient->pl.fixangle = FIXANGLE_NONE;
+
+ // Restore the globals..
+ gpGlobals->frametime = flOldFrametime;
+ gpGlobals->curtime = flOldCurtime;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Run this Bot's AI for one frame.
+//-----------------------------------------------------------------------------
+void Bot_Think( CDODPlayer *pBot )
+{
+ // Make sure we stay being a bot
+ pBot->AddFlag( FL_FAKECLIENT );
+
+ botdata_t *botdata = &g_BotData[ ENTINDEX( pBot->edict() ) - 1 ];
+
+ QAngle vecViewAngles;
+ float forwardmove = 0.0;
+ float sidemove = botdata->sidemove;
+ float upmove = 0.0;
+ unsigned short buttons = 0;
+ byte impulse = 0;
+ float frametime = gpGlobals->frametime;
+
+ vecViewAngles = pBot->GetLocalAngles();
+
+
+ // Create some random values
+ if ( pBot->GetTeamNumber() == TEAM_UNASSIGNED && gpGlobals->curtime > botdata->m_flJoinTeamTime )
+ {
+ pBot->HandleCommand_JoinTeam( botdata->m_WantedTeam );
+ }
+ else if ( pBot->GetTeamNumber() != TEAM_UNASSIGNED && pBot->m_Shared.PlayerClass() == PLAYERCLASS_UNDEFINED )
+ {
+ // If they're on a team but haven't picked a class, choose a random class..
+ pBot->HandleCommand_JoinClass( botdata->m_WantedClass );
+ pBot->DODRespawn();
+ }
+ else if ( pBot->IsAlive() && (pBot->GetSolid() == SOLID_BBOX) )
+ {
+ trace_t trace;
+
+ // Stop when shot
+ if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) )
+ {
+ if ( pBot->m_iHealth == 100 )
+ {
+ forwardmove = 600 * ( botdata->backwards ? -1 : 1 );
+ if ( botdata->sidemove != 0.0f )
+ {
+ forwardmove *= random->RandomFloat( 0.1, 1.0f );
+ }
+ }
+ else
+ {
+ forwardmove = 0;
+ }
+ }
+
+ // Only turn if I haven't been hurt
+ if ( !pBot->IsEFlagSet(EFL_BOT_FROZEN) && pBot->m_iHealth == 100 )
+ {
+ Vector vecEnd;
+ Vector forward;
+
+ QAngle angle;
+ float angledelta = 15.0;
+
+ int maxtries = (int)360.0/angledelta;
+
+ if ( botdata->lastturntoright )
+ {
+ angledelta = -angledelta;
+ }
+
+ angle = pBot->GetLocalAngles();
+
+ Vector vecSrc;
+ while ( --maxtries >= 0 )
+ {
+ AngleVectors( angle, &forward );
+
+ vecSrc = pBot->GetLocalOrigin() + Vector( 0, 0, 36 );
+
+ vecEnd = vecSrc + forward * 10;
+
+ UTIL_TraceHull( vecSrc, vecEnd, VEC_HULL_MIN_SCALED( pBot ), VEC_HULL_MAX_SCALED( pBot ),
+ MASK_PLAYERSOLID, pBot, COLLISION_GROUP_NONE, &trace );
+
+ if ( trace.fraction == 1.0 )
+ {
+ if ( gpGlobals->curtime < botdata->nextturntime )
+ {
+ break;
+ }
+ }
+
+ angle.y += angledelta;
+
+ if ( angle.y > 180 )
+ angle.y -= 360;
+ else if ( angle.y < -180 )
+ angle.y += 360;
+
+ botdata->nextturntime = gpGlobals->curtime + 2.0;
+ botdata->lastturntoright = random->RandomInt( 0, 1 ) == 0 ? true : false;
+
+ botdata->forwardAngle = angle;
+ botdata->lastAngles = angle;
+
+ }
+
+
+ if ( gpGlobals->curtime >= botdata->nextstrafetime )
+ {
+ botdata->nextstrafetime = gpGlobals->curtime + 1.0f;
+
+ if ( random->RandomInt( 0, 5 ) == 0 )
+ {
+ botdata->sidemove = -600.0f + 1200.0f * random->RandomFloat( 0, 2 );
+ }
+ else
+ {
+ botdata->sidemove = 0;
+ }
+ sidemove = botdata->sidemove;
+
+ if ( random->RandomInt( 0, 20 ) == 0 )
+ {
+ botdata->backwards = true;
+ }
+ else
+ {
+ botdata->backwards = false;
+ }
+ }
+
+ pBot->SetLocalAngles( angle );
+ vecViewAngles = angle;
+ }
+
+ // Is my team being forced to defend?
+ if ( bot_defend.GetInt() == pBot->GetTeamNumber() )
+ {
+ buttons |= IN_ATTACK2;
+ }
+ // If bots are being forced to fire a weapon, see if I have it
+ else if ( bot_forcefireweapon.GetString() )
+ {
+ CBaseCombatWeapon *pWeapon = pBot->Weapon_OwnsThisType( bot_forcefireweapon.GetString() );
+ if ( pWeapon )
+ {
+ // Switch to it if we don't have it out
+ CBaseCombatWeapon *pActiveWeapon = pBot->GetActiveWeapon();
+
+ // Switch?
+ if ( pActiveWeapon != pWeapon )
+ {
+ pBot->Weapon_Switch( pWeapon );
+ }
+ else
+ {
+ // Start firing
+ // Some weapons require releases, so randomise firing
+ if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
+ {
+ buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
+ }
+ }
+ }
+ }
+
+ if ( bot_flipout.GetInt() )
+ {
+ if ( bot_forceattackon.GetBool() || (RandomFloat(0.0,1.0) > 0.5) )
+ {
+ buttons |= bot_forceattack2.GetBool() ? IN_ATTACK2 : IN_ATTACK;
+ }
+
+ if ( RandomFloat(0.0,1.0) > 0.9 )
+ buttons |= IN_RELOAD;
+ }
+
+ if ( Q_strlen( bot_sendcmd.GetString() ) > 0 )
+ {
+ //send the cmd from this bot
+ CCommand args;
+ args.Tokenize( bot_sendcmd.GetString() );
+ pBot->ClientCommand( args );
+
+ bot_sendcmd.SetValue("");
+ }
+ }
+ else
+ {
+ // Wait for Reinforcement wave
+ if ( !pBot->IsAlive() )
+ {
+ // Try hitting my buttons occasionally
+ if ( random->RandomInt( 0, 100 ) > 80 )
+ {
+ // Respawn the bot
+ if ( random->RandomInt( 0, 1 ) == 0 )
+ {
+ buttons |= IN_JUMP;
+ }
+ else
+ {
+ buttons = 0;
+ }
+ }
+ }
+ }
+
+ if ( bot_flipout.GetInt() >= 2 )
+ {
+ botdata->lastAngles.x = sin( gpGlobals->curtime + pBot->entindex() ) * 90;
+ botdata->lastAngles.y = AngleNormalize( ( gpGlobals->curtime * 1.7 + pBot->entindex() ) * 45 );
+ botdata->lastAngles.z = 0.0;
+
+ // botdata->lastAngles = QAngle( 0, 0, 0 );
+
+ /*
+ QAngle angOffset = RandomAngle( -1, 1 );
+ for ( int i = 0 ; i < 2; i++ )
+ {
+ if ( fabs( botdata->lastAngles[ i ] - botdata->forwardAngle[ i ] ) > 15.0f )
+ {
+ if ( botdata->lastAngles[ i ] > botdata->forwardAngle[ i ] )
+ {
+ botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] + 15;
+ }
+ else
+ {
+ botdata->lastAngles[ i ] = botdata->forwardAngle[ i ] - 15;
+ }
+ }
+ }
+ botdata->lastAngles[ 2 ] = 0;
+ */
+
+ float speed = 300; // sin( gpGlobals->curtime / 1.7 + pBot->entindex() ) * 600;
+ forwardmove = sin( gpGlobals->curtime + pBot->entindex() ) * speed;
+ sidemove = cos( gpGlobals->curtime * 2.3 + pBot->entindex() ) * speed;
+
+ if (sin(gpGlobals->curtime ) < -0.5)
+ {
+ buttons |= IN_DUCK;
+ }
+ else if (sin(gpGlobals->curtime ) < 0.5)
+ {
+ buttons |= IN_WALK;
+ }
+
+ pBot->SetLocalAngles( botdata->lastAngles );
+ }
+
+ RunPlayerMove( pBot, pBot->GetLocalAngles(), forwardmove, sidemove, upmove, buttons, impulse, frametime );
+}
+
+
diff --git a/game/server/dod/dod_bot_temp.h b/game/server/dod/dod_bot_temp.h
new file mode 100644
index 0000000..2036f3d
--- /dev/null
+++ b/game/server/dod/dod_bot_temp.h
@@ -0,0 +1,19 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef BOT_BASE_H
+#define BOT_BASE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+// If iTeam or iClass is -1, then a team or class is randomly chosen.
+CBasePlayer *BotPutInServer( bool bFrozen, int iTeam, int iClass );
+
+
+#endif // BOT_BASE_H
diff --git a/game/server/dod/dod_client.cpp b/game/server/dod/dod_client.cpp
new file mode 100644
index 0000000..2d6160c
--- /dev/null
+++ b/game/server/dod/dod_client.cpp
@@ -0,0 +1,236 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+/*
+
+===== tf_client.cpp ========================================================
+
+ HL2 client/server game specific stuff
+
+*/
+
+#include "cbase.h"
+#include "player.h"
+#include "gamerules.h"
+#include "entitylist.h"
+#include "physics.h"
+#include "game.h"
+#include "ai_network.h"
+#include "ai_node.h"
+#include "ai_hull.h"
+#include "shake.h"
+#include "player_resource.h"
+#include "engine/IEngineSound.h"
+#include "dod_player.h"
+#include "dod_gamerules.h"
+#include "tier0/vprof.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+extern CBaseEntity *FindPickerEntity( CBasePlayer *pPlayer );
+
+extern bool g_fGameOver;
+
+void FinishClientPutInServer( CDODPlayer *pPlayer )
+{
+ pPlayer->InitialSpawn();
+ pPlayer->Spawn();
+
+ if (!pPlayer->IsBot())
+ {
+ // When the player first joins the server, they
+ pPlayer->m_takedamage = DAMAGE_NO;
+ pPlayer->pl.deadflag = true;
+ pPlayer->m_lifeState = LIFE_DEAD;
+ pPlayer->AddEffects( EF_NODRAW );
+ pPlayer->SetThink( NULL );
+
+ if ( 1 )
+ {
+ pPlayer->ChangeTeam( TEAM_UNASSIGNED );
+
+ // Move them to the first intro camera.
+ pPlayer->MoveToNextIntroCamera();
+ pPlayer->SetMoveType( MOVETYPE_NONE );
+ }
+ }
+
+
+ char sName[128];
+ Q_strncpy( sName, pPlayer->GetPlayerName(), sizeof( sName ) );
+
+ // First parse the name and remove any %'s
+ for ( char *pApersand = sName; pApersand != NULL && *pApersand != 0; pApersand++ )
+ {
+ // Replace it with a space
+ if ( *pApersand == '%' )
+ *pApersand = ' ';
+ }
+
+ // notify other clients of player joining the game
+ UTIL_ClientPrintAll( HUD_PRINTNOTIFY, "#Game_connected", sName[0] != 0 ? sName : "<unconnected>" );
+}
+
+/*
+===========
+ClientPutInServer
+
+called each time a player is spawned into the game
+============
+*/
+void ClientPutInServer( edict_t *pEdict, const char *playername )
+{
+ // Allocate a CBaseTFPlayer for pev, and call spawn
+ CDODPlayer *pPlayer = CDODPlayer::CreatePlayer( "player", pEdict );
+
+ pPlayer->SetPlayerName( playername );
+}
+
+
+void ClientActive( edict_t *pEdict, bool bLoadGame )
+{
+ // Can't load games in CS!
+ Assert( !bLoadGame );
+
+ CDODPlayer *pPlayer = ToDODPlayer( CBaseEntity::Instance( pEdict ) );
+ FinishClientPutInServer( pPlayer );
+}
+
+
+/*
+===============
+const char *GetGameDescription()
+
+Returns the descriptive name of this .dll. E.g., Half-Life, or Team Fortress 2
+===============
+*/
+const char *GetGameDescription()
+{
+ if ( g_pGameRules ) // this function may be called before the world has spawned, and the game rules initialized
+ return g_pGameRules->GetGameDescription();
+ else
+ return "Day of Defeat";
+}
+
+int g_iHelmetModels[NUM_HELMETS];
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache game-specific models & sounds
+//-----------------------------------------------------------------------------
+void ClientGamePrecache( void )
+{
+ // Materials used by the client effects
+ CBaseEntity::PrecacheModel( "sprites/white.vmt" );
+ CBaseEntity::PrecacheModel( "sprites/physbeam.vmt" );
+
+ for( int i=0;i<NUM_HELMETS;i++ )
+ {
+ g_iHelmetModels[i] = CBaseEntity::PrecacheModel( m_pszHelmetModels[i] );
+ }
+
+// Moved to pure_server_minimal.txt
+// // Sniper scope
+// engine->ForceExactFile( "sprites/scopes/scope_spring_ul.vmt" );
+// engine->ForceExactFile( "sprites/scopes/scope_spring_ul.vtf" );
+// engine->ForceExactFile( "sprites/scopes/scope_spring_ur.vmt" );
+// engine->ForceExactFile( "sprites/scopes/scope_spring_ur.vtf" );
+// engine->ForceExactFile( "sprites/scopes/scope_spring_ll.vmt" );
+// engine->ForceExactFile( "sprites/scopes/scope_spring_ll.vtf" );
+// engine->ForceExactFile( "sprites/scopes/scope_spring_lr.vmt" );
+// engine->ForceExactFile( "sprites/scopes/scope_spring_lr.vtf" );
+//
+// engine->ForceExactFile( "sprites/scopes/scope_k43_ul.vmt" );
+// engine->ForceExactFile( "sprites/scopes/scope_k43_ul.vtf" );
+// engine->ForceExactFile( "sprites/scopes/scope_k43_ur.vmt" );
+// engine->ForceExactFile( "sprites/scopes/scope_k43_ur.vtf" );
+// engine->ForceExactFile( "sprites/scopes/scope_k43_ll.vmt" );
+// engine->ForceExactFile( "sprites/scopes/scope_k43_ll.vtf" );
+// engine->ForceExactFile( "sprites/scopes/scope_k43_lr.vmt" );
+// engine->ForceExactFile( "sprites/scopes/scope_k43_lr.vtf" );
+//
+// // Smoke grenade-related files
+// engine->ForceExactFile( "particle/particle_smokegrenade1.vmt" );
+// engine->ForceExactFile( "particle/particle_smokegrenade.vtf" );
+// engine->ForceExactFile( "effects/stun.vmt" );
+//
+// // DSP presets - don't want people avoiding the deafening + ear ring
+// engine->ForceExactFile( "scripts/dsp_presets.txt" );
+//
+// // force weapon scripts because people are dirty cheaters
+// engine->ForceExactFile( "scripts/weapon_30cal.ctx");
+// engine->ForceExactFile( "scripts/weapon_amerknife.ctx");
+// engine->ForceExactFile( "scripts/weapon_bar.ctx");
+// engine->ForceExactFile( "scripts/weapon_bazooka.ctx");
+// engine->ForceExactFile( "scripts/weapon_c96.ctx");
+// engine->ForceExactFile( "scripts/weapon_colt.ctx");
+// engine->ForceExactFile( "scripts/weapon_frag_ger.ctx");
+// engine->ForceExactFile( "scripts/weapon_frag_ger_live.ctx");
+// engine->ForceExactFile( "scripts/weapon_frag_us.ctx");
+// engine->ForceExactFile( "scripts/weapon_frag_us_live.ctx");
+// engine->ForceExactFile( "scripts/weapon_garand.ctx");
+// engine->ForceExactFile( "scripts/weapon_k98.ctx");
+// engine->ForceExactFile( "scripts/weapon_k98_scoped.ctx");
+// engine->ForceExactFile( "scripts/weapon_m1carbine.ctx");
+// engine->ForceExactFile( "scripts/weapon_mg42.ctx");
+// engine->ForceExactFile( "scripts/weapon_mp40.ctx");
+// engine->ForceExactFile( "scripts/weapon_mp44.ctx");
+// engine->ForceExactFile( "scripts/weapon_p38.ctx");
+// engine->ForceExactFile( "scripts/weapon_pschreck.ctx");
+// engine->ForceExactFile( "scripts/weapon_riflegren_ger.ctx");
+// engine->ForceExactFile( "scripts/weapon_riflegren_ger_live.ctx");
+// engine->ForceExactFile( "scripts/weapon_riflegren_us.ctx");
+// engine->ForceExactFile( "scripts/weapon_riflegren_us_live.ctx");
+// engine->ForceExactFile( "scripts/weapon_smoke_ger.ctx");
+// engine->ForceExactFile( "scripts/weapon_smoke_us.ctx");
+// engine->ForceExactFile( "scripts/weapon_spade.ctx");
+// engine->ForceExactFile( "scripts/weapon_spring.ctx");
+// engine->ForceExactFile( "scripts/weapon_thompson.ctx");
+}
+
+
+// called by ClientKill and DeadThink
+void respawn( CBaseEntity *pEdict, bool fCopyCorpse )
+{
+ if (gpGlobals->coop || gpGlobals->deathmatch)
+ {
+ if ( fCopyCorpse )
+ {
+ // make a copy of the dead body for appearances sake
+ dynamic_cast< CBasePlayer* >( pEdict )->CreateCorpse();
+ }
+
+ // respawn player
+ pEdict->Spawn();
+ }
+ else
+ { // restart the entire server
+ engine->ServerCommand("reload\n");
+ }
+}
+
+void GameStartFrame( void )
+{
+ VPROF( "GameStartFrame" );
+
+ if ( g_fGameOver )
+ return;
+
+ gpGlobals->teamplay = teamplay.GetInt() ? true : false;
+
+ extern void Bot_RunAll();
+ Bot_RunAll();
+}
+
+//=========================================================
+// instantiate the proper game rules object
+//=========================================================
+void InstallGameRules()
+{
+ CreateGameRulesObject( "CDODGameRules" );
+}
diff --git a/game/server/dod/dod_control_point.cpp b/game/server/dod/dod_control_point.cpp
new file mode 100644
index 0000000..5b74f8e
--- /dev/null
+++ b/game/server/dod/dod_control_point.cpp
@@ -0,0 +1,738 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_shareddefs.h"
+#include "dod_control_point.h"
+#include "dod_player.h"
+#include "dod_gamerules.h"
+#include "dod_team.h"
+#include "dod_objective_resource.h"
+#include "dod_control_point_master.h"
+
+LINK_ENTITY_TO_CLASS( dod_control_point, CControlPoint );
+
+#define OBJ_ICON_NEUTRAL_FLAG 0
+#define OBJ_ICON_ALLIES_FLAG 1
+#define OBJ_ICON_AXIS_FLAG 2
+
+BEGIN_DATADESC(CControlPoint)
+
+ DEFINE_KEYFIELD( m_iszPrintName, FIELD_STRING, "point_printname" ),
+
+ DEFINE_KEYFIELD( m_iszAlliesCapSound, FIELD_STRING, "point_allies_capsound" ),
+ DEFINE_KEYFIELD( m_iszAxisCapSound, FIELD_STRING, "point_axis_capsound" ),
+ DEFINE_KEYFIELD( m_iszResetSound, FIELD_STRING, "point_resetsound" ),
+
+ DEFINE_KEYFIELD( m_iszAlliesModel, FIELD_STRING, "point_allies_model" ),
+ DEFINE_KEYFIELD( m_iszAxisModel, FIELD_STRING, "point_axis_model" ),
+ DEFINE_KEYFIELD( m_iszResetModel, FIELD_STRING, "point_reset_model" ),
+
+ DEFINE_KEYFIELD( m_iCPGroup, FIELD_INTEGER, "point_group" ),
+
+ DEFINE_KEYFIELD( m_iTimedPointsAllies, FIELD_INTEGER, "point_timedpoints_allies" ),
+ DEFINE_KEYFIELD( m_iTimedPointsAxis, FIELD_INTEGER, "point_timedpoints_axis" ),
+
+ DEFINE_KEYFIELD( m_iBombsRequired, FIELD_INTEGER, "point_num_bombs" ),
+
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "SetOwner", InputSetOwner ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "ShowModel", InputShowModel ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "HideModel", InputHideModel ),
+
+ DEFINE_OUTPUT( m_AlliesCapOutput, "OnAlliesCap" ), // these are fired whenever the point changes modes
+ DEFINE_OUTPUT( m_AxisCapOutput, "OnAxisCap" ),
+ DEFINE_OUTPUT( m_PointResetOutput, "OnCapReset" ),
+
+ DEFINE_OUTPUT( m_OwnerChangedToAllies, "AlliesCapturePoint" ), // these are fired when a team does the work to change the owner
+ DEFINE_OUTPUT( m_OwnerChangedToAxis, "AxisCapturePoint" ),
+
+END_DATADESC();
+
+
+CControlPoint::CControlPoint( )
+{
+ m_iPointIndex = -1;
+ m_bPointVisible = false;
+
+ m_iAlliesIcon = 0;
+ m_iAxisIcon = 0;
+ m_iNeutralIcon = 0;
+ m_iNeutralIcon = 0;
+
+ //default group is 0 for backwards compatibility
+ m_iAlliesModelBodygroup = 0;
+ m_iAxisModelBodygroup = 0;
+ m_iResetModelBodygroup = 0;
+
+ m_bBombPlanted = false;
+}
+
+void CControlPoint::Spawn( void )
+{
+ Precache();
+
+ SetTouch( NULL );
+
+ InternalSetOwner( m_iDefaultOwner, false ); //init the owner of this point
+
+ SetActive( !m_bStartDisabled );
+
+ BaseClass::Spawn();
+
+ UseClientSideAnimation();
+
+ SetPlaybackRate( 1.0 );
+
+ SetCycle( random->RandomFloat( 0, 1.0 ) );
+
+ m_iAlliesRequired = 0;
+ m_iAxisRequired = 0;
+
+ if ( FBitSet( m_spawnflags, CAP_POINT_HIDE_MODEL ) )
+ {
+ AddEffects( EF_NODRAW );
+ }
+
+ m_iBombsRemaining = m_iBombsRequired;
+}
+
+void CControlPoint::SetNumCappersRequired( int alliesRequired, int axisRequired )
+{
+ m_iAlliesRequired = alliesRequired;
+ m_iAxisRequired = axisRequired;
+}
+
+//================================
+// InputReset
+// Used by ControlMaster to this point to its default owner
+//================================
+void CControlPoint::InputReset( inputdata_t &input )
+{
+ InternalSetOwner( m_iDefaultOwner, false );
+ g_pObjectiveResource->SetOwningTeam( GetPointIndex(), m_iTeam );
+}
+
+//================================
+// InputReset
+// Used by Area caps to set the owner
+//================================
+void CControlPoint::InputSetOwner( inputdata_t &input )
+{
+ int team = input.value.Int();
+
+ Assert( team == TEAM_UNASSIGNED || team == TEAM_ALLIES || team == TEAM_AXIS );
+
+ Assert( input.pCaller );
+ Assert( input.pCaller->IsPlayer() );
+
+ CDODPlayer *pPlayer = ToDODPlayer( input.pCaller );
+
+ int iCappingPlayer = pPlayer->entindex();
+
+ // Only allow cp caps when round is running ( but not when in warmup )
+ if( DODGameRules()->State_Get() == STATE_RND_RUNNING &&
+ !DODGameRules()->IsInWarmup() )
+ {
+ InternalSetOwner( team, true, 1, &iCappingPlayer );
+ g_pObjectiveResource->SetOwningTeam( GetPointIndex(), m_iTeam );
+ }
+}
+
+void CControlPoint::SetOwner( int owner, bool bMakeSound, int iNumCappers, int *pCappingPlayers )
+{
+ // Only allow cp caps when round is running ( but not when in warmup )
+ if( DODGameRules()->State_Get() == STATE_RND_RUNNING &&
+ !DODGameRules()->IsInWarmup() )
+ {
+ InternalSetOwner( owner, bMakeSound, iNumCappers, pCappingPlayers );
+ g_pObjectiveResource->SetOwningTeam( GetPointIndex(), m_iTeam );
+ }
+}
+
+void CControlPoint::InputShowModel( inputdata_t &input )
+{
+ RemoveEffects( EF_NODRAW );
+}
+
+void CControlPoint::InputHideModel( inputdata_t &input )
+{
+ AddEffects( EF_NODRAW );
+}
+
+int CControlPoint::GetCurrentHudIconIndex( void )
+{
+ return GetHudIconIndexForTeam( GetOwner() );
+}
+
+int CControlPoint::GetHudIconIndexForTeam( int team )
+{
+ int icon = m_iNeutralIcon;
+
+ switch( team )
+ {
+ case TEAM_ALLIES:
+ icon = m_iAlliesIcon;
+ break;
+ case TEAM_AXIS:
+ icon = m_iAxisIcon;
+ break;
+ case TEAM_UNASSIGNED:
+ icon = m_iNeutralIcon;
+ break;
+ default:
+ Assert( !"Bad team in GetHudIconIndexForTeam()" );
+ break;
+ }
+
+ return icon;
+}
+
+int CControlPoint::GetTimerCapHudIcon( void )
+{
+ return m_iTimerCapIcon;
+}
+
+int CControlPoint::GetBombedHudIcon( void )
+{
+ return m_iBombedIcon;
+}
+
+// SetOwner
+// ========
+// Sets the new owner of the point
+// plays the appropriate sound
+// and shows the right model
+
+void CControlPoint::InternalSetOwner( int owner, bool bMakeSound, int iNumCappers, int *pCappingPlayers )
+{
+ m_iTeam = owner;
+
+ switch ( m_iTeam )
+ {
+ case TEAM_UNASSIGNED:
+ {
+ if( bMakeSound )
+ {
+ CBroadcastRecipientFilter filter;
+ EmitSound( filter, entindex(), STRING(m_iszResetSound) );
+ }
+
+ SetModel( STRING(m_iszResetModel) );
+ SetBodygroup( 0, m_iResetModelBodygroup );
+
+ m_PointResetOutput.FireOutput( this, this );
+ }
+ break;
+ case TEAM_ALLIES:
+ {
+ if( bMakeSound )
+ {
+ CBroadcastRecipientFilter filter;
+ EmitSound( filter, entindex(), STRING(m_iszAlliesCapSound) );
+ }
+
+ SetModel( STRING(m_iszAlliesModel) );
+ SetBodygroup( 0, m_iAlliesModelBodygroup );
+
+ m_AlliesCapOutput.FireOutput( this, this );
+ }
+ break;
+ case TEAM_AXIS:
+ {
+ if( bMakeSound )
+ {
+ CBroadcastRecipientFilter filter;
+ EmitSound( filter, entindex(), STRING(m_iszAxisCapSound) );
+ }
+
+ SetModel( STRING(m_iszAxisModel) );
+ SetBodygroup( 0, m_iAxisModelBodygroup );
+
+ m_AxisCapOutput.FireOutput( this, this );
+ }
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+
+ if( bMakeSound ) //make sure this is a real cap and not a reset
+ {
+ for( int i=0;i<iNumCappers;i++ )
+ {
+ int playerIndex = pCappingPlayers[i];
+
+ Assert( playerIndex > 0 && playerIndex <= gpGlobals->maxClients );
+
+ CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( playerIndex ) );
+
+ Assert( pPlayer );
+
+ if ( pPlayer )
+ {
+ pPlayer->AddScore( PLAYER_POINTS_FOR_CAP );
+ pPlayer->Stats_AreaCaptured();
+ DODGameRules()->Stats_PlayerCap( pPlayer->GetTeamNumber(), pPlayer->m_Shared.DesiredPlayerClass() );
+
+ // count bomb plants as point capture
+ //if ( !m_bSetOwnerIsBombPlant )
+ {
+ pPlayer->StatEvent_PointCaptured();
+ }
+ }
+ }
+
+ if ( m_iTeam == TEAM_ALLIES )
+ {
+ m_OwnerChangedToAllies.FireOutput( this, this );
+ }
+ else if ( m_iTeam == TEAM_AXIS )
+ {
+ m_OwnerChangedToAxis.FireOutput( this, this );
+ }
+ }
+
+ if( bMakeSound && m_iTeam != TEAM_UNASSIGNED && iNumCappers > 0 ) //dont print message if its a reset
+ SendCapString( m_iTeam, iNumCappers, pCappingPlayers );
+
+ // have control point master check the win conditions now!
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_control_point_master" );
+
+ while( pEnt )
+ {
+ CControlPointMaster *pMaster = dynamic_cast<CControlPointMaster *>( pEnt );
+
+ if ( pMaster->IsActive() )
+ {
+ pMaster->CheckWinConditions();
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "dod_control_point_master" );
+ }
+
+ // Capping the last flag achievement
+ if ( ( DODGameRules()->State_Get() == STATE_ALLIES_WIN && m_iTeam == TEAM_ALLIES ) ||
+ ( DODGameRules()->State_Get() == STATE_AXIS_WIN && m_iTeam == TEAM_AXIS ) )
+ {
+ // limit to flag cap maps
+ if ( m_iBombsRequired == 0 )
+ {
+ for ( int i=0;i<iNumCappers;i++ )
+ {
+ CDODPlayer *pDODPlayer = ToDODPlayer( UTIL_PlayerByIndex( pCappingPlayers[i] ) );
+
+ if ( pDODPlayer )
+ {
+ pDODPlayer->AwardAchievement( ACHIEVEMENT_DOD_CAP_LAST_FLAG );
+ }
+ }
+ }
+ }
+}
+
+void CControlPoint::SendCapString( int team, int iNumCappers, int *pCappingPlayers )
+{
+ if( strlen( STRING(m_iszPrintName) ) <= 0 )
+ return;
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "dod_point_captured" );
+
+ if ( event )
+ {
+ event->SetInt( "cp", m_iPointIndex );
+ event->SetString( "cpname", STRING(m_iszPrintName) );
+
+ char cappers[9]; // pCappingPlayers is max length 8
+ int i;
+ for( i=0;i<iNumCappers;i++ )
+ {
+ cappers[i] = (char)pCappingPlayers[i];
+ }
+
+ cappers[i] = '\0';
+
+ // pCappingPlayers is a null terminated list of player indeces
+ event->SetString( "cappers", cappers );
+ event->SetInt( "priority", 9 );
+
+ event->SetBool( "bomb", m_bSetOwnerIsBombPlant );
+
+ gameeventmanager->FireEvent( event );
+ }
+}
+
+void CControlPoint::CaptureBlocked( CDODPlayer *pPlayer )
+{
+ if( strlen( STRING(m_iszPrintName) ) <= 0 )
+ return;
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_capture_blocked" );
+
+ if ( event )
+ {
+ event->SetInt( "cp", m_iPointIndex );
+ event->SetString( "cpname", STRING(m_iszPrintName) );
+ event->SetInt( "blocker", pPlayer->entindex() );
+ event->SetInt( "priority", 9 );
+ event->SetBool( "bomb", GetBombsRemaining() > 0 );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ pPlayer->AddScore( PLAYER_POINTS_FOR_BLOCK );
+
+ if ( GetBombsRemaining() <= 0 ) // no bomb blocks ( kills planter or defuser )
+ {
+ pPlayer->StatEvent_CaptureBlocked();
+ }
+
+ pPlayer->Stats_AreaDefended();
+ DODGameRules()->Stats_PlayerDefended( pPlayer->GetTeamNumber(), pPlayer->m_Shared.DesiredPlayerClass() );
+}
+
+// GetOwner
+// ========
+// returns the current owner
+// 2 for allies
+// 3 for axis
+// 0 if noone owns it
+
+int CControlPoint::GetOwner( void ) const
+{
+ return m_iTeam;
+}
+
+int CControlPoint::GetDefaultOwner( void ) const
+{
+ return m_iDefaultOwner;
+}
+
+int CControlPoint::GetCPGroup( void )
+{
+ return m_iCPGroup;
+}
+
+// PointValue
+// ==========
+// returns the time-based point value of this control point
+int CControlPoint::PointValue( void )
+{
+ // teams earn 0 points for a flag they start with
+ // after that its 1 additional point for each flag closer to the spawn.
+
+ int value = 0;
+
+ if ( FBitSet( m_spawnflags, CAP_POINT_TICK_FOR_BOMBS_REMAINING ) )
+ {
+ value = m_iBombsRemaining;
+ }
+ else if ( GetOwner() == m_iDefaultOwner )
+ {
+ value = 0;
+ }
+ else
+ {
+ if ( GetOwner() == TEAM_ALLIES )
+ {
+ value = m_iTimedPointsAllies;
+ }
+ else if ( GetOwner() == TEAM_AXIS )
+ {
+ value = m_iTimedPointsAxis;
+ }
+ }
+
+ return value;
+}
+
+//precache the necessary models and sounds
+void CControlPoint::Precache( void )
+{
+ if ( m_iszAlliesCapSound != NULL_STRING )
+ {
+ PrecacheScriptSound( STRING(m_iszAlliesCapSound) );
+ }
+ if ( m_iszAxisCapSound != NULL_STRING )
+ {
+ PrecacheScriptSound( STRING(m_iszAxisCapSound) );
+ }
+ if ( m_iszResetSound != NULL_STRING )
+ {
+ PrecacheScriptSound( STRING(m_iszResetSound) );
+ }
+
+ PrecacheModel( STRING( m_iszAlliesModel ) );
+ PrecacheModel( STRING( m_iszAxisModel ) );
+ PrecacheModel( STRING( m_iszResetModel ) );
+
+ PrecacheMaterial( STRING( m_iszAlliesIcon ) );
+ m_iAlliesIcon = GetMaterialIndex( STRING( m_iszAlliesIcon ) );
+ Assert( m_iAlliesIcon != 0 );
+
+ PrecacheMaterial( STRING( m_iszAxisIcon ) );
+ m_iAxisIcon = GetMaterialIndex( STRING( m_iszAxisIcon ) );
+ Assert( m_iAxisIcon != 0 );
+
+ PrecacheMaterial( STRING( m_iszNeutralIcon ) );
+ m_iNeutralIcon = GetMaterialIndex( STRING( m_iszNeutralIcon ) );
+ Assert( m_iNeutralIcon != 0 );
+
+ if ( strlen( STRING( m_iszTimerCapIcon ) ) > 0 )
+ {
+ PrecacheMaterial( STRING( m_iszTimerCapIcon ) );
+ m_iTimerCapIcon = GetMaterialIndex( STRING( m_iszTimerCapIcon ) );
+ }
+
+ if ( strlen( STRING( m_iszBombedIcon ) ) > 0 )
+ {
+ PrecacheMaterial( STRING( m_iszBombedIcon ) );
+ m_iBombedIcon = GetMaterialIndex( STRING( m_iszBombedIcon ) );
+ }
+
+ if( !m_iNeutralIcon || !m_iAxisIcon || !m_iAlliesIcon )
+ {
+ Warning( "Invalid hud icon material in control point ( point index %d )\n", GetPointIndex() );
+ }
+}
+
+// Keyvalue
+// ========
+// this function interfaces with the keyvalues set by the mapper
+//
+// Values:
+// point_name - the name of the point displayed in the capture string ( and ControlStatus command )
+// point_reset_time - time after which the point spontaneously resets - currently not used
+// point_default_owner - the owner of this point at the start and after resets
+
+// point_allies_capsound |
+// point_axis_capsound | the sounds that are made when the points are capped by each team
+// point_resetsound |
+//
+// point_allies_model |
+// point_axis_model | the models that the ent is set to after each team caps it
+// point_reset_model |
+
+//point_team_points - number of team points to give to the capper's team for capping
+
+bool CControlPoint::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if (FStrEq(szKeyName, "point_default_owner"))
+ {
+ m_iDefaultOwner = atoi(szValue);
+
+ Assert( m_iDefaultOwner == TEAM_ALLIES ||
+ m_iDefaultOwner == TEAM_AXIS ||
+ m_iDefaultOwner == TEAM_UNASSIGNED );
+
+ if( m_iDefaultOwner != TEAM_ALLIES &&
+ m_iDefaultOwner != TEAM_AXIS &&
+ m_iDefaultOwner != TEAM_UNASSIGNED )
+ {
+ Warning( "dod_control_point with bad point_default_owner - probably '1'\n" );
+ m_iDefaultOwner = TEAM_UNASSIGNED;
+ }
+ }
+ else if (FStrEq(szKeyName, "point_allies_model_bodygroup"))
+ {
+ m_iAlliesModelBodygroup = atoi(szValue);
+ }
+ else if (FStrEq(szKeyName, "point_axis_model_bodygroup"))
+ {
+ m_iAxisModelBodygroup = atoi(szValue);
+ }
+ else if (FStrEq(szKeyName, "point_reset_model_bodygroup"))
+ {
+ m_iResetModelBodygroup = atoi(szValue);
+ }
+ else if (FStrEq(szKeyName, "point_index"))
+ {
+ m_iPointIndex = atoi(szValue);
+ }
+ else if (FStrEq(szKeyName, "point_hud_icon_allies"))
+ {
+ m_iszAlliesIcon = AllocPooledString(szValue);
+ }
+ else if (FStrEq(szKeyName, "point_hud_icon_axis"))
+ {
+ m_iszAxisIcon = AllocPooledString(szValue);
+ }
+ else if (FStrEq(szKeyName, "point_hud_icon_neutral"))
+ {
+ m_iszNeutralIcon = AllocPooledString(szValue);
+ }
+ else if (FStrEq(szKeyName, "point_hud_icon_timercap"))
+ {
+ m_iszTimerCapIcon = AllocPooledString(szValue);
+ }
+ else if (FStrEq(szKeyName, "point_hud_icon_bombed"))
+ {
+ m_iszBombedIcon = AllocPooledString(szValue);
+ }
+ else
+ return CBaseEntity::KeyValue( szKeyName, szValue );
+
+ return true;
+}
+
+void CControlPoint::SetActive( bool active )
+{
+ m_bActive = active;
+
+ if( active )
+ {
+ RemoveEffects( EF_NODRAW );
+ }
+ else
+ {
+ AddEffects( EF_NODRAW );
+ }
+}
+
+void CControlPoint::BombPlanted( float flTimerLength, CDODPlayer *pPlantingPlayer )
+{
+ if ( m_bBombPlanted == true )
+ {
+ Warning( "ERROR - dod_control_point only supports one bomb being planted at once\n" );
+ return;
+ }
+
+ //send it to everyone
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_bomb_planted" );
+ if ( event )
+ {
+ int iOppositeTeam = ( pPlantingPlayer->GetTeamNumber() == TEAM_ALLIES ) ? TEAM_AXIS : TEAM_ALLIES;
+
+ event->SetInt( "team", iOppositeTeam );
+
+ event->SetInt( "cp", m_iPointIndex );
+ event->SetString( "cpname", STRING(m_iszPrintName) );
+ event->SetInt( "userid", pPlantingPlayer->GetUserID() );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ m_bBombPlanted = true;
+ m_flBombExplodeTime = gpGlobals->curtime + flTimerLength;
+
+ // give the planter a point
+ pPlantingPlayer->AddScore( PLAYER_POINTS_FOR_BOMB_PLANT );
+
+ pPlantingPlayer->StatEvent_BombPlanted();
+
+ // set hud display
+ // this triggers the countdown ( using a shared, constant bomb timer )
+ g_pObjectiveResource->SetBombPlanted( GetPointIndex(), true );
+}
+
+void CControlPoint::BombExploded( CDODPlayer *pPlantingPlayer /* = NULL */, int iPlantingTeam /* = TEAM_UNASSIGNED */ )
+{
+ m_bBombPlanted = false;
+
+ m_iBombsRemaining -= 1;
+ g_pObjectiveResource->SetBombsRemaining( GetPointIndex(), m_iBombsRemaining );
+
+ if ( pPlantingPlayer )
+ {
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_bomb_exploded" );
+ if ( event )
+ {
+ event->SetInt( "cp", m_iPointIndex );
+ event->SetString( "cpname", STRING(m_iszPrintName) );
+ event->SetInt( "userid", pPlantingPlayer->GetUserID() );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ pPlantingPlayer->Stats_BombDetonated();
+
+ int iCappingPlayer = pPlantingPlayer->entindex();
+
+ DODGameRules()->CapEvent( CAP_EVENT_BOMB, iPlantingTeam );
+
+ if ( m_iBombsRemaining <= 0 )
+ {
+ m_bSetOwnerIsBombPlant = true;
+
+ InternalSetOwner( iPlantingTeam, true, 1, &iCappingPlayer );
+ g_pObjectiveResource->SetOwningTeam( GetPointIndex(), GetOwner() );
+
+ m_bSetOwnerIsBombPlant = false;
+
+ // top up points so it equals PLAYER_POINTS_FOR_BOMB_EXPLODED
+ pPlantingPlayer->AddScore( PLAYER_POINTS_FOR_BOMB_EXPLODED - PLAYER_POINTS_FOR_CAP );
+ }
+ else
+ {
+ // give that bomber a point!
+ pPlantingPlayer->AddScore( PLAYER_POINTS_FOR_BOMB_EXPLODED );
+
+ pPlantingPlayer->Stats_AreaCaptured();
+
+ pPlantingPlayer->StatEvent_PointCaptured();
+
+ // send something to death notices to show that something was accomplished
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "dod_point_captured" );
+
+ if ( event )
+ {
+ event->SetInt( "cp", m_iPointIndex );
+ event->SetString( "cpname", STRING(m_iszPrintName) );
+
+ char cappers[3];
+ cappers[0] = iCappingPlayer;
+ cappers[1] = '\0';
+
+ // pCappingPlayers is a null terminated list of player indeces
+ event->SetString( "cappers", cappers );
+ event->SetInt( "priority", 9 );
+
+ event->SetBool( "bomb", true );
+
+ gameeventmanager->FireEvent( event );
+ }
+ }
+ }
+ else
+ {
+ if ( m_iBombsRemaining <= 0 )
+ {
+ // get the opposite team
+ int team = ( GetOwner() == TEAM_ALLIES ) ? TEAM_AXIS : TEAM_ALLIES;
+
+ InternalSetOwner( team, true );
+ g_pObjectiveResource->SetOwningTeam( GetPointIndex(), GetOwner() );
+ }
+ }
+
+ g_pObjectiveResource->SetBombPlanted( GetPointIndex(), false );
+}
+
+void CControlPoint::BombDisarmed( CDODPlayer *pDisarmingPlayer )
+{
+ CancelBombPlanted();
+
+ CaptureBlocked( pDisarmingPlayer );
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_bomb_defused" );
+ if ( event )
+ {
+ event->SetInt( "cp", m_iPointIndex );
+ event->SetString( "cpname", STRING(m_iszPrintName) );
+ event->SetInt( "userid", pDisarmingPlayer->GetUserID() );
+ event->SetInt( "team", pDisarmingPlayer->GetTeamNumber() );
+ gameeventmanager->FireEvent( event );
+ }
+}
+
+void CControlPoint::CancelBombPlanted( void )
+{
+ m_bBombPlanted = false;
+
+ // reset hud display
+ g_pObjectiveResource->SetBombPlanted( GetPointIndex(), false );
+} \ No newline at end of file
diff --git a/game/server/dod/dod_control_point.h b/game/server/dod/dod_control_point.h
new file mode 100644
index 0000000..50905c3
--- /dev/null
+++ b/game/server/dod/dod_control_point.h
@@ -0,0 +1,164 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_CONTROL_POINT_H
+#define DOD_CONTROL_POINT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_player.h"
+
+#define CAP_ICON_ALLIES_FLAG 1
+#define CAP_ICON_BRIT_FLAG 27 //from dod_objectives.cpp
+
+#define CAP_POINT_HIDEFLAG (1<<0)
+#define CAP_POINT_HIDE_MODEL (1<<1)
+#define CAP_POINT_TICK_FOR_BOMBS_REMAINING (1<<2)
+
+#define PLAYER_POINTS_FOR_CAP 1
+#define PLAYER_POINTS_FOR_BLOCK 1
+#define PLAYER_POINTS_FOR_BOMB_PLANT 1
+#define PLAYER_POINTS_FOR_BOMB_EXPLODED 3
+
+
+class CControlPoint : public CBaseAnimating
+{
+
+public:
+ DECLARE_CLASS( CControlPoint, CBaseAnimating );
+ DECLARE_DATADESC();
+
+ CControlPoint();
+
+ virtual void Spawn( void );
+ virtual bool KeyValue( const char *szKeyName, const char *szValue );
+ virtual void Precache( void );
+
+ void Reset( void );
+
+ //Inputs
+ inline void Enable( inputdata_t &input ) { SetActive( false ); }
+ inline void Disable( inputdata_t &input ) { SetActive( true ); }
+ void InputReset( inputdata_t &input );
+ void InputSetOwner( inputdata_t &input );
+
+ void InputShowModel( inputdata_t &input );
+ void InputHideModel( inputdata_t &input );
+
+ int PointValue( void );
+
+ void RoundRespawn( void ); //Mugsy - resetting
+ void TriggerTargets( void );
+
+ void SetActive( bool active );
+
+ bool PointIsVisible( void ) { return !( FBitSet( m_spawnflags, CAP_POINT_HIDEFLAG ) ); }
+
+ void SendCapString( int team, int iNumCappers, int *pCappingPlayers );
+
+ void SetOwner( int team, bool bMakeSound = true, int iNumCappers = 0, int *iCappingPlayers = NULL );
+ int GetOwner( void ) const;
+
+ int GetDefaultOwner( void ) const;
+
+ inline const char *GetName( void ) { return STRING(m_iszPrintName); }
+ int GetCPGroup( void );
+ int GetPointIndex( void ) { return m_iPointIndex; } //the mapper set index
+ void SetPointIndex( int index ) { m_iPointIndex = index; }
+ int GetAlliesIcon( void ) { return m_iAlliesIcon; }
+ int GetAxisIcon( void ) { return m_iAxisIcon; }
+ int GetNeutralIcon( void ) { return m_iNeutralIcon; }
+
+ int GetCurrentHudIconIndex( void );
+ int GetHudIconIndexForTeam( int team );
+ int GetTimerCapHudIcon( void );
+ int GetBombedHudIcon( void );
+
+ inline bool IsActive( void ) { return m_bActive; }
+
+ void SetNumCappersRequired( int alliesRequired, int axisRequired );
+
+ void CaptureBlocked( CDODPlayer *pPlayer );
+
+ // Bomb interface
+ void BombPlanted( float flTimerLength, CDODPlayer *pPlantingPlayer );
+ void BombExploded( CDODPlayer *pPlantingPlayer = NULL, int iPlantingTeam = TEAM_UNASSIGNED );
+ void BombDisarmed( CDODPlayer *pDisarmingPlayer );
+ void CancelBombPlanted( void );
+
+ int GetBombsRemaining( void ) { return m_iBombsRemaining; } // total bombs required
+ int GetBombsRequired( void ) { return m_iBombsRequired; } // number of bombs remaining
+
+private:
+ void InternalSetOwner( int team, bool bMakeSound = true, int iNumCappers = 0, int *iCappingPlayers = NULL );
+
+ int m_iTeam; //0 - clear, 2 - allies, 3 - axis
+ int m_iDefaultOwner; //team that initially owns the cap point
+ int m_iIndex; //the index of this point in the controlpointArray
+
+ string_t m_iszPrintName;
+
+ string_t m_iszAlliesCapSound; //the sound to play on cap
+ string_t m_iszAxisCapSound;
+ string_t m_iszResetSound;
+
+ string_t m_iszAlliesModel; //models to set the ent to on capture
+ string_t m_iszAxisModel;
+ string_t m_iszResetModel;
+
+ int m_iAlliesModelBodygroup;//which bodygroup to use in the model
+ int m_iAxisModelBodygroup;
+ int m_iResetModelBodygroup;
+
+ COutputEvent m_AlliesCapOutput; //outputs to fire when capped
+ COutputEvent m_AxisCapOutput;
+ COutputEvent m_PointResetOutput;
+
+ COutputEvent m_OwnerChangedToAllies;
+ COutputEvent m_OwnerChangedToAxis;
+
+ int m_iAlliesIcon; //custom hud sprites for cap point
+ int m_iAxisIcon;
+ int m_iNeutralIcon;
+ int m_iTimerCapIcon;
+ int m_iBombedIcon;
+
+ string_t m_iszAlliesIcon;
+ string_t m_iszAxisIcon;
+ string_t m_iszNeutralIcon;
+ string_t m_iszTimerCapIcon;
+ string_t m_iszBombedIcon;
+
+ int m_bPointVisible; //should this capture point be visible on the hud?
+ int m_iPointIndex; //the mapper set index value of this control point
+
+ int m_iCPGroup; //the group that this control point belongs to
+ bool m_bActive; //
+
+ string_t m_iszName; //Name used in cap messages
+
+ bool m_bStartDisabled;
+
+ int m_iAlliesRequired; // if we're controlled by an area cap,
+ int m_iAxisRequired; // these hold the number of cappers required. Used to calc point value
+
+ int m_iTimedPointsAllies; // timed points value of this flag, per team
+ int m_iTimedPointsAxis;
+
+ bool m_bBombPlanted;
+ float m_flBombExplodeTime;
+
+ int m_iBombsRemaining;
+ int m_iBombsRequired; // number of bombs required to flip this control point
+
+ bool m_bSetOwnerIsBombPlant; // temp flag to indicate if the set owner we're doing is the result of a bomb
+
+private:
+ CControlPoint( const CControlPoint & );
+};
+
+#endif //DOD_CONTROL_POINT_H \ No newline at end of file
diff --git a/game/server/dod/dod_control_point_master.cpp b/game/server/dod/dod_control_point_master.cpp
new file mode 100644
index 0000000..b511ef5
--- /dev/null
+++ b/game/server/dod/dod_control_point_master.cpp
@@ -0,0 +1,587 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_control_point_master.h"
+
+BEGIN_DATADESC( CControlPointMaster )
+ DEFINE_KEYFIELD( m_bDisabled, FIELD_BOOLEAN, "StartDisabled" ),
+
+ DEFINE_KEYFIELD( m_iTimerLength, FIELD_INTEGER, "cpm_timer_length" ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "Enable", InputEnable ),
+ DEFINE_INPUTFUNC( FIELD_VOID, "Disable", InputDisable ),
+
+ DEFINE_INPUTFUNC( FIELD_VOID, "RoundInit", InputRoundInit ),
+
+ DEFINE_FUNCTION( CPMThink ),
+
+ DEFINE_OUTPUT( m_AlliesWinOutput, "OnAlliesWin" ),
+ DEFINE_OUTPUT( m_AxisWinOutput, "OnAxisWin" ),
+
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "AddTimerSeconds", InputAddTimerSeconds ),
+
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( dod_control_point_master, CControlPointMaster );
+
+CControlPointMaster::CControlPointMaster()
+{
+ m_bUseTimer = false;
+ m_iTimerTeam = TEAM_UNASSIGNED;
+
+ SetDefLessFunc( m_ControlPoints );
+}
+
+void CControlPointMaster::Spawn( void )
+{
+ SetTouch( NULL );
+ m_bFoundPoints = false;
+ BaseClass::Spawn();
+}
+
+ConVar mp_tickpointinterval( "mp_tickpointinterval", "30", FCVAR_GAMEDLL, "Delay between point gives.", true, 1, false, 0 );
+
+void CControlPointMaster::RoundRespawn( void )
+{
+ m_fGivePointsTime = gpGlobals->curtime + mp_tickpointinterval.GetInt();
+}
+
+
+// KeyValue
+// ========
+// this function interfaces with the keyvalues set by the mapper
+//
+// Values
+// point_give_delay_time - how often to give time based points ( seconds )
+// allies_capture_target - target to fire on allies complete capture
+// axis_capture_target - target to fire on axis complete capture
+
+bool CControlPointMaster::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if (FStrEq(szKeyName, "cpm_use_timer"))
+ {
+ m_bUseTimer = ( atoi(szValue) > 0 );
+ }
+ else if (FStrEq(szKeyName, "cpm_timer_team"))
+ {
+ m_iTimerTeam = atoi(szValue);
+ }
+ else
+ {
+ return CBaseEntity::KeyValue( szKeyName, szValue );
+ }
+
+ return true;
+}
+
+void CControlPointMaster::Reset( void )
+{
+
+}
+
+bool CControlPointMaster::FindControlPoints( void )
+{
+ //go through all the points
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_control_point" );
+
+ int numFound = 0;
+
+ while( pEnt )
+ {
+ CControlPoint *pPoint = (CControlPoint *)pEnt;
+
+ if( pPoint->IsActive() )
+ {
+ int index = pPoint->GetPointIndex();
+
+ Assert( index >= 0 );
+
+ if( m_ControlPoints.Find( index ) == m_ControlPoints.InvalidIndex())
+ {
+ DevMsg( 2, "Adding control point %s with index %d\n", pPoint->GetName(), index );
+ m_ControlPoints.Insert( index, pPoint );
+ numFound++;
+ }
+ else
+ {
+ Warning( "!!!!\nMultiple control points with the same index, duplicates ignored\n!!!!\n" );
+ UTIL_Remove( pPoint );
+ }
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "dod_control_point" );
+ }
+
+ if( numFound > MAX_CONTROL_POINTS )
+ {
+ Warning( "Too many control points! Max is %d\n", MAX_CONTROL_POINTS );
+ }
+
+ //Remap the indeces of the control points so they are 0-based
+ //======================
+ unsigned int j;
+
+ bool bHandled[MAX_CONTROL_POINTS];
+ memset( bHandled, 0, sizeof(bHandled) );
+
+ unsigned int numPoints = m_ControlPoints.Count();
+ unsigned int newIndex = 0;
+
+ while( newIndex < numPoints )
+ {
+ //Find the lowest numbered, unhandled point
+ int lowestIndex = -1;
+ int lowestValue = 999;
+
+ //find the lowest unhandled index
+ for( j=0; j<numPoints; j++ )
+ {
+ if( !bHandled[j] && m_ControlPoints[j]->GetPointIndex() < lowestValue )
+ {
+ lowestIndex = j;
+ lowestValue = m_ControlPoints[j]->GetPointIndex();
+ }
+ }
+
+ //Don't examine this point again
+ Assert( lowestIndex >= 0 );
+ bHandled[lowestIndex] = true;
+
+ //Give it its new index
+ m_ControlPoints[lowestIndex]->SetPointIndex( newIndex );
+ newIndex++;
+ }
+
+ if( m_ControlPoints.Count() == 0 )
+ {
+ Warning( "Error! No control points found in map!\n");
+ return false;
+ }
+
+ g_pObjectiveResource->SetNumControlPoints( m_ControlPoints.Count() );
+
+ unsigned int i;
+ for( i=0;i<m_ControlPoints.Count();i++ )
+ {
+ CControlPoint *pPoint = m_ControlPoints[i];
+
+ int iPointIndex = m_ControlPoints[i]->GetPointIndex();
+
+ g_pObjectiveResource->SetOwningTeam( iPointIndex, pPoint->GetOwner() );
+ g_pObjectiveResource->SetCPVisible( iPointIndex, pPoint->PointIsVisible() );
+ g_pObjectiveResource->SetCPPosition( iPointIndex, pPoint->GetAbsOrigin() );
+
+ g_pObjectiveResource->SetBombsRequired( iPointIndex, pPoint->GetBombsRequired() );
+ g_pObjectiveResource->SetBombsRemaining( iPointIndex, pPoint->GetBombsRemaining() );
+
+ g_pObjectiveResource->SetCPIcons( iPointIndex,
+ pPoint->GetHudIconIndexForTeam(TEAM_ALLIES),
+ pPoint->GetHudIconIndexForTeam(TEAM_AXIS),
+ pPoint->GetHudIconIndexForTeam(TEAM_UNASSIGNED),
+ pPoint->GetTimerCapHudIcon(),
+ pPoint->GetBombedHudIcon() );
+ }
+
+ return true;
+}
+
+// Think
+// =====
+// Think is called every 0.1 seconds and checks the status of all the control points
+// if one team owns them all, it gives points and resets
+// Think also gives the time based points at the specified time intervals
+//
+// I moved the search for spawn points to the initial think - sometimes the points spawned
+// after the master and it wasnt finding them.
+
+void CControlPointMaster::CPMThink( void )
+{
+ // search for all "dod_control_point" entities in the map
+ // and put them in the array
+ // only done the first Think
+
+ //try to establish if any dod_area_capture ents are linked to our flags
+ //via dod_point_relay
+
+ //if there exists a point relay that has this as the target,
+ //AND there exists a capture area that has that relay as a target
+ //then we have our area!
+
+ //every think
+
+ //go through all the control points and make sure theyre the same as the last think
+ //check masters
+
+ //if they are, do nothing
+ //else
+ //
+
+ //Note! Only one cpm should ever be active at the same time
+ //funny things may happen if there are two of em!
+ //if we are recently mastered on or off, touch the cps
+
+ //---------------------------------------------------------------------------
+ //below here we shouldn't execute unless we are an active control point master
+
+ //if the round has been decided, don't do any more thinking
+ //if this cpm is not active, dont' do any thinking
+
+ int iRoundState = DODGameRules()->State_Get();
+
+ // No points or triggering new wins if we are not active
+ if( m_bDisabled || iRoundState != STATE_RND_RUNNING )
+ {
+ SetContextThink( &CControlPointMaster::CPMThink, gpGlobals->curtime + 0.2, FLAGS_CONTEXT );
+ return;
+ }
+
+ // is it time to give time-based points yet?
+ if( gpGlobals->curtime > m_fGivePointsTime )
+ {
+ int AlliesPoints = 0;
+ int AxisPoints = 0;
+
+ //give points based on who owns what
+ unsigned int i;
+ for( i=0;i<m_ControlPoints.Count();i++ )
+ {
+ switch( m_ControlPoints[i]->GetOwner() )
+ {
+ case TEAM_ALLIES:
+ AlliesPoints += m_ControlPoints[i]->PointValue();
+ break;
+ case TEAM_AXIS:
+ AxisPoints += m_ControlPoints[i]->PointValue();
+ break;
+ default:
+ break;
+ }
+ }
+
+ //================
+ //give team points
+ //================
+ // See if there are players active on these teams
+
+ bool bFoundAllies = ( GetGlobalTeam(TEAM_ALLIES)->GetNumPlayers() > 0 );
+ bool bFoundAxis = ( GetGlobalTeam(TEAM_AXIS)->GetNumPlayers() > 0 );
+
+ bool bReportScore = true;
+
+ // don't give team points to a team with no-one on it!
+ if( AlliesPoints > 0 && bFoundAllies && DODGameRules()->State_Get() == STATE_RND_RUNNING )
+ {
+ if( bReportScore )
+ {
+ if (AlliesPoints == 1)
+ UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_score_allie_point" );
+ else
+ {
+ char buf[8];
+ Q_snprintf( buf, sizeof(buf), "%d", AlliesPoints );
+ UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_score_allie_points", buf );
+ }
+
+ }
+ GetGlobalTeam(TEAM_ALLIES)->AddScore( AlliesPoints );
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_tick_points" );
+
+ if ( event )
+ {
+ event->SetInt( "team", TEAM_ALLIES );
+ event->SetInt( "score", AlliesPoints );
+ event->SetInt( "totalscore", GetGlobalTeam(TEAM_ALLIES)->GetScore() );
+
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ if( AxisPoints > 0 && bFoundAxis && DODGameRules()->State_Get() == STATE_RND_RUNNING )
+ {
+ if( bReportScore )
+ {
+ if (AxisPoints == 1)
+ UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_score_axis_point" );
+ else
+ {
+ char buf[8];
+ Q_snprintf( buf, sizeof(buf), "%d", AxisPoints );
+ UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_score_axis_points", buf );
+ }
+ }
+ GetGlobalTeam(TEAM_AXIS)->AddScore( AxisPoints );
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_tick_points" );
+
+ if ( event )
+ {
+ event->SetInt( "team", TEAM_AXIS );
+ event->SetInt( "score", AxisPoints );
+ event->SetInt( "totalscore", GetGlobalTeam(TEAM_AXIS)->GetScore() );
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ // the next time we'll give points
+ m_fGivePointsTime = gpGlobals->curtime + mp_tickpointinterval.GetInt();
+ }
+
+ // If we call this from dod_control_point, this function should never
+ // trigger a win. but we'll leave it here just incase.
+ CheckWinConditions();
+
+ // the next time we 'think'
+ SetContextThink( &CControlPointMaster::CPMThink, gpGlobals->curtime + 0.2, FLAGS_CONTEXT );
+}
+
+void CControlPointMaster::CheckWinConditions( void )
+{
+ // ============
+ // Check that the points aren't all held by one team
+ // if they are this will reset the round
+ // and will reset all the points
+ // ============
+ switch( TeamOwnsAllPoints() )
+ {
+ case TEAM_ALLIES: //allies own all
+ {
+ TeamWins( TEAM_ALLIES );
+ }
+ break;
+ case TEAM_AXIS: //axis owns all
+ {
+ TeamWins( TEAM_AXIS );
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+void CControlPointMaster::InputRoundInit( inputdata_t &input )
+{
+ //clear out old control points
+ m_ControlPoints.RemoveAll();
+
+ //find the control points
+
+ //if successful, do CPMThink
+ if( FindControlPoints() )
+ {
+ SetContextThink( &CControlPointMaster::CPMThink, gpGlobals->curtime + 0.1, FLAGS_CONTEXT );
+ }
+
+ //init the ClientAreas
+ int index = 0;
+
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_capture_area" );
+ while( pEnt )
+ {
+ CAreaCapture *pArea = (CAreaCapture *)pEnt;
+ Assert( pArea );
+ pArea->area_SetIndex( index );
+ index++;
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "dod_capture_area" );
+ }
+
+ g_pObjectiveResource->ResetControlPoints();
+}
+
+void CControlPointMaster::FireTeamWinOutput( int iWinningTeam )
+{
+ switch( iWinningTeam )
+ {
+ case TEAM_ALLIES:
+ m_AlliesWinOutput.FireOutput(this,this);
+ break;
+ case TEAM_AXIS:
+ m_AxisWinOutput.FireOutput(this,this);
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+}
+
+void CControlPointMaster::TeamWins( int iWinningTeam )
+{
+ DODGameRules()->SetWinningTeam( iWinningTeam );
+
+ FireTeamWinOutput( iWinningTeam );
+}
+
+void CControlPointMaster::BecomeActive( void )
+{
+ Assert( m_bDisabled );
+ m_bDisabled = false;
+}
+
+void CControlPointMaster::BecomeInactive( void )
+{
+ Assert( !m_bDisabled );
+ m_bDisabled = true;
+}
+
+int CControlPointMaster::GetPointOwner( int point )
+{
+ Assert( point >= 0 );
+ Assert( point < MAX_CONTROL_POINTS );
+
+ CControlPoint *pPoint = m_ControlPoints[point];
+
+ if( pPoint )
+ {
+ return pPoint->GetOwner();
+ }
+ else
+ return TEAM_UNASSIGNED;
+}
+
+// TeamOwnsAllPoints
+// =================
+// This function returns the team that owns all
+// the cap points. if its not the case that one
+// team owns them all, it returns 0
+
+// New - cps are now broken into groups.
+// A team can win by owning all flags within a single group.
+
+// Can be passed an overriding team. If this is not null, the passed team
+// number will be used for that cp. Used to predict if that CP changing would
+// win the game.
+
+int CControlPointMaster::TeamOwnsAllPoints( CControlPoint *pOverridePoint /* = NULL */, int iOverrideNewTeam /* = TEAM_UNASSIGNED */ )
+{
+ unsigned int i;
+
+ int iWinningTeam[MAX_CONTROL_POINT_GROUPS];
+
+ for( i=0;i<MAX_CONTROL_POINT_GROUPS;i++ )
+ {
+ iWinningTeam[i] = TEAM_INVALID;
+ }
+
+ // if TEAM_INVALID, haven't found a flag for this group yet
+ // if TEAM_UNASSIGNED, the group is still being contested
+
+ // for each control point
+ for( i=0;i<m_ControlPoints.Count();i++ )
+ {
+ int group = m_ControlPoints[i]->GetCPGroup();
+ int owner = m_ControlPoints[i]->GetOwner();
+
+ if ( pOverridePoint == m_ControlPoints[i] )
+ {
+ owner = iOverrideNewTeam;
+ }
+
+ // the first one we find in this group, set the win to true
+ if ( iWinningTeam[group] == TEAM_INVALID )
+ {
+ iWinningTeam[group] = owner;
+ }
+ // unassigned means this group is already contested, move on
+ else if ( iWinningTeam[group] == TEAM_UNASSIGNED )
+ {
+ continue;
+ }
+ // if we find another one in the group that isn't the same owner, set the win to false
+ else if ( owner != iWinningTeam[group] )
+ {
+ iWinningTeam[group] = TEAM_UNASSIGNED;
+ }
+ }
+
+ // report the first win we find as the winner
+ for ( i=0;i<MAX_CONTROL_POINT_GROUPS;i++ )
+ {
+ if ( iWinningTeam[i] == TEAM_ALLIES || iWinningTeam[i] == TEAM_AXIS )
+ return iWinningTeam[i];
+ }
+
+ // no wins yet
+ return TEAM_UNASSIGNED;
+}
+
+// an advantage flag is a flag that we've taken that didn't initially
+// belong to our team
+int CControlPointMaster::CountAdvantageFlags( int team )
+{
+ int numFlags = 0;
+
+ unsigned int i;
+ for( i = 0;i<m_ControlPoints.Count();i++ )
+ {
+ CControlPoint *pPoint = m_ControlPoints[i];
+
+ if ( pPoint->GetOwner() != pPoint->GetDefaultOwner() )
+ {
+ if ( pPoint->GetOwner() == team )
+ {
+ // we own a flag we didn't initially
+ numFlags++;
+ }
+ else //
+ {
+ // the enemy owns one of our flags
+ numFlags--;
+ }
+ }
+ }
+
+ return numFlags;
+}
+
+void CControlPointMaster::InputAddTimerSeconds( inputdata_t &inputdata )
+{
+ DODGameRules()->AddTimerSeconds( inputdata.value.Int() );
+}
+
+void CControlPointMaster::GetTimerData( int &iTimerSeconds, int &iTimerWinTeam )
+{
+ iTimerSeconds = m_iTimerLength;
+ iTimerWinTeam = m_iTimerTeam;
+}
+
+bool CControlPointMaster::WouldNewCPOwnerWinGame( CControlPoint *pPoint, int iNewOwner )
+{
+ return ( TeamOwnsAllPoints( pPoint, iNewOwner ) == iNewOwner );
+}
+
+int CControlPointMaster::GetNumPoints( void )
+{
+ return m_ControlPoints.Count();
+}
+
+CControlPoint *CControlPointMaster::GetCPByIndex( int index )
+{
+ CControlPoint *pPoint = NULL;
+
+ if ( index >= 0 && index < (int)m_ControlPoints.Count() )
+ {
+ pPoint = m_ControlPoints[index];
+ }
+
+ return pPoint;
+}
+
+// custom scoring entity
+BEGIN_DATADESC( CDODCustomScoring )
+ DEFINE_INPUTFUNC( FIELD_INTEGER, "GiveTickPoints", InputGivePoints ),
+
+ DEFINE_KEYFIELD( m_iPointTeam, FIELD_INTEGER, "TeamNum" ),
+ DEFINE_KEYFIELD( m_iTickLength, FIELD_INTEGER, "point_give_delay" ),
+ DEFINE_KEYFIELD( m_iPointsToGive, FIELD_INTEGER, "point_give_amount" ),
+ DEFINE_KEYFIELD( m_iNumPointGives, FIELD_INTEGER, "point_give_max_times" ),
+
+ DEFINE_THINKFUNC( PointsThink ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( dod_scoring, CDODCustomScoring ); \ No newline at end of file
diff --git a/game/server/dod/dod_control_point_master.h b/game/server/dod/dod_control_point_master.h
new file mode 100644
index 0000000..acb531e
--- /dev/null
+++ b/game/server/dod/dod_control_point_master.h
@@ -0,0 +1,222 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_CONTROL_POINT_MASTER_H
+#define DOD_CONTROL_POINT_MASTER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "utlmap.h"
+#include "dod_shareddefs.h"
+#include "dod_team.h"
+#include "dod_gamerules.h"
+#include "dod_control_point.h"
+#include "dod_area_capture.h"
+#include "dod_objective_resource.h"
+#include "GameEventListener.h"
+
+// ====================
+// Control Point Master
+// ====================
+// One ControlPointMaster is spawned per level. Shortly after spawning it detects all the Control
+// points in the map and puts them into the m_ControlPoints. From there it detects the state
+// where all points are captured and resets them if necessary It gives points every time interval to
+// the owners of the points
+// ====================
+
+#define TIMER_CONTEXT "TIMER_CONTEXT"
+#define FLAGS_CONTEXT "FLAGS_CONTEXT"
+
+class CControlPointMaster : public CBaseEntity
+{
+public:
+ DECLARE_CLASS( CControlPointMaster, CBaseEntity );
+
+ CControlPointMaster();
+
+ virtual void Spawn( void );
+ virtual bool KeyValue( const char *szKeyName, const char *szValue );
+
+ int GetNumPoints( void );
+ int GetPointOwner( int point );
+ void Reset( void );
+
+ void RoundRespawn( void );
+
+ int CountAdvantageFlags( int team );
+
+ bool IsActive( void ) { return ( m_bDisabled == false ); }
+
+ bool IsUsingRoundTimer( void ) { return m_bUseTimer; }
+ void GetTimerData( int &iTimerSeconds, int &iTimerWinTeam );
+
+ void FireTeamWinOutput( int iWinningTeam );
+
+ void CheckWinConditions( void );
+
+ bool WouldNewCPOwnerWinGame( CControlPoint *pPoint, int iNewOwner );
+
+ CControlPoint *GetCPByIndex( int index );
+
+private:
+ void BecomeActive( void );
+ void BecomeInactive( void );
+
+ void EXPORT CPMThink( void );
+
+ int TeamOwnsAllPoints( CControlPoint *pOverridePoint = NULL, int iOverrideNewTeam = TEAM_UNASSIGNED );
+
+ void TeamWins( int team );
+
+ bool FindControlPoints( void ); //look in the map to find active control points
+
+ void InputAddTimerSeconds( inputdata_t &inputdata );
+
+ CUtlMap<int, CControlPoint *> m_ControlPoints;
+
+ bool m_bFoundPoints; //true when the control points have been found and the array is initialized
+
+ float m_fGivePointsTime; //the time at which we give points for holding control points
+
+ DECLARE_DATADESC();
+
+ bool m_bDisabled; //is this CPM active or not
+ void InputEnable( inputdata_t &inputdata ) { BecomeInactive(); }
+ void InputDisable( inputdata_t &inputdata ) { BecomeActive(); }
+
+ void InputRoundInit( inputdata_t &inputdata );
+ void InputRoundStart( inputdata_t &inputdata );
+
+ bool m_bUseTimer;
+ int m_iTimerTeam;
+ int m_iTimerLength;
+
+ COutputEvent m_AlliesWinOutput;
+ COutputEvent m_AxisWinOutput;
+};
+
+class CDODCustomScoring : public CBaseEntity, public CGameEventListener
+{
+public:
+ DECLARE_CLASS( CDODCustomScoring, CBaseEntity );
+
+ DECLARE_DATADESC();
+
+ CDODCustomScoring()
+ {
+ ListenForGameEvent( "dod_round_win" );
+ ListenForGameEvent( "dod_round_active" );
+ }
+
+ virtual void Spawn( void )
+ {
+ Assert( m_iPointTeam == TEAM_ALLIES || m_iPointTeam == TEAM_AXIS );
+ }
+
+ virtual void FireGameEvent( IGameEvent *event )
+ {
+ const char *eventName = event->GetName();
+
+ if ( !Q_strcmp( eventName, "dod_round_win" ) )
+ {
+ int team = event->GetInt( "team" );
+ if ( team == m_iPointTeam )
+ {
+ GiveRemainingPoints();
+ }
+
+ // stop giving points, round is over
+ SetThink( NULL ); // think no more!
+ }
+ else if ( !Q_strcmp( eventName, "dod_round_active" ) )
+ {
+ StartGivingPoints();
+ }
+ }
+
+ // Needs to be activated by gamerules
+ void StartGivingPoints( void )
+ {
+ if ( m_iNumPointGives <= 0 )
+ return;
+
+ if ( m_iPointsToGive <= 0 )
+ return;
+
+ m_iRemainingPoints = m_iNumPointGives * m_iPointsToGive;
+
+ SetThink( &CDODCustomScoring::PointsThink );
+ SetNextThink( gpGlobals->curtime + m_iTickLength );
+ }
+
+ // Give this team all the points that they would have gotten had we continued
+ void GiveRemainingPoints( void )
+ {
+ GivePoints( m_iRemainingPoints );
+ }
+
+ // input to give tick points to our team
+ void InputGivePoints( inputdata_t &inputdata )
+ {
+ int iPoints = inputdata.value.Int();
+ GetGlobalTeam(m_iPointTeam)->AddScore( MAX( iPoints, 0 ) );
+ }
+
+ // accessor for our point team
+ int GetPointTeam( void )
+ {
+ return m_iPointTeam;
+ }
+
+private:
+
+ void GivePoints( int points )
+ {
+ GetGlobalTeam(m_iPointTeam)->AddScore( MAX( points, 0 ) );
+ m_iRemainingPoints -= points;
+
+ if ( points > 0 )
+ {
+ if ( points == 1 )
+ {
+ UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_score_allie_point" );
+ }
+ else
+ {
+ char buf[8];
+ Q_snprintf( buf, sizeof(buf), "%d", points );
+ UTIL_ClientPrintAll( HUD_PRINTTALK, "#game_score_allie_points", buf );
+ }
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_tick_points" );
+
+ if ( event )
+ {
+ event->SetInt( "team", m_iPointTeam );
+ event->SetInt( "score", points );
+ event->SetInt( "totalscore", GetGlobalTeam(m_iPointTeam)->GetScore() );
+
+ gameeventmanager->FireEvent( event );
+ }
+ }
+ }
+
+ void PointsThink( void )
+ {
+ GivePoints( m_iPointsToGive );
+
+ SetNextThink( gpGlobals->curtime + m_iTickLength );
+ }
+
+ int m_iPointTeam; // team to give points to
+ int m_iPointsToGive; // points to give per tick
+ int m_iRemainingPoints; // total number of points we have left to give
+ int m_iTickLength; // time between point gives
+ int m_iNumPointGives; // number of times we're planning on giving out points
+};
+
+#endif //DOD_CONTROL_POINT_MASTER_H
diff --git a/game/server/dod/dod_cvars.cpp b/game/server/dod/dod_cvars.cpp
new file mode 100644
index 0000000..a722728
--- /dev/null
+++ b/game/server/dod/dod_cvars.cpp
@@ -0,0 +1,90 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_cvars.h"
+
+
+/*
+// Gameplay
+cvar_t cvar_forcechasecam = {"mp_forcechasecam", "1", FCVAR_SERVER };
+cvar_t cvar_forcecamera = {"mp_forcecamera", "1", FCVAR_SERVER };
+
+
+// Clan cvars
+cvar_t cvar_waveTime = {"mp_clan_respawntime", "0", FCVAR_SERVER };
+cvar_t cvar_clanRestartRound = {"mp_clan_restartround", "0", FCVAR_SERVER };
+cvar_t cvar_clanReadyRestart = {"mp_clan_readyrestart", "0", FCVAR_SERVER };
+cvar_t cvar_clanMatchWarmup = {"mp_clan_match_warmup", "0", FCVAR_SERVER };
+cvar_t cvar_clanMatch = {"mp_clan_match", "0", FCVAR_SERVER };
+cvar_t cvar_clanTimer = {"mp_clan_timer", "20", FCVAR_SERVER };
+cvar_t cvar_clanScoring = {"mp_clan_scoring", "0", FCVAR_SERVER };
+cvar_t cvar_clanScoringValuesAllies = {"mp_clan_scoring_values_allies", "111111", FCVAR_SERVER };
+cvar_t cvar_clanScoringValuesAxis = {"mp_clan_scoring_values_axis", "111111", FCVAR_SERVER };
+cvar_t cvar_clanScoringDelay = {"mp_clan_scoring_delay", "60", FCVAR_SERVER };
+cvar_t cvar_clanReadyString = {"mp_clan_ready_signal", "ready", FCVAR_SERVER };
+cvar_t cvar_clanScoringBonusAllies = {"mp_clan_scoring_bonus_allies", "-1", FCVAR_SERVER };
+cvar_t cvar_clanScoringBonusAxis = {"mp_clan_scoring_bonus_axis", "-1", FCVAR_SERVER };
+
+
+// Other Server Cvars ( Not FCVAR_SERVER! )
+//===================
+cvar_t cvar_nummapmarkers = {"mp_nummapmarkers", "1" };
+cvar_t cvar_markerstaytime = {"mp_markerstaytime", "30"};
+
+cvar_t cvar_teamSpectators = {"mp_allowspectators", "1" };
+cvar_t cvar_logScores = {"mp_log_scores", "0" };
+cvar_t cvar_logScoresDelay = {"mp_log_scores_delay", "60"};
+cvar_t cvar_tkPenalty = {"mp_tkpenalty", "6" };
+* now mp_limitteams * cvar_t cvar_teamOver = {"mp_teamlimit", "2" };
+cvar_t cvar_deathMsg = {"mp_deathmsg", "1" };
+cvar_t cvar_chatMsg = {"mp_chatmsg", "1" };
+cvar_t cvar_alliesclasses = {"mp_alliesclasses", "-1"};
+cvar_t cvar_axisclasses = {"mp_axisclasses", "-1"};
+*/
+
+ ConVar mp_winlimit( "mp_winlimit", "0", FCVAR_REPLICATED | FCVAR_NOTIFY, "Max score one team can reach before server changes maps", true, 0, false, 0 );
+
+ ConVar mp_clan_restartround( "mp_clan_restartround", "0", FCVAR_GAMEDLL, "If non-zero, the round will restart in the specified number of seconds" );
+
+ ConVar mp_limitteams(
+ "mp_limitteams",
+ "2",
+ FCVAR_REPLICATED,
+ "Max # of players 1 team can have over another",
+ true, 1, // min value
+ true, 20 // max value
+ );
+
+ ConVar mp_autokick(
+ "mp_autokick",
+ "0",
+ FCVAR_REPLICATED,
+ "Kick idle/team-killing players" );
+
+ ConVar mp_combinemglimits(
+ "mp_combinemglimits",
+ "0",
+ FCVAR_REPLICATED,
+ "Set to 1 to combine the class limit cvars for mg34 and mg42. New limit is sum of two" );
+
+ // Class limit cvars
+ // welcome to ugly-town
+
+ ConVar mp_limitAlliesRifleman( "mp_limit_allies_rifleman", "-1", FCVAR_REPLICATED, "Class limit for team: Allies class: Rifleman" );
+ ConVar mp_limitAlliesAssault( "mp_limit_allies_assault", "-1", FCVAR_REPLICATED, "Class limit for team: Allies class: Assault" );
+ ConVar mp_limitAlliesSupport( "mp_limit_allies_support", "-1", FCVAR_REPLICATED, "Class limit for team: Allies class: Support" );
+ ConVar mp_limitAlliesSniper( "mp_limit_allies_sniper", "-1", FCVAR_REPLICATED, "Class limit for team: Allies class: Sniper" );
+ ConVar mp_limitAlliesMachinegun( "mp_limit_allies_mg", "-1", FCVAR_REPLICATED, "Class limit for team: Allies class: Machinegunner" );
+ ConVar mp_limitAlliesRocket( "mp_limit_allies_rocket", "-1", FCVAR_REPLICATED, "Class limit for team: Allies class: Rocket" );
+
+ ConVar mp_limitAxisRifleman( "mp_limit_axis_rifleman", "-1", FCVAR_REPLICATED, "Class limit for team: Axis class: Rifleman" );
+ ConVar mp_limitAxisAssault( "mp_limit_axis_assault", "-1", FCVAR_REPLICATED, "Class limit for team: Axis class: Assault" );
+ ConVar mp_limitAxisSupport( "mp_limit_axis_support", "-1", FCVAR_REPLICATED, "Class limit for team: Axis class: Support" );
+ ConVar mp_limitAxisSniper( "mp_limit_axis_sniper", "-1", FCVAR_REPLICATED, "Class limit for team: Axis class: Sniper" );
+ ConVar mp_limitAxisMachinegun( "mp_limit_axis_mg", "-1", FCVAR_REPLICATED, "Class limit for team: Axis class: Machinegunner" );
+ ConVar mp_limitAxisRocket( "mp_limit_axis_rocket", "-1", FCVAR_REPLICATED, "Class limit for team: Axis class: Rocket" );
diff --git a/game/server/dod/dod_cvars.h b/game/server/dod/dod_cvars.h
new file mode 100644
index 0000000..bae53eb
--- /dev/null
+++ b/game/server/dod/dod_cvars.h
@@ -0,0 +1,42 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DOD_CVARS_H
+#define DOD_CVARS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define MAX_INTERMISSION_TIME 120
+
+extern ConVar mp_limitteams;
+extern ConVar mp_autokick;
+
+extern ConVar mp_clan_restartround;
+extern ConVar mp_clan_readyrestart;
+extern ConVar mp_clan_ready_signal;
+extern ConVar mp_warmup_time;
+extern ConVar mp_combinemglimits;
+extern ConVar mp_restartwarmup;
+extern ConVar mp_cancelwarmup;
+extern ConVar mp_winlimit;
+
+extern ConVar mp_limitAlliesRifleman;
+extern ConVar mp_limitAlliesAssault;
+extern ConVar mp_limitAlliesSupport;
+extern ConVar mp_limitAlliesSniper;
+extern ConVar mp_limitAlliesMachinegun;
+extern ConVar mp_limitAlliesRocket;
+
+extern ConVar mp_limitAxisRifleman;
+extern ConVar mp_limitAxisAssault;
+extern ConVar mp_limitAxisSupport;
+extern ConVar mp_limitAxisSniper;
+extern ConVar mp_limitAxisMachinegun;
+extern ConVar mp_limitAxisRocket;
+
+#endif //DOD_CVARS_H \ No newline at end of file
diff --git a/game/server/dod/dod_eventlog.cpp b/game/server/dod/dod_eventlog.cpp
new file mode 100644
index 0000000..0434b05
--- /dev/null
+++ b/game/server/dod/dod_eventlog.cpp
@@ -0,0 +1,547 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "../EventLog.h"
+#include "team.h"
+#include "KeyValues.h"
+#include "dod_shareddefs.h"
+#include "dod_team.h"
+
+#define LOG_DETAIL_ENEMY_ATTACKS 1
+#define LOG_DETAIL_TEAMMATE_ATTACKS 2
+
+ConVar mp_logdetail( "mp_logdetail", "0", FCVAR_NONE, "Logs attacks. Values are: 0=off, 1=enemy, 2=teammate, 3=both)", true, 0.0f, true, 3.0f );
+
+class CDODEventLog : public CEventLog
+{
+private:
+ typedef CEventLog BaseClass;
+
+public:
+ bool PrintEvent( IGameEvent * event ) // override virtual function
+ {
+ if ( !PrintDodEvent( event ) ) // allow DOD to override logging
+ {
+ return BaseClass::PrintEvent( event );
+ }
+ else
+ {
+ return true;
+ }
+ }
+
+ bool Init()
+ {
+ BaseClass::Init();
+
+ ListenForGameEvent( "player_death" );
+ ListenForGameEvent( "player_hurt" );
+ ListenForGameEvent( "player_changeclass" );
+ ListenForGameEvent( "dod_warmup_begins" );
+ ListenForGameEvent( "dod_warmup_ends" );
+ ListenForGameEvent( "dod_round_start" );
+ ListenForGameEvent( "dod_restart_round" );
+ ListenForGameEvent( "dod_ready_restart" );
+ ListenForGameEvent( "dod_allies_ready" );
+ ListenForGameEvent( "dod_axis_ready" );
+ ListenForGameEvent( "dod_round_restart_seconds" );
+ ListenForGameEvent( "dod_team_scores" );
+ ListenForGameEvent( "dod_round_win" );
+ ListenForGameEvent( "dod_tick_points" );
+ ListenForGameEvent( "dod_game_over" );
+ ListenForGameEvent( "dod_point_captured" );
+ ListenForGameEvent( "dod_capture_blocked" );
+ ListenForGameEvent( "dod_bomb_planted" );
+ ListenForGameEvent( "dod_bomb_exploded" );
+ ListenForGameEvent( "dod_bomb_defused" );
+ ListenForGameEvent( "dod_kill_planter" );
+ ListenForGameEvent( "dod_kill_defuser" );
+
+ return true;
+ }
+
+protected:
+
+ bool PrintDodEvent( IGameEvent * event ) // print Mod specific logs
+ {
+ const char *eventName = event->GetName();
+
+ if ( !Q_strncmp( eventName, "server_", strlen("server_")) )
+ {
+ return false; // ignore server_ messages
+ }
+
+ if ( FStrEq( eventName, "player_death" ) )
+ {
+ const int userid = event->GetInt( "userid" );
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( userid );
+
+ if ( !pPlayer )
+ {
+ return false;
+ }
+
+ const int attackerid = event->GetInt("attacker" );
+ const char *weapon = event->GetString( "weapon" );
+ CBasePlayer *pAttacker = UTIL_PlayerByUserId( attackerid );
+
+ if ( pPlayer == pAttacker )
+ {
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" committed suicide with \"%s\"\n",
+ pPlayer->GetPlayerName(),
+ userid,
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName(),
+ weapon
+ );
+ }
+ else if ( pAttacker )
+ {
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" killed \"%s<%i><%s><%s>\" with \"%s\"\n",
+ pAttacker->GetPlayerName(),
+ attackerid,
+ pAttacker->GetNetworkIDString(),
+ pAttacker->GetTeam()->GetName(),
+ pPlayer->GetPlayerName(),
+ userid,
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName(),
+ weapon
+ );
+ }
+ else
+ {
+ // killed by the world
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" committed suicide with \"world\"\n",
+ pPlayer->GetPlayerName(),
+ userid,
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName()
+ );
+ }
+
+ // Domination and Revenge
+ // pAttacker //int attackerid = engine->GetPlayerForUserID( event->GetInt( "attacker" ) );
+ // pPlayer //int userid = engine->GetPlayerForUserID( event->GetInt( "userid" ) );
+
+ if ( event->GetInt( "dominated" ) > 0 && pAttacker )
+ {
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"domination\" against \"%s<%i><%s><%s>\"\n",
+ pAttacker->GetPlayerName(),
+ attackerid,
+ pAttacker->GetNetworkIDString(),
+ pAttacker->GetTeam()->GetName(),
+ pPlayer->GetPlayerName(),
+ userid,
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName()
+ );
+ }
+ if ( event->GetInt( "revenge" ) > 0 && pAttacker )
+ {
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"revenge\" against \"%s<%i><%s><%s>\"\n",
+ pAttacker->GetPlayerName(),
+ attackerid,
+ pAttacker->GetNetworkIDString(),
+ pAttacker->GetTeam()->GetName(),
+ pPlayer->GetPlayerName(),
+ userid,
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName()
+ );
+ }
+
+ return true;
+ }
+ else if ( FStrEq( eventName, "player_hurt" ) )
+ {
+ const int userid = event->GetInt( "userid" );
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( userid );
+
+ if ( !pPlayer )
+ {
+ return false;
+ }
+
+ const int attackerid = event->GetInt("attacker" );
+ const char *weapon = event->GetString( "weapon" );
+ CBasePlayer *pAttacker = UTIL_PlayerByUserId( attackerid );
+ if ( !pAttacker )
+ {
+ return false;
+ }
+
+ bool isTeamAttack = ( (pPlayer->GetTeamNumber() == pAttacker->GetTeamNumber() ) && (pPlayer != pAttacker) );
+ int detail = mp_logdetail.GetInt();
+ if ( ( isTeamAttack && ( detail & LOG_DETAIL_TEAMMATE_ATTACKS ) ) ||
+ ( !isTeamAttack && ( detail & LOG_DETAIL_ENEMY_ATTACKS ) ) )
+ {
+ int hitgroup = event->GetInt( "hitgroup" );
+ const char *hitgroupStr = "GENERIC";
+ switch ( hitgroup )
+ {
+ case HITGROUP_GENERIC:
+ hitgroupStr = "generic";
+ break;
+ case HITGROUP_HEAD:
+ hitgroupStr = "head";
+ break;
+ case HITGROUP_CHEST:
+ hitgroupStr = "chest";
+ break;
+ case HITGROUP_STOMACH:
+ hitgroupStr = "stomach";
+ break;
+ case HITGROUP_LEFTARM:
+ hitgroupStr = "left arm";
+ break;
+ case HITGROUP_RIGHTARM:
+ hitgroupStr = "right arm";
+ break;
+ case HITGROUP_LEFTLEG:
+ hitgroupStr = "left leg";
+ break;
+ case HITGROUP_RIGHTLEG:
+ hitgroupStr = "right leg";
+ break;
+ }
+
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" attacked \"%s<%i><%s><%s>\" with \"%s\" (damage \"%d\") (health \"%d\") (hitgroup \"%s\")\n",
+ pAttacker->GetPlayerName(),
+ attackerid,
+ pAttacker->GetNetworkIDString(),
+ pAttacker->GetTeam()->GetName(),
+ pPlayer->GetPlayerName(),
+ userid,
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName(),
+ weapon,
+ event->GetInt( "damage" ),
+ event->GetInt( "health" ),
+ hitgroupStr );
+ }
+ return true;
+ }
+ else if ( FStrEq( eventName, "player_changeclass" ) )
+ {
+ const int userid = event->GetInt( "userid" );
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( userid );
+
+ if ( !pPlayer )
+ {
+ return false;
+ }
+
+ int iClass = event->GetInt("class");
+ int iTeam = pPlayer->GetTeamNumber();
+
+ if ( iTeam != TEAM_ALLIES && iTeam != TEAM_AXIS )
+ return true;
+
+ CDODTeam *pTeam = GetGlobalDODTeam( iTeam );
+
+ if ( iClass == PLAYERCLASS_RANDOM )
+ {
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" changed role to \"Random\"\n",
+ pPlayer->GetPlayerName(),
+ userid,
+ pPlayer->GetNetworkIDString(),
+ pTeam->GetName()
+ );
+ }
+ else if ( iClass < GetGlobalDODTeam(iTeam)->GetNumPlayerClasses() )
+ {
+ const CDODPlayerClassInfo &pInfo = GetGlobalDODTeam(iTeam)->GetPlayerClassInfo( iClass );
+
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" changed role to \"%s\"\n",
+ pPlayer->GetPlayerName(),
+ userid,
+ pPlayer->GetNetworkIDString(),
+ pTeam->GetName(),
+ pInfo.m_szPrintName
+ );
+ }
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_warmup_begins" ) )
+ {
+ UTIL_LogPrintf( "World triggered \"Warmup_Begin\"\n" );
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_warmup_ends" ) )
+ {
+ UTIL_LogPrintf( "World triggered \"Warmup_Ends\"\n" );
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_round_start" ) )
+ {
+ UTIL_LogPrintf("World triggered \"Round_Start\"\n");
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_restart_round" ) )
+ {
+ UTIL_LogPrintf("World triggered \"Round_Restart\"\n");
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_ready_restart" ) )
+ {
+ UTIL_LogPrintf( "World triggered \"Ready_Restart_Begin\"\n" );
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_allies_ready" ) )
+ {
+ UTIL_LogPrintf("World triggered \"Allies Ready\"\n");
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_axis_ready" ) )
+ {
+ UTIL_LogPrintf("World triggered \"Axis Ready\"\n");
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_round_restart_seconds" ) )
+ {
+ UTIL_LogPrintf( "World triggered \"Round_Restart_In\" (delay \"%d\")\n", event->GetInt("seconds") );
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_team_scores" ) )
+ {
+ int iAlliesRoundsWon = event->GetInt( "allies_caps" );
+ int iAlliesTickPoints = event->GetInt( "allies_tick" );
+ int iNumAllies = event->GetInt( "allies_players" );
+ int iAxisRoundsWon = event->GetInt( "axis_caps" );
+ int iAxisTickPoints = event->GetInt( "axis_tick" );
+ int iNumAxis = event->GetInt( "axis_players" );
+
+ UTIL_LogPrintf( "Team \"Allies\" triggered \"team_scores\" (roundswon \"%d\") (tickpoints \"%d\") (numplayers \"%d\")\n", iAlliesRoundsWon, iAlliesTickPoints, iNumAllies );
+ UTIL_LogPrintf( "Team \"Allies\" triggered \"team_scores\" (roundswon \"%d\") (tickpoints \"%d\") (numplayers \"%d\")\n", iAxisRoundsWon, iAxisTickPoints, iNumAxis );
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_round_win" ) )
+ {
+ CDODTeam *pTeam = GetGlobalDODTeam( event->GetInt( "team" ) );
+
+ UTIL_LogPrintf( "Team \"%s\" triggered \"round_win\" (rounds_won \"%d\") (numplayers \"%d\")\n",
+ pTeam->GetName(),
+ pTeam->GetRoundsWon(),
+ pTeam->GetNumPlayers() );
+
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_tick_points" ) )
+ {
+ CDODTeam *pTeam = GetGlobalDODTeam( event->GetInt( "team" ) );
+
+ int iScore = event->GetInt( "score" );
+ int iTotalScore = event->GetInt( "totalscore" );
+
+ UTIL_LogPrintf( "Team \"%s\" triggered \"tick_score\" (score \"%i\") (totalscore \"%d\") (numplayers \"%d\")\n",
+ pTeam->GetName(),
+ iScore,
+ iTotalScore,
+ pTeam->GetNumPlayers() );
+
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_game_over" ) )
+ {
+ UTIL_LogPrintf( "World triggered \"Game_Over\" reason \"%s\"\n", event->GetString( "reason" ) );
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_point_captured" ) )
+ {
+ // identifier of the area "cp" "cpname"
+ int iPointIndex = event->GetInt( "cp" );
+ const char *szPointName = event->GetString( "cpname" );
+
+ const char *szCappers = event->GetString( "cappers" );
+
+ int iNumCappers = Q_strlen( szCappers );
+
+ if ( iNumCappers <= 0 )
+ return true;
+
+ //"Team "Allies" captured location "<3><#map_flag_2nd_axis>" with "2"
+ // players (1 "Matt<UID><STEAMID><TEAM>") (2 "Player2<2><STEAMID><TEAM>")
+
+ char buf[512];
+
+ for ( int i=0;i<iNumCappers;i++ )
+ {
+ int iPlayerIndex = szCappers[i];
+
+ Assert( iPlayerIndex != '\0' && iPlayerIndex > 0 && iPlayerIndex < MAX_PLAYERS );
+
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( iPlayerIndex );
+
+ if ( i == 0 )
+ {
+ Q_snprintf( buf, sizeof(buf), "Team \"%s\" triggered \"captured_loc\" (flagindex \"%d\") (flagname \"%s\") (numplayers \"%d\") ",
+ pPlayer->GetTeam()->GetName(),
+ iPointIndex,
+ szPointName,
+ iNumCappers );
+ }
+
+ char playerBuf[256];
+ Q_snprintf( playerBuf, sizeof(playerBuf), "(player \"%s<%i><%s><%s>\") ",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName() );
+
+ Q_strncat( buf, playerBuf, sizeof(buf), COPY_ALL_CHARACTERS );
+ }
+
+ UTIL_LogPrintf( "%s\n", buf );
+ }
+ else if ( FStrEq( eventName, "dod_capture_blocked" ) )
+ {
+ int iPointIndex = event->GetInt( "cp" );
+ const char *szPointName = event->GetString( "cpname" );
+
+ int iBlocker = event->GetInt( "blocker" );
+
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( iBlocker );
+
+ if ( !pPlayer )
+ return false;
+
+ // "Matt<2><UNKNOWN><Allies>" triggered "capblock" (flagindex "2") (flagname "#map_flag_2nd_axis")
+
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"capblock\" (flagindex \"%d\") (flagname \"%s\")\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName(),
+ iPointIndex,
+ szPointName );
+ }
+ else if ( FStrEq( eventName, "dod_bomb_planted" ) )
+ {
+ int iPointIndex = event->GetInt( "cp" );
+ const char *szPointName = event->GetString( "cpname" );
+
+ int iPlanter = event->GetInt( "userid" );
+
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( iPlanter );
+
+ if ( !pPlayer )
+ return false;
+
+ // "Matt<2><UNKNOWN><Allies>" triggered "bomb_plant" (flagindex "2") (flagname "#map_flag_2nd_axis")
+
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"bomb_plant\" (flagindex \"%d\") (flagname \"%s\")\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName(),
+ iPointIndex,
+ szPointName );
+
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_bomb_defused" ) )
+ {
+ int iPointIndex = event->GetInt( "cp" );
+ const char *szPointName = event->GetString( "cpname" );
+
+ int iDefuser = event->GetInt( "userid" );
+
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( iDefuser );
+
+ if ( !pPlayer )
+ return false;
+
+ // "Matt<2><UNKNOWN><Allies>" triggered "bomb_defuse" (flagindex "2") (flagname "#map_flag_2nd_axis")
+
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"bomb_defuse\" (flagindex \"%d\") (flagname \"%s\")\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName(),
+ iPointIndex,
+ szPointName );
+
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_kill_planter" ) )
+ {
+ int iKiller = event->GetInt( "userid" );
+
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( iKiller );
+
+ if ( !pPlayer )
+ return false;
+
+ int iVictim = event->GetInt( "victimid" );
+
+ CBasePlayer *pVictim = UTIL_PlayerByUserId( iVictim );
+
+ if ( !pVictim )
+ return false;
+
+ // "Matt<2><UNKNOWN><Allies>" triggered "kill_planter" against "Fred<3><UNKNOWN><Axis>"
+
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"kill_planter\" against \"%s<%i><%s><%s>\"\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName(),
+ pVictim->GetPlayerName(),
+ pVictim->GetUserID(),
+ pVictim->GetNetworkIDString(),
+ pVictim->GetTeam()->GetName() );
+
+ return true;
+ }
+ else if ( FStrEq( eventName, "dod_kill_defuser" ) )
+ {
+ int iKiller = event->GetInt( "userid" );
+
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( iKiller );
+
+ if ( !pPlayer )
+ return false;
+
+ int iVictim = event->GetInt( "victimid" );
+
+ CBasePlayer *pVictim = UTIL_PlayerByUserId( iVictim );
+
+ if ( !pVictim )
+ return false;
+
+ // "Matt<2><UNKNOWN><Allies>" triggered "kill_defuser" against "Fred<3><UNKNOWN><Axis>"
+
+ UTIL_LogPrintf( "\"%s<%i><%s><%s>\" triggered \"kill_defuser\" against \"%s<%i><%s><%s>\"\n",
+ pPlayer->GetPlayerName(),
+ pPlayer->GetUserID(),
+ pPlayer->GetNetworkIDString(),
+ pPlayer->GetTeam()->GetName(),
+ pVictim->GetPlayerName(),
+ pVictim->GetUserID(),
+ pVictim->GetNetworkIDString(),
+ pVictim->GetTeam()->GetName() );
+
+ return true;
+ }
+
+ return false;
+ }
+
+};
+
+CDODEventLog g_DODEventLog;
+
+//-----------------------------------------------------------------------------
+// Singleton access
+//-----------------------------------------------------------------------------
+IGameSystem* GameLogSystem()
+{
+ return &g_DODEventLog;
+}
+
diff --git a/game/server/dod/dod_gameinterface.cpp b/game/server/dod/dod_gameinterface.cpp
new file mode 100644
index 0000000..1d322be
--- /dev/null
+++ b/game/server/dod/dod_gameinterface.cpp
@@ -0,0 +1,30 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "gameinterface.h"
+#include "mapentities.h"
+#include "dod_gameinterface.h"
+
+// -------------------------------------------------------------------------------------------- //
+// Mod-specific CServerGameClients implementation.
+// -------------------------------------------------------------------------------------------- //
+
+void CServerGameClients::GetPlayerLimits( int& minplayers, int& maxplayers, int &defaultMaxPlayers ) const
+{
+ minplayers = 2; // Force multiplayer.
+ maxplayers = MAX_PLAYERS;
+ defaultMaxPlayers = 32;
+}
+
+// -------------------------------------------------------------------------------------------- //
+// Mod-specific CServerGameDLL implementation.
+// -------------------------------------------------------------------------------------------- //
+
+void CServerGameDLL::LevelInit_ParseAllEntities( const char *pMapEntities )
+{
+}
+
diff --git a/game/server/dod/dod_gameinterface.h b/game/server/dod/dod_gameinterface.h
new file mode 100644
index 0000000..8ca4c93
--- /dev/null
+++ b/game/server/dod/dod_gameinterface.h
@@ -0,0 +1,15 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_GAMEINTERFACE_H
+#define DOD_GAMEINTERFACE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "gameinterface.h"
+
+#endif // DOD_GAMEINTERFACE_H \ No newline at end of file
diff --git a/game/server/dod/dod_gamestats.cpp b/game/server/dod/dod_gamestats.cpp
new file mode 100644
index 0000000..e692e04
--- /dev/null
+++ b/game/server/dod/dod_gamestats.cpp
@@ -0,0 +1,150 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: dod game stats
+//
+// $NoKeywords: $
+//=============================================================================//
+
+// Some tricky business here - we don't want to include the precompiled header for the statreader
+// and trying to #ifdef it out does funky things like ignoring the #endif. Define our header file
+// separately and include it based on the switch
+
+#include "cbase.h"
+
+#ifdef GAME_DLL
+ #include "weapon_dodbase.h"
+#endif
+
+#include <tier0/platform.h>
+#include "dod_gamestats.h"
+
+int iDistanceStatWeapons[DOD_NUM_DISTANCE_STAT_WEAPONS] =
+{
+ WEAPON_COLT,
+ WEAPON_P38,
+ WEAPON_C96,
+ WEAPON_GARAND,
+ WEAPON_GARAND_ZOOMED,
+ WEAPON_M1CARBINE,
+ WEAPON_K98,
+ WEAPON_K98_ZOOMED,
+ WEAPON_SPRING,
+ WEAPON_SPRING_ZOOMED,
+ WEAPON_K98_SCOPED,
+ WEAPON_K98_SCOPED_ZOOMED,
+ WEAPON_THOMPSON,
+ WEAPON_MP40,
+ WEAPON_MP44,
+ WEAPON_MP44_SEMIAUTO,
+ WEAPON_BAR,
+ WEAPON_BAR_SEMIAUTO,
+ WEAPON_30CAL,
+ WEAPON_30CAL_UNDEPLOYED,
+ WEAPON_MG42,
+ WEAPON_MG42_UNDEPLOYED,
+};
+
+// Send hit/shots only for the following weapons
+int iNoDistStatWeapons[DOD_NUM_NODIST_STAT_WEAPONS] =
+{
+ WEAPON_AMERKNIFE,
+ WEAPON_SPADE,
+ WEAPON_BAZOOKA,
+ WEAPON_PSCHRECK,
+ WEAPON_FRAG_US,
+ WEAPON_FRAG_GER,
+ WEAPON_FRAG_US_LIVE,
+ WEAPON_FRAG_GER_LIVE,
+ WEAPON_RIFLEGREN_US,
+ WEAPON_RIFLEGREN_GER,
+ WEAPON_RIFLEGREN_US_LIVE,
+ WEAPON_RIFLEGREN_GER_LIVE,
+ WEAPON_THOMPSON_PUNCH,
+ WEAPON_MP40_PUNCH,
+};
+
+int iWeaponBucketDistances[DOD_NUM_WEAPON_DISTANCE_BUCKETS-1] =
+{
+ 50,
+ 150,
+ 300,
+ 450,
+ 700,
+ 1000,
+ 1300,
+ 1600,
+ 2000
+};
+
+#ifndef GAME_DLL
+
+ const char * s_WeaponAliasInfo[] =
+ {
+ "none", // WEAPON_NONE = 0,
+
+ //Melee
+ "amerknife", //WEAPON_AMERKNIFE,
+ "spade", //WEAPON_SPADE,
+
+ //Pistols
+ "colt", //WEAPON_COLT,
+ "p38", //WEAPON_P38,
+ "c96", //WEAPON_C96
+
+ //Rifles
+ "garand", //WEAPON_GARAND,
+ "m1carbine", //WEAPON_M1CARBINE,
+ "k98", //WEAPON_K98,
+
+ //Sniper Rifles
+ "spring", //WEAPON_SPRING,
+ "k98_scoped", //WEAPON_K98_SCOPED,
+
+ //SMG
+ "thompson", //WEAPON_THOMPSON,
+ "mp40", //WEAPON_MP40,
+ "mp44", //WEAPON_MP44,
+ "bar", //WEAPON_BAR,
+
+ //Machine guns
+ "30cal", //WEAPON_30CAL,
+ "mg42", //WEAPON_MG42,
+
+ //Rocket weapons
+ "bazooka", //WEAPON_BAZOOKA,
+ "pschreck", //WEAPON_PSCHRECK,
+
+ //Grenades
+ "frag_us", //WEAPON_FRAG_US,
+ "frag_ger", //WEAPON_FRAG_GER,
+
+ "frag_us_live", //WEAPON_FRAG_US_LIVE
+ "frag_ger_live", //WEAPON_FRAG_GER_LIVE
+
+ "smoke_us", //WEAPON_SMOKE_US
+ "smoke_ger", //WEAPON_SMOKE_GER
+
+ "riflegren_us", //WEAPON_RIFLEGREN_US
+ "riflegren_ger", //WEAPON_RIFLEGREN_GER
+
+ "riflegren_us_live", //WEAPON_RIFLEGREN_US_LIVE
+ "riflegren_ger_live", //WEAPON_RIFLEGREN_GER_LIVE
+
+ // not actually separate weapons, but defines used in stats recording
+ "thompson_punch", //WEAPON_THOMPSON_PUNCH
+ "mp40_punch", //WEAPON_MP40_PUNCH
+ "garand_zoomed", //WEAPON_GARAND_ZOOMED,
+
+ "k98_zoomed", //WEAPON_K98_ZOOMED
+ "spring_zoomed", //WEAPON_SPRING_ZOOMED
+ "k98_scoped_zoomed", //WEAPON_K98_SCOPED_ZOOMED
+
+ "30cal_undeployed", //WEAPON_30CAL_UNDEPLOYED,
+ "mg42_undeployed", //WEAPON_MG42_UNDEPLOYED,
+
+ "bar_semiauto", //WEAPON_BAR_SEMIAUTO,
+ "mp44_semiauto", //WEAPON_MP44_SEMIAUTO,
+
+ 0, // end of list marker
+ };
+#endif \ No newline at end of file
diff --git a/game/server/dod/dod_gamestats.h b/game/server/dod/dod_gamestats.h
new file mode 100644
index 0000000..c8daccc
--- /dev/null
+++ b/game/server/dod/dod_gamestats.h
@@ -0,0 +1,175 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: The dod game stats header
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DOD_GAMESTATS_H
+#define DOD_GAMESTATS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+// Redefine some things for the stat reader so it doesn't have to include weapon_dodbase.h
+#ifndef GAME_DLL
+
+ typedef enum
+ {
+ WEAPON_NONE = 0,
+
+ //Melee
+ WEAPON_AMERKNIFE,
+ WEAPON_SPADE,
+
+ //Pistols
+ WEAPON_COLT,
+ WEAPON_P38,
+ WEAPON_C96,
+
+ //Rifles
+ WEAPON_GARAND,
+ WEAPON_M1CARBINE,
+ WEAPON_K98,
+
+ //Sniper Rifles
+ WEAPON_SPRING,
+ WEAPON_K98_SCOPED,
+
+ //SMG
+ WEAPON_THOMPSON,
+ WEAPON_MP40,
+ WEAPON_MP44,
+ WEAPON_BAR,
+
+ //Machine guns
+ WEAPON_30CAL,
+ WEAPON_MG42,
+
+ //Rocket weapons
+ WEAPON_BAZOOKA,
+ WEAPON_PSCHRECK,
+
+ //Grenades
+ WEAPON_FRAG_US,
+ WEAPON_FRAG_GER,
+
+ WEAPON_FRAG_US_LIVE,
+ WEAPON_FRAG_GER_LIVE,
+
+ WEAPON_SMOKE_US,
+ WEAPON_SMOKE_GER,
+
+ WEAPON_RIFLEGREN_US,
+ WEAPON_RIFLEGREN_GER,
+
+ WEAPON_RIFLEGREN_US_LIVE,
+ WEAPON_RIFLEGREN_GER_LIVE,
+
+ // not actually separate weapons, but defines used in stats recording
+ // find a better way to do this without polluting the list of actual weapons.
+ WEAPON_THOMPSON_PUNCH,
+ WEAPON_MP40_PUNCH,
+
+ WEAPON_GARAND_ZOOMED,
+ WEAPON_K98_ZOOMED,
+ WEAPON_SPRING_ZOOMED,
+ WEAPON_K98_SCOPED_ZOOMED,
+
+ WEAPON_30CAL_UNDEPLOYED,
+ WEAPON_MG42_UNDEPLOYED,
+
+ WEAPON_BAR_SEMIAUTO,
+ WEAPON_MP44_SEMIAUTO,
+
+ WEAPON_MAX, // number of weapons weapon index
+
+ } DODWeaponID;
+
+#endif // ndef WEAPON_NONE
+
+#define DOD_STATS_BLOB_VERSION 2 // changed to 2 for the orange box beta
+
+#define DOD_NUM_DISTANCE_STAT_WEAPONS 22
+#define DOD_NUM_NODIST_STAT_WEAPONS 14
+#define DOD_NUM_WEAPON_DISTANCE_BUCKETS 10
+
+extern int iDistanceStatWeapons[DOD_NUM_DISTANCE_STAT_WEAPONS];
+extern int iNoDistStatWeapons[DOD_NUM_NODIST_STAT_WEAPONS];
+extern int iWeaponBucketDistances[DOD_NUM_WEAPON_DISTANCE_BUCKETS-1];
+
+#ifndef GAME_DLL
+ extern const char * s_WeaponAliasInfo[];
+#endif
+
+typedef struct
+{
+ char szGameName[8];
+ byte iVersion;
+ char szMapName[32];
+ char ipAddr[4];
+ short port;
+ int serverid;
+} gamestats_header_t;
+
+// Stats for bullet weapons - includes distance of hits
+typedef struct
+{
+ short iNumAttacks; // times fired
+ short iNumHits; // times hit
+
+ // distance buckets - distances are defined per-weapon ( 0 is closest, buckets-1 farthest )
+ short iDistanceBuckets[DOD_NUM_WEAPON_DISTANCE_BUCKETS];
+
+} dod_gamestats_weapon_distance_t;
+
+// Stats for non-bullet weapons
+typedef struct
+{
+ short iNumAttacks; // times fired
+ short iNumHits; // times hit
+} dod_gamestats_weapon_nodist_t;
+
+typedef struct
+{
+ gamestats_header_t header;
+
+ // Team Scores
+ byte iNumAlliesWins;
+ byte iNumAxisWins;
+
+ short iAlliesTickPoints;
+ short iAxisTickPoints;
+
+ short iMinutesPlayed; // time spent on the map rotation
+
+ // Player Data
+ short iMinutesPlayedPerClass_Allies[7]; // includes random
+ short iMinutesPlayedPerClass_Axis[7]; // includes random
+
+ short iKillsPerClass_Allies[6];
+ short iKillsPerClass_Axis[6];
+
+ short iSpawnsPerClass_Allies[6];
+ short iSpawnsPerClass_Axis[6];
+
+ short iCapsPerClass_Allies[6];
+ short iCapsPerClass_Axis[6];
+
+ byte iDefensesPerClass_Allies[6];
+ byte iDefensesPerClass_Axis[6];
+
+ // Server Settings
+ // assume these class limits don't change through the course of the map
+ byte iClassLimits_Allies[6];
+ byte iClassLimits_Axis[6];
+
+ // Weapon Data
+ dod_gamestats_weapon_distance_t weaponStatsDistance[DOD_NUM_DISTANCE_STAT_WEAPONS]; // 14 * 22 = 308 bytes
+ dod_gamestats_weapon_nodist_t weaponStats[DOD_NUM_NODIST_STAT_WEAPONS]; // 4 * 14 = 56 bytes
+
+ // how many times a weapon was picked up ?
+
+} dod_gamestats_t;
+
+#endif // DOD_GAMESTATS_H \ No newline at end of file
diff --git a/game/server/dod/dod_handgrenade.cpp b/game/server/dod/dod_handgrenade.cpp
new file mode 100644
index 0000000..c24c41e
--- /dev/null
+++ b/game/server/dod/dod_handgrenade.cpp
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_handgrenade.h"
+#include "dod_shareddefs.h"
+
+#define GRENADE_MODEL "models/Weapons/w_frag.mdl"
+
+LINK_ENTITY_TO_CLASS( grenade_frag_us, CDODHandGrenade );
+PRECACHE_WEAPON_REGISTER( grenade_frag_us );
+
+CDODHandGrenade* CDODHandGrenade::Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer,
+ DODWeaponID weaponID )
+{
+ CDODHandGrenade *pGrenade = (CDODHandGrenade*)CBaseEntity::Create( "grenade_frag_us", position, angles, pOwner );
+
+ Assert( pGrenade );
+
+ if( !pGrenade )
+ return NULL;
+
+ IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->AddVelocity( &velocity, &angVelocity );
+ }
+
+ // Who threw this grenade
+ pGrenade->SetThrower( pOwner );
+
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ // How long until we explode
+ pGrenade->SetDetonateTimerLength( timer );
+
+ // Save the weapon id for stats purposes
+ pGrenade->m_EmitterWeaponID = weaponID;
+
+ return pGrenade;
+}
+
+void CDODHandGrenade::Spawn()
+{
+ SetModel( GRENADE_MODEL );
+ BaseClass::Spawn();
+}
+
+void CDODHandGrenade::Precache()
+{
+ PrecacheModel( GRENADE_MODEL );
+
+ PrecacheScriptSound( "HEGrenade.Bounce" );
+
+ BaseClass::Precache();
+}
+
+void CDODHandGrenade::BounceSound( void )
+{
+ EmitSound( "HEGrenade.Bounce" );
+}
+
+//Pass the classname of the exploding version of this grenade.
+char *CDODHandGrenade::GetExplodingClassname()
+{
+ return "weapon_frag_us_live";
+} \ No newline at end of file
diff --git a/game/server/dod/dod_handgrenade.h b/game/server/dod/dod_handgrenade.h
new file mode 100644
index 0000000..3840725
--- /dev/null
+++ b/game/server/dod/dod_handgrenade.h
@@ -0,0 +1,48 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_HANDGRENADE_H
+#define DOD_HANDGRENADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_basegrenade.h"
+
+class CDODHandGrenade : public CDODBaseGrenade
+{
+public:
+ DECLARE_CLASS( CDODHandGrenade, CDODBaseGrenade );
+
+// Overrides.
+public:
+ CDODHandGrenade() {}
+ virtual void Spawn();
+ virtual void Precache();
+ virtual void BounceSound( void );
+
+// Grenade stuff.
+public:
+
+ static CDODHandGrenade* Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer,
+ DODWeaponID weaponID );
+
+ void SetTimer( float timer );
+
+ virtual char *GetExplodingClassname();
+
+private:
+ float m_flDetonateTime;
+};
+
+
+#endif // DOD_HANDGRENADE_H
diff --git a/game/server/dod/dod_hltvdirector.cpp b/game/server/dod/dod_hltvdirector.cpp
new file mode 100644
index 0000000..20a366d
--- /dev/null
+++ b/game/server/dod/dod_hltvdirector.cpp
@@ -0,0 +1,140 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "hltvdirector.h"
+
+class CDODHLTVDirector : public CHLTVDirector
+{
+public:
+ DECLARE_CLASS( CDODHLTVDirector, CHLTVDirector );
+
+ const char** GetModEvents();
+ void SetHLTVServer( IHLTVServer *hltv );
+ void CreateShotFromEvent( CHLTVGameEvent *event );
+};
+
+void CDODHLTVDirector::SetHLTVServer( IHLTVServer *hltv )
+{
+ BaseClass::SetHLTVServer( hltv );
+
+ if ( m_pHLTVServer )
+ {
+ // mod specific events the director uses to find interesting shots
+ ListenForGameEvent( "dod_point_captured" );
+ ListenForGameEvent( "dod_capture_blocked" );
+ }
+}
+
+void CDODHLTVDirector::CreateShotFromEvent( CHLTVGameEvent *event )
+{
+ // show event at least for 2 more seconds after it occured
+ const char *name = event->m_Event->GetName();
+ IGameEvent *shot = NULL;
+
+ if ( !Q_strcmp( "dod_point_captured", name ) ||
+ !Q_strcmp( "dod_capture_blocked", name ) )
+ {
+ // try to find a capper or blocker
+ const char *text = event->m_Event->GetString("blocker");
+ int playerIndex = text[0];
+
+ if ( playerIndex < 1 )
+ {
+ // maybe its a capper ?
+ text = event->m_Event->GetString("cappers");
+ playerIndex = text[0];
+ }
+
+ // if we found one, show him
+ if ( playerIndex > 0 )
+ {
+ // shot player as primary, hostage as secondary target
+ shot = gameeventmanager->CreateEvent( "hltv_chase", true );
+ shot->SetInt( "target1", playerIndex );
+ shot->SetInt( "target2", 0 );
+ shot->SetFloat( "distance", 96.0f );
+ shot->SetInt( "theta", 0 );
+ shot->SetInt( "phi", 20 );
+
+ // shot 2 seconds after event
+ m_nNextShotTick = MIN( m_nNextShotTick, (event->m_Tick+TIME_TO_TICKS(2.0)) );
+ m_iPVSEntity = playerIndex;
+ }
+ }
+ else
+ {
+ // let baseclass create a shot
+ BaseClass::CreateShotFromEvent( event );
+
+ return;
+ }
+
+ if ( shot )
+ {
+ m_pHLTVServer->BroadcastEvent( shot );
+ gameeventmanager->FreeEvent( shot );
+ DevMsg("DrcCmd: %s\n", name );
+ }
+}
+
+const char** CDODHLTVDirector::GetModEvents()
+{
+ // game events relayed to spectator clients
+ static const char *s_modevents[] =
+ {
+ "hltv_status",
+ "hltv_chat",
+ "player_connect",
+ "player_disconnect",
+ "player_team",
+ "player_info",
+ "server_cvar",
+ "player_death",
+ "player_chat",
+ "round_start",
+ "round_end",
+ // additional DoD:S events:
+ "player_changeclass",
+ "dod_warmup_begins",
+ "dod_warmup_ends",
+ "dod_round_start",
+ "dod_restart_round",
+ "dod_ready_restart",
+ "dod_allies_ready",
+ "dod_axis_ready",
+ "dod_round_restart_seconds",
+ "dod_team_scores",
+ "dod_round_win",
+ "dod_tick_points",
+ "dod_game_over",
+ "dod_broadcast_audio",
+ "dod_point_captured",
+ "dod_capture_blocked",
+ "dod_top_cappers",
+ "dod_timer_flash",
+ "dod_bomb_planted",
+ NULL
+ };
+
+ return s_modevents;
+}
+
+static CDODHLTVDirector s_HLTVDirector; // singleton
+
+EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CHLTVDirector, IHLTVDirector, INTERFACEVERSION_HLTVDIRECTOR, s_HLTVDirector );
+
+CHLTVDirector* HLTVDirector()
+{
+ return &s_HLTVDirector;
+}
+
+IGameSystem* HLTVDirectorSystem()
+{
+ return &s_HLTVDirector;
+} \ No newline at end of file
diff --git a/game/server/dod/dod_location.cpp b/game/server/dod/dod_location.cpp
new file mode 100644
index 0000000..84d42e2
--- /dev/null
+++ b/game/server/dod/dod_location.cpp
@@ -0,0 +1,29 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_location.h"
+
+#include "tier0/memdbgon.h"
+
+LINK_ENTITY_TO_CLASS( dod_location, CDODLocation );
+
+void CDODLocation::Spawn( void )
+{
+ BaseClass::Spawn();
+}
+
+bool CDODLocation::KeyValue( const char *szKeyName, const char *szValue )
+{
+ if (FStrEq(szKeyName, "location_name")) //name of this location
+ {
+ Q_strncpy( m_szLocationName, szValue, sizeof(m_szLocationName) );
+ }
+ else
+ return CBaseEntity::KeyValue( szKeyName, szValue );
+
+ return true;
+} \ No newline at end of file
diff --git a/game/server/dod/dod_location.h b/game/server/dod/dod_location.h
new file mode 100644
index 0000000..7795e90
--- /dev/null
+++ b/game/server/dod/dod_location.h
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================
+
+#ifndef DOD_LOCATION_H
+#define DOD_LOCATION_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "baseentity.h"
+
+//a location on the map that players can refernce in their say messages
+//with the %l escape sequence
+class CDODLocation : public CBaseEntity
+{
+public:
+ DECLARE_CLASS( CDODLocation, CBaseEntity );
+ CDODLocation()
+ {
+ m_szLocationName[0] = '\0';
+ }
+
+ virtual void Spawn( void );
+ virtual bool KeyValue( const char *szKeyName, const char *szValue );
+
+ inline const char *GetName( void ) { return m_szLocationName; }
+
+private:
+ char m_szLocationName[64];
+
+private:
+ CDODLocation( const CDODLocation & );
+};
+
+#endif //DOD_LOCATION_H \ No newline at end of file
diff --git a/game/server/dod/dod_objective_resource.cpp b/game/server/dod/dod_objective_resource.cpp
new file mode 100644
index 0000000..c2e0825
--- /dev/null
+++ b/game/server/dod/dod_objective_resource.cpp
@@ -0,0 +1,226 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Entity that propagates general data needed by clients for every player.
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "dod_objective_resource.h"
+#include "shareddefs.h"
+#include <coordsize.h>
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+// Datatable
+IMPLEMENT_SERVERCLASS_ST_NOBASE(CDODObjectiveResource, DT_DODObjectiveResource)
+
+ SendPropInt( SENDINFO(m_iNumControlPoints), 4, SPROP_UNSIGNED ),
+
+ // data variables
+ SendPropArray( SendPropVector( SENDINFO_ARRAY(m_vCPPositions), -1, SPROP_COORD), m_vCPPositions ),
+ SendPropArray3( SENDINFO_ARRAY3(m_bCPIsVisible), SendPropInt( SENDINFO_ARRAY(m_bCPIsVisible), 1, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iAlliesIcons), SendPropInt( SENDINFO_ARRAY(m_iAlliesIcons), 8, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iAxisIcons), SendPropInt( SENDINFO_ARRAY(m_iAxisIcons), 8, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iNeutralIcons), SendPropInt( SENDINFO_ARRAY(m_iNeutralIcons), 8, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iTimerCapIcons), SendPropInt( SENDINFO_ARRAY(m_iTimerCapIcons), 8, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iBombedIcons), SendPropInt( SENDINFO_ARRAY(m_iBombedIcons), 8, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iAlliesReqCappers), SendPropInt( SENDINFO_ARRAY(m_iAlliesReqCappers), 4, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iAxisReqCappers), SendPropInt( SENDINFO_ARRAY(m_iAxisReqCappers), 4, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_flAlliesCapTime), SendPropTime( SENDINFO_ARRAY(m_flAlliesCapTime) ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_flAxisCapTime), SendPropTime( SENDINFO_ARRAY(m_flAxisCapTime) ) ),
+
+ SendPropArray3( SENDINFO_ARRAY3(m_bBombPlanted), SendPropInt( SENDINFO_ARRAY(m_bBombPlanted), 1, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iBombsRequired), SendPropInt( SENDINFO_ARRAY(m_iBombsRequired), 2, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iBombsRemaining), SendPropInt( SENDINFO_ARRAY(m_iBombsRemaining), 2, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_bBombBeingDefused), SendPropInt( SENDINFO_ARRAY(m_bBombBeingDefused), 1, SPROP_UNSIGNED ) ),
+
+ // state variables
+ SendPropArray3( SENDINFO_ARRAY3(m_iNumAllies), SendPropInt( SENDINFO_ARRAY(m_iNumAllies), 4, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iNumAxis), SendPropInt( SENDINFO_ARRAY(m_iNumAxis), 4, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iCappingTeam), SendPropInt( SENDINFO_ARRAY(m_iCappingTeam), 4, SPROP_UNSIGNED ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iOwner), SendPropInt( SENDINFO_ARRAY(m_iOwner), 4, SPROP_UNSIGNED ) ),
+
+END_SEND_TABLE()
+
+
+BEGIN_DATADESC( CDODObjectiveResource )
+END_DATADESC()
+
+
+LINK_ENTITY_TO_CLASS( dod_objective_resource, CDODObjectiveResource );
+
+CDODObjectiveResource *g_pObjectiveResource;
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODObjectiveResource::Spawn( void )
+{
+ m_iNumControlPoints = 0;
+
+ for ( int i=0; i < MAX_CONTROL_POINTS; i++ )
+ {
+ // data variables
+ m_vCPPositions.Set( i, vec3_origin );
+ m_bCPIsVisible.Set( i, true );
+ m_iAlliesIcons.Set( i, 0 );
+ m_iAxisIcons.Set( i, 0 );
+ m_iNeutralIcons.Set( i, 0 );
+ m_iTimerCapIcons.Set( i, 0 );
+ m_iBombedIcons.Set( i, 0 );
+ m_iAlliesReqCappers.Set( i, 0 );
+ m_iAxisReqCappers.Set( i, 0 );
+ m_flAlliesCapTime.Set( i, 0.0f );
+ m_flAxisCapTime.Set( i, 0.0f );
+ m_bBombPlanted.Set( i, 0 );
+ m_iBombsRequired.Set( i, 0 );
+ m_iBombsRemaining.Set( i, 0 );
+ m_bBombBeingDefused.Set( i, 0 );
+
+ // state variables
+ m_iNumAllies.Set( i, 0 );
+ m_iNumAxis.Set( i, 0 );
+ m_iCappingTeam.Set( i, TEAM_UNASSIGNED );
+ m_iOwner.Set( i, TEAM_UNASSIGNED );
+ }
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: The objective resource is always transmitted to clients
+//-----------------------------------------------------------------------------
+int CDODObjectiveResource::UpdateTransmitState()
+{
+ // ALWAYS transmit to all clients.
+ return SetTransmitState( FL_EDICT_ALWAYS );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Round is starting, reset state
+//-----------------------------------------------------------------------------
+void CDODObjectiveResource::ResetControlPoints( void )
+{
+ for ( int i=0; i < MAX_CONTROL_POINTS; i++ )
+ {
+ m_iNumAllies.Set( i, 0 );
+ m_iNumAxis.Set( i, 0 );
+ m_iCappingTeam.Set( i, TEAM_UNASSIGNED );
+
+ m_bBombPlanted.Set( i, 0 );
+ m_bBombBeingDefused.Set( i, 0 );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Data setting functions
+//-----------------------------------------------------------------------------
+void CDODObjectiveResource::SetNumControlPoints( int num )
+{
+ Assert( num <= MAX_CONTROL_POINTS );
+ m_iNumControlPoints = num;
+}
+
+void CDODObjectiveResource::SetCPIcons( int index, int iAlliesIcon, int iAxisIcon, int iNeutralIcon, int iTimerCapIcon, int iBombedIcon )
+{
+ AssertValidIndex(index);
+ m_iAlliesIcons.Set( index, iAlliesIcon);
+ m_iAxisIcons.Set( index, iAxisIcon );
+ m_iNeutralIcons.Set( index, iNeutralIcon );
+ m_iTimerCapIcons.Set( index, iTimerCapIcon );
+ m_iBombedIcons.Set( index, iBombedIcon );
+}
+
+void CDODObjectiveResource::SetCPPosition( int index, const Vector& vPosition )
+{
+ AssertValidIndex(index);
+ m_vCPPositions.Set( index, vPosition );
+}
+
+void CDODObjectiveResource::SetCPVisible( int index, bool bVisible )
+{
+ AssertValidIndex(index);
+ m_bCPIsVisible.Set( index, bVisible );
+}
+
+void CDODObjectiveResource::SetCPRequiredCappers( int index, int iReqAllies, int iReqAxis )
+{
+ AssertValidIndex(index);
+ m_iAlliesReqCappers.Set( index, iReqAllies );
+ m_iAxisReqCappers.Set( index, iReqAxis );
+}
+
+void CDODObjectiveResource::SetCPCapTime( int index, float flAlliesCapTime, float flAxisCapTime )
+{
+ AssertValidIndex(index);
+ m_flAlliesCapTime.Set( index, flAlliesCapTime );
+ m_flAxisCapTime.Set( index, flAxisCapTime );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Data setting functions
+//-----------------------------------------------------------------------------
+void CDODObjectiveResource::SetNumPlayers( int index, int team, int iNumPlayers )
+{
+ AssertValidIndex(index);
+
+ switch( team )
+ {
+ case TEAM_ALLIES:
+ m_iNumAllies.Set( index, iNumPlayers );
+ break;
+
+ case TEAM_AXIS:
+ m_iNumAxis.Set( index, iNumPlayers );
+ break;
+
+ default:
+ Assert( 0 );
+ break;
+ }
+}
+
+void CDODObjectiveResource::StartCap( int index, int team )
+{
+ AssertValidIndex(index);
+ m_iCappingTeam.Set( index, team );
+}
+
+void CDODObjectiveResource::SetOwningTeam( int index, int team )
+{
+ AssertValidIndex(index);
+ m_iOwner.Set( index, team );
+
+ // clear the capper
+ m_iCappingTeam.Set( index, TEAM_UNASSIGNED );
+}
+
+void CDODObjectiveResource::SetCappingTeam( int index, int team )
+{
+ AssertValidIndex(index);
+ m_iCappingTeam.Set( index, team );
+}
+
+void CDODObjectiveResource::SetBombPlanted( int index, bool bPlanted )
+{
+ AssertValidIndex(index);
+ m_bBombPlanted.Set( index, bPlanted );
+}
+
+void CDODObjectiveResource::SetBombBeingDefused( int index, bool bBeingDefused )
+{
+ AssertValidIndex(index);
+ m_bBombBeingDefused.Set( index, bBeingDefused );
+}
+
+void CDODObjectiveResource::SetBombsRequired( int index, int iBombsRequired )
+{
+ AssertValidIndex(index);
+ m_iBombsRequired.Set( index, iBombsRequired );
+}
+
+void CDODObjectiveResource::SetBombsRemaining( int index, int iBombsRemaining )
+{
+ AssertValidIndex(index);
+ m_iBombsRemaining.Set( index, iBombsRemaining );
+}
diff --git a/game/server/dod/dod_objective_resource.h b/game/server/dod/dod_objective_resource.h
new file mode 100644
index 0000000..f553232
--- /dev/null
+++ b/game/server/dod/dod_objective_resource.h
@@ -0,0 +1,86 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: DOD's objective resource, transmits all objective states to players
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DOD_OBJECTIVE_RESOURCE_H
+#define DOD_OBJECTIVE_RESOURCE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_shareddefs.h"
+
+class CDODObjectiveResource : public CBaseEntity
+{
+ DECLARE_CLASS( CDODObjectiveResource, CBaseEntity );
+public:
+ DECLARE_SERVERCLASS();
+ DECLARE_DATADESC();
+
+ virtual void Spawn( void );
+ virtual int UpdateTransmitState(void);
+
+ void ResetControlPoints( void );
+
+ // Data functions, called to set up the state at the beginning of a round
+ void SetNumControlPoints( int num );
+ void SetCPIcons( int index, int iAlliesIcon, int iAxisIcon, int iNeutralIcon, int iTimerCapIcon, int iBombedIcon );
+ void SetCPPosition( int index, const Vector& vPosition );
+ void SetCPVisible( int index, bool bVisible );
+ void SetCPRequiredCappers( int index, int iReqAllies, int iReqAxis );
+ void SetCPCapTime( int index, float flAlliesCapTime, float flAxisCapTime );
+
+ // State functions, called many times
+ void SetNumPlayers( int index, int team, int iNumPlayers );
+ void StartCap( int index, int team );
+ void SetOwningTeam( int index, int team );
+ void SetCappingTeam( int index, int team );
+ void SetBombPlanted( int index, bool bPlanted );
+ void SetBombsRequired( int index, int iBombsRequired );
+ void SetBombsRemaining( int index, int iBombsRemaining );
+ void SetBombBeingDefused( int index, bool bBeingDefused );
+
+ void AssertValidIndex( int index )
+ {
+ Assert( 0 <= index && index <= MAX_CONTROL_POINTS && index < m_iNumControlPoints );
+ }
+
+private:
+ CNetworkVar( int, m_iNumControlPoints );
+
+ // data variables
+ CNetworkArray( Vector, m_vCPPositions, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_bCPIsVisible, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iAlliesIcons, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iAxisIcons, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iNeutralIcons, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iTimerCapIcons, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iBombedIcons, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iAlliesReqCappers,MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iAxisReqCappers, MAX_CONTROL_POINTS );
+ CNetworkArray( float, m_flAlliesCapTime, MAX_CONTROL_POINTS );
+ CNetworkArray( float, m_flAxisCapTime, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_bBombPlanted, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iBombsRequired, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iBombsRemaining, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_bBombBeingDefused,MAX_CONTROL_POINTS );
+
+ // state variables
+
+ // change when players enter/exit an area
+ CNetworkArray( int, m_iNumAllies, MAX_CONTROL_POINTS );
+ CNetworkArray( int, m_iNumAxis, MAX_CONTROL_POINTS );
+
+ // changes when a cap starts. start and end times are calculated on client
+ CNetworkArray( int, m_iCappingTeam, MAX_CONTROL_POINTS );
+
+ // changes when a point is successfully captured
+ CNetworkArray( int, m_iOwner, MAX_CONTROL_POINTS );
+};
+
+extern CDODObjectiveResource *g_pObjectiveResource;
+
+#endif //DOD_OBJECTIVE_RESOURCE_H \ No newline at end of file
diff --git a/game/server/dod/dod_player.cpp b/game/server/dod/dod_player.cpp
new file mode 100644
index 0000000..9d09a18
--- /dev/null
+++ b/game/server/dod/dod_player.cpp
@@ -0,0 +1,4936 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Player for HL1.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_player.h"
+#include "dod_gamerules.h"
+#include "team.h" //GetGlobalTeam
+#include "in_buttons.h"
+#include "dod_shareddefs.h"
+#include "dod_cvars.h"
+#include "weapon_dodbase.h"
+#include "weapon_dodbasegrenade.h"
+#include "weapon_dodbaserpg.h"
+#include "weapon_dodbipodgun.h"
+#include "dod_basegrenade.h"
+#include "dod_ammo_box.h"
+#include "effect_dispatch_data.h"
+#include "movehelper_server.h"
+#include "tier0/vprof.h"
+#include "te_effect_dispatch.h"
+#include "vphysics/player_controller.h"
+#include <KeyValues.h>
+#include "engine/IEngineSound.h"
+#include "studio.h"
+#include "dod_viewmodel.h"
+#include "info_camera_link.h"
+#include "dod_team.h"
+#include "dod_control_point.h"
+#include "dod_location.h"
+#include "viewport_panel_names.h"
+#include "obstacle_pushaway.h"
+#include "datacache/imdlcache.h"
+#include "bone_setup.h"
+#include "dod_baserocket.h"
+#include "dod_basegrenade.h"
+#include "dod_bombtarget.h"
+#include "collisionutils.h"
+#include "gamestats.h"
+#include "gameinterface.h"
+#include "holiday_gift.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#pragma warning( disable: 4355 ) // disables ' 'this' : used in base member initializer list'
+
+ConVar sv_max_usercmd_future_ticks( "sv_max_usercmd_future_ticks", "8", 0, "Prevents clients from running usercmds too far in the future. Prevents speed hacks." );
+ConVar sv_motd_unload_on_dismissal( "sv_motd_unload_on_dismissal", "0", 0, "If enabled, the MOTD contents will be unloaded when the player closes the MOTD." );
+
+EHANDLE g_pLastAlliesSpawn;
+EHANDLE g_pLastAxisSpawn;
+
+int g_iLastAlliesSpawnIndex = 0;
+int g_iLastAxisSpawnIndex = 0;
+
+ConVar dod_playerstatetransitions( "dod_playerstatetransitions", "-2", FCVAR_CHEAT, "dod_playerstatetransitions <ent index or -1 for all>. Show player state transitions." );
+ConVar dod_debugdamage( "dod_debugdamage", "0", FCVAR_CHEAT );
+
+ConVar mp_bandage_heal_amount( "mp_bandage_heal_amount",
+ "40",
+ FCVAR_GAMEDLL,
+ "How much health to give after a successful bandage\n",
+ true, 0, false, 0 );
+
+ConVar dod_freezecam( "dod_freezecam", "1", FCVAR_REPLICATED | FCVAR_ARCHIVE, "show players a freezecam shot of their killer on death" );
+
+extern ConVar spec_freeze_time;
+extern ConVar spec_freeze_traveltime;
+
+#define DOD_PUSHAWAY_THINK_CONTEXT "DODPushawayThink"
+#define DOD_DEAFEN_CONTEXT "DeafenContext"
+
+int g_iCurrentLifeID = 0;
+
+int GetNextLifeID( void )
+{
+ return ++g_iCurrentLifeID;
+}
+
+// -------------------------------------------------------------------------------- //
+// Classes
+// -------------------------------------------------------------------------------- //
+
+class CPhysicsPlayerCallback : public IPhysicsPlayerControllerEvent
+{
+public:
+ int ShouldMoveTo( IPhysicsObject *pObject, const Vector &position )
+ {
+ CDODPlayer *pPlayer = (CDODPlayer *)pObject->GetGameData();
+ if ( pPlayer )
+ {
+ if ( pPlayer->TouchedPhysics() )
+ {
+ return 0;
+ }
+ }
+ return 1;
+ }
+};
+
+static CPhysicsPlayerCallback playerCallback;
+
+// -------------------------------------------------------------------------------- //
+// Ragdoll entities.
+// -------------------------------------------------------------------------------- //
+
+class CDODRagdoll : public CBaseAnimatingOverlay
+{
+public:
+ DECLARE_CLASS( CDODRagdoll, CBaseAnimatingOverlay );
+ DECLARE_SERVERCLASS();
+
+ // Transmit ragdolls to everyone.
+ virtual int UpdateTransmitState()
+ {
+ return SetTransmitState( FL_EDICT_ALWAYS );
+ }
+
+public:
+ // In case the client has the player entity, we transmit the player index.
+ // In case the client doesn't have it, we transmit the player's model index, origin, and angles
+ // so they can create a ragdoll in the right place.
+ CNetworkHandle( CBaseEntity, m_hPlayer ); // networked entity handle
+ CNetworkVector( m_vecRagdollVelocity );
+ CNetworkVector( m_vecRagdollOrigin );
+};
+
+LINK_ENTITY_TO_CLASS( dod_ragdoll, CDODRagdoll );
+
+IMPLEMENT_SERVERCLASS_ST_NOBASE( CDODRagdoll, DT_DODRagdoll )
+ SendPropVector( SENDINFO(m_vecRagdollOrigin), -1, SPROP_COORD ),
+ SendPropEHandle( SENDINFO( m_hPlayer ) ),
+ SendPropModelIndex( SENDINFO( m_nModelIndex ) ),
+ SendPropInt ( SENDINFO(m_nForceBone), 8, 0 ),
+ SendPropVector ( SENDINFO(m_vecForce), -1, SPROP_NOSCALE ),
+ SendPropVector( SENDINFO( m_vecRagdollVelocity ) )
+END_SEND_TABLE()
+
+
+// -------------------------------------------------------------------------------- //
+// Player animation event. Sent to the client when a player fires, jumps, reloads, etc..
+// -------------------------------------------------------------------------------- //
+
+class CTEPlayerAnimEvent : public CBaseTempEntity
+{
+public:
+ DECLARE_CLASS( CTEPlayerAnimEvent, CBaseTempEntity );
+ DECLARE_SERVERCLASS();
+
+ CTEPlayerAnimEvent( const char *name ) : CBaseTempEntity( name )
+ {
+ }
+
+ CNetworkHandle( CBasePlayer, m_hPlayer );
+ CNetworkVar( int, m_iEvent );
+ CNetworkVar( int, m_nData );
+};
+
+IMPLEMENT_SERVERCLASS_ST_NOBASE( CTEPlayerAnimEvent, DT_TEPlayerAnimEvent )
+ SendPropEHandle( SENDINFO( m_hPlayer ) ),
+ SendPropInt( SENDINFO( m_iEvent ), Q_log2( PLAYERANIMEVENT_COUNT ) + 1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_nData ), 32 )
+END_SEND_TABLE()
+
+static CTEPlayerAnimEvent g_TEPlayerAnimEvent( "PlayerAnimEvent" );
+
+void TE_PlayerAnimEvent( CBasePlayer *pPlayer, PlayerAnimEvent_t event, int nData )
+{
+ CPVSFilter filter( (const Vector&)pPlayer->EyePosition() );
+
+ g_TEPlayerAnimEvent.m_hPlayer = pPlayer;
+ g_TEPlayerAnimEvent.m_iEvent = event;
+ g_TEPlayerAnimEvent.m_nData = nData;
+ g_TEPlayerAnimEvent.Create( filter, 0 );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Filters updates to a variable so that only non-local players see
+// the changes. This is so we can send a low-res origin to non-local players
+// while sending a hi-res one to the local player.
+// Input : *pVarData -
+// *pOut -
+// objectID -
+//-----------------------------------------------------------------------------
+
+void* SendProxy_SendNonLocalDataTable( const SendProp *pProp, const void *pStruct, const void *pVarData, CSendProxyRecipients *pRecipients, int objectID )
+{
+ pRecipients->SetAllRecipients();
+ pRecipients->ClearRecipient( objectID - 1 );
+ return ( void * )pVarData;
+}
+
+// -------------------------------------------------------------------------------- //
+// Tables.
+// -------------------------------------------------------------------------------- //
+
+LINK_ENTITY_TO_CLASS( player, CDODPlayer );
+PRECACHE_REGISTER(player);
+
+// CDODPlayerShared Data Tables
+//=============================
+
+// specific to the local player
+BEGIN_SEND_TABLE_NOBASE( CDODPlayerShared, DT_DODSharedLocalPlayerExclusive )
+ SendPropInt( SENDINFO( m_iPlayerClass), 4 ),
+ SendPropInt( SENDINFO( m_iDesiredPlayerClass ), 4 ),
+ SendPropFloat( SENDINFO( m_flDeployedYawLimitLeft ) ),
+ SendPropFloat( SENDINFO( m_flDeployedYawLimitRight ) ),
+ SendPropInt( SENDINFO( m_iCPIndex ), 4 ),
+ SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominated ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominated ) ) ),
+ SendPropArray3( SENDINFO_ARRAY3( m_bPlayerDominatingMe ), SendPropBool( SENDINFO_ARRAY( m_bPlayerDominatingMe ) ) ),
+END_SEND_TABLE()
+
+// main table
+BEGIN_SEND_TABLE_NOBASE( CDODPlayerShared, DT_DODPlayerShared )
+ SendPropFloat( SENDINFO( m_flStamina ), 0, SPROP_NOSCALE | SPROP_CHANGES_OFTEN ),
+ SendPropTime( SENDINFO( m_flSlowedUntilTime ) ),
+ SendPropBool( SENDINFO( m_bProne ) ),
+ SendPropBool( SENDINFO( m_bIsSprinting ) ),
+ SendPropTime( SENDINFO( m_flGoProneTime ) ),
+ SendPropTime( SENDINFO( m_flUnProneTime ) ),
+ SendPropTime( SENDINFO( m_flDeployChangeTime ) ),
+ SendPropTime( SENDINFO( m_flDeployedHeight ) ),
+ SendPropBool( SENDINFO( m_bPlanting ) ),
+ SendPropBool( SENDINFO( m_bDefusing ) ),
+ SendPropDataTable( "dodsharedlocaldata", 0, &REFERENCE_SEND_TABLE(DT_DODSharedLocalPlayerExclusive), SendProxy_SendLocalDataTable ),
+END_SEND_TABLE()
+
+
+// CDODPlayer Data Tables
+//=========================
+extern void SendProxy_Origin( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID );
+
+// specific to the local player
+BEGIN_SEND_TABLE_NOBASE( CDODPlayer, DT_DODLocalPlayerExclusive )
+ // send a hi-res origin to the local player for use in prediction
+ SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_NOSCALE|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
+ SendPropFloat( SENDINFO(m_flStunDuration), 0, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_flStunMaxAlpha), 0, SPROP_NOSCALE ),
+ SendPropInt( SENDINFO( m_iProgressBarDuration ), 4, SPROP_UNSIGNED ),
+ SendPropFloat( SENDINFO( m_flProgressBarStartTime ), 0, SPROP_NOSCALE ),
+END_SEND_TABLE()
+
+// all players except the local player
+BEGIN_SEND_TABLE_NOBASE( CDODPlayer, DT_DODNonLocalPlayerExclusive )
+ // send a lo-res origin to other players
+ SendPropVector (SENDINFO(m_vecOrigin), -1, SPROP_COORD|SPROP_CHANGES_OFTEN, 0.0f, HIGH_DEFAULT, SendProxy_Origin ),
+END_SEND_TABLE()
+
+// main table
+IMPLEMENT_SERVERCLASS_ST( CDODPlayer, DT_DODPlayer )
+ SendPropExclude( "DT_BaseAnimating", "m_flPoseParameter" ),
+ SendPropExclude( "DT_BaseAnimating", "m_flPlaybackRate" ),
+ SendPropExclude( "DT_BaseAnimating", "m_nSequence" ),
+ SendPropExclude( "DT_BaseAnimating", "m_nNewSequenceParity" ),
+ SendPropExclude( "DT_BaseAnimating", "m_nResetEventsParity" ),
+ SendPropExclude( "DT_BaseEntity", "m_angRotation" ),
+ SendPropExclude( "DT_BaseAnimatingOverlay", "overlay_vars" ),
+
+ // dod_playeranimstate and clientside animation takes care of these on the client
+ SendPropExclude( "DT_ServerAnimationData" , "m_flCycle" ),
+ SendPropExclude( "DT_AnimTimeMustBeFirst" , "m_flAnimTime" ),
+
+ // We need to send a hi-res origin to the local player to avoid prediction errors sliding along walls
+ SendPropExclude( "DT_BaseEntity", "m_vecOrigin" ),
+
+ // Data that only gets sent to the local player.
+ SendPropDataTable( SENDINFO_DT( m_Shared ), &REFERENCE_SEND_TABLE( DT_DODPlayerShared ) ),
+
+ // Data that only gets sent to the local player.
+ SendPropDataTable( "dodlocaldata", 0, &REFERENCE_SEND_TABLE(DT_DODLocalPlayerExclusive), SendProxy_SendLocalDataTable ),
+ SendPropDataTable( "dodnonlocaldata", 0, &REFERENCE_SEND_TABLE(DT_DODNonLocalPlayerExclusive), SendProxy_SendNonLocalDataTable ),
+
+ SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 0), 13, SPROP_CHANGES_OFTEN ),
+ SendPropAngle( SENDINFO_VECTORELEM(m_angEyeAngles, 1), 13, SPROP_CHANGES_OFTEN ),
+ SendPropEHandle( SENDINFO( m_hRagdoll ) ),
+ SendPropBool( SENDINFO( m_bSpawnInterpCounter ) ),
+ SendPropInt( SENDINFO( m_iAchievementAwardsMask ), NUM_ACHIEVEMENT_AWARDS, SPROP_UNSIGNED ),
+END_SEND_TABLE()
+
+BEGIN_DATADESC( CDODPlayer )
+ DEFINE_THINKFUNC( PushawayThink ),
+ DEFINE_THINKFUNC( DeafenThink )
+END_DATADESC()
+
+// -------------------------------------------------------------------------------- //
+
+void cc_CreatePredictionError_f()
+{
+ CBaseEntity *pEnt = CBaseEntity::Instance( 1 );
+ pEnt->SetAbsOrigin( pEnt->GetAbsOrigin() + Vector( 63, 0, 0 ) );
+}
+
+ConCommand cc_CreatePredictionError( "CreatePredictionError", cc_CreatePredictionError_f, "Create a prediction error", FCVAR_CHEAT );
+
+bool PlayerStatLessFunc( const int &lhs, const int &rhs )
+{
+ return lhs < rhs;
+}
+
+// -------------------------------------------------------------------------------- //
+// CDODPlayer implementation.
+// -------------------------------------------------------------------------------- //
+CDODPlayer::CDODPlayer()
+#if !defined(NO_STEAM)
+: m_CallbackGSStatsReceived( this, &CDODPlayer::OnGSStatsReceived )
+#endif
+{
+ m_PlayerAnimState = CreatePlayerAnimState( this );
+ m_Shared.Init( this );
+
+ UseClientSideAnimation();
+ m_angEyeAngles.Init();
+
+ SetViewOffset( DOD_PLAYER_VIEW_OFFSET );
+
+ m_pCurStateInfo = NULL; // no state yet
+ m_lifeState = LIFE_DEAD; // Start "dead".
+
+ m_Shared.SetPlayerClass( PLAYERCLASS_UNDEFINED );
+
+ m_bTeamChanged = false;
+
+ // Clear the signals
+ m_signals.Update();
+ m_fHandleSignalsTime = 0.0f;
+
+ // autokick
+ m_flLastMovement = gpGlobals->curtime;
+
+ m_flNextVoice = gpGlobals->curtime;
+ m_flNextHandSignal = gpGlobals->curtime;
+
+ m_flNextTimeCheck = gpGlobals->curtime;
+
+ m_bAutoReload = true;
+
+ // Init our hint system
+ Hints()->Init( this, NUM_HINTS, g_pszHintMessages );
+ Hints()->RegisterHintTimer( HINT_USE_MELEE, 60 );
+ Hints()->RegisterHintTimer( HINT_USE_ZOOM, 30 );
+ Hints()->RegisterHintTimer( HINT_USE_IRON_SIGHTS, 60 );
+ Hints()->RegisterHintTimer( HINT_USE_SEMI_AUTO, 60 );
+ Hints()->RegisterHintTimer( HINT_USE_SPRINT, 120 );
+ Hints()->RegisterHintTimer( HINT_USE_DEPLOY, 30 );
+ Hints()->RegisterHintTimer( HINT_USE_PRIME, 2 );
+
+ memset( &m_WeaponStats, 0, sizeof(weaponstat_t) );
+
+ m_KilledPlayers.SetLessFunc( PlayerStatLessFunc );
+ m_KilledByPlayers.SetLessFunc( PlayerStatLessFunc );
+
+ m_iNumAreaDefenses = 0;
+ m_iNumAreaCaptures = 0;
+ m_iNumBonusRoundKills = 0;
+
+ memset( &m_flTimePlayedPerClass_Allies, 0, sizeof(m_flTimePlayedPerClass_Allies) );
+ memset( &m_flTimePlayedPerClass_Axis, 0, sizeof(m_flTimePlayedPerClass_Axis) );
+ m_flLastClassChangeTime = gpGlobals->curtime;
+
+ Q_memset( iNumKilledByUnanswered, 0, sizeof( iNumKilledByUnanswered ) );
+
+ m_iAchievementAwardsMask = 0;
+
+ m_hLastDroppedWeapon = NULL;
+ m_hLastDroppedAmmoBox = NULL;
+
+ m_flTimeAsClassAccumulator = 0;
+}
+
+
+CDODPlayer::~CDODPlayer()
+{
+ m_PlayerAnimState->Release();
+}
+
+
+CDODPlayer *CDODPlayer::CreatePlayer( const char *className, edict_t *ed )
+{
+ CDODPlayer::s_PlayerEdict = ed;
+ return (CDODPlayer*)CreateEntityByName( className );
+}
+
+void CDODPlayer::PrecachePlayerModel( const char *szPlayerModel )
+{
+ PrecacheModel( szPlayerModel );
+
+ // Strange numbers, but gotten from the actual max bounds of our
+ // player models
+ const static Vector mins( -13.9, -30.1, -12.1 );
+ const static Vector maxs( 30.9, 30.1, 73.1 );
+
+// Moved to pure_server_minimal.txt
+// engine->ForceModelBounds( szPlayerModel, mins, maxs );
+}
+
+void CDODPlayer::Precache()
+{
+ PrecachePlayerModel( DOD_PLAYERMODEL_AXIS_RIFLEMAN );
+ PrecachePlayerModel( DOD_PLAYERMODEL_AXIS_ASSAULT );
+ PrecachePlayerModel( DOD_PLAYERMODEL_AXIS_SUPPORT );
+ PrecachePlayerModel( DOD_PLAYERMODEL_AXIS_SNIPER );
+ PrecachePlayerModel( DOD_PLAYERMODEL_AXIS_MG );
+ PrecachePlayerModel( DOD_PLAYERMODEL_AXIS_ROCKET );
+
+ PrecachePlayerModel( DOD_PLAYERMODEL_US_RIFLEMAN );
+ PrecachePlayerModel( DOD_PLAYERMODEL_US_ASSAULT );
+ PrecachePlayerModel( DOD_PLAYERMODEL_US_SUPPORT );
+ PrecachePlayerModel( DOD_PLAYERMODEL_US_SNIPER );
+ PrecachePlayerModel( DOD_PLAYERMODEL_US_MG );
+ PrecachePlayerModel( DOD_PLAYERMODEL_US_ROCKET );
+
+/// Moved to pure_server_minimal.txt
+// engine->ForceSimpleMaterial( "materials/models/player/american/american_body.vmt" );
+// engine->ForceSimpleMaterial( "materials/models/player/american/american_gear.vmt" );
+// engine->ForceSimpleMaterial( "materials/models/player/german/german_body.vmt" );
+// engine->ForceSimpleMaterial( "materials/models/player/german/german_gear.vmt" );
+
+ UTIL_PrecacheOther( "dod_ammo_box" );
+
+ PrecacheScriptSound( "Player.FlashlightOn" );
+ PrecacheScriptSound( "Player.FlashlightOff" );
+
+ PrecacheScriptSound( "Player.FreezeCam" );
+ PrecacheScriptSound( "Camera.SnapShot" );
+ PrecacheScriptSound( "Achievement.Earned" );
+ PrecacheScriptSound( "Game.Revenge" );
+ PrecacheScriptSound( "Game.Domination" );
+ PrecacheScriptSound( "Game.Nemesis" );
+
+ PrecacheModel ( "sprites/glow01.vmt" );
+
+ BaseClass::Precache();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Allow pre-frame adjustments on the player
+//-----------------------------------------------------------------------------
+ConVar sv_runcmds( "sv_runcmds", "1" );
+void CDODPlayer::PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper )
+{
+ VPROF( "CDODPlayer::PlayerRunCommand" );
+
+ if ( !sv_runcmds.GetInt() )
+ return;
+
+ // don't run commands in the future
+ if ( !IsEngineThreaded() &&
+ ( ucmd->tick_count > (gpGlobals->tickcount + sv_max_usercmd_future_ticks.GetInt()) ) )
+ {
+ DevMsg( "Client cmd out of sync (delta %i).\n", ucmd->tick_count - gpGlobals->tickcount );
+ return;
+ }
+
+ BaseClass::PlayerRunCommand( ucmd, moveHelper );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Simulates a single frame of movement for a player
+//-----------------------------------------------------------------------------
+void CDODPlayer::RunPlayerMove( const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime )
+{
+ CUserCmd cmd;
+
+ // Store off the globals.. they're gonna get whacked
+ float flOldFrametime = gpGlobals->frametime;
+ float flOldCurtime = gpGlobals->curtime;
+
+ float flTimeBase = gpGlobals->curtime + gpGlobals->frametime - frametime;
+ this->SetTimeBase( flTimeBase );
+
+ CUserCmd lastUserCmd = *GetLastUserCommand();
+ Q_memset( &cmd, 0, sizeof( cmd ) );
+ MoveHelperServer()->SetHost( this );
+ this->PlayerRunCommand( &cmd, MoveHelperServer() );
+ this->SetLastUserCommand( cmd );
+
+ // Clear out any fixangle that has been set
+ this->pl.fixangle = FIXANGLE_NONE;
+
+ // Restore the globals..
+ gpGlobals->frametime = flOldFrametime;
+ gpGlobals->curtime = flOldCurtime;
+}
+
+
+void CDODPlayer::InitialSpawn( void )
+{
+ BaseClass::InitialSpawn();
+
+ State_Enter( STATE_WELCOME );
+}
+
+void CDODPlayer::Spawn()
+{
+ // Get rid of the progress bar...
+ SetProgressBarTime( 0 );
+
+ SetModel( DOD_PLAYERMODEL_US_RIFLEMAN ); //temporarily
+ BaseClass::Spawn();
+
+ AddFlag( FL_ONGROUND ); // set the player on the ground at the start of the round.
+
+ m_Shared.SetStamina( 100 );
+ m_bTeamChanged = false;
+
+ ClearCapAreaIndex();
+
+ m_bHasGenericAmmo = true;
+
+ m_bSlowedByHit = false;
+
+ m_flIdleTime = gpGlobals->curtime + random->RandomFloat( 20, 30 );
+
+ InitProne();
+ InitSprinting();
+
+ // update this counter, used to not interp players when they spawn
+ m_bSpawnInterpCounter = !m_bSpawnInterpCounter;
+
+ EmitSound( "Player.Spawn" );
+
+ SetContextThink( &CDODPlayer::PushawayThink, gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, DOD_PUSHAWAY_THINK_CONTEXT );
+
+ m_Shared.SetCPIndex( -1 );
+
+ ResetBleeding();
+
+ // Our own version of remove decals, as we can't pass entity messages to
+ // the target's base class.
+
+ EntityMessageBegin( this );
+ WRITE_BYTE( DOD_PLAYER_REMOVE_DECALS );
+ MessageEnd();
+
+ if ( m_Shared.PlayerClass() != PLAYERCLASS_UNDEFINED )
+ {
+ Hints()->StartHintTimer( HINT_USE_SPRINT );
+ }
+
+ // Get a unique "life id" used for tracking stats
+ m_iLifeID = GetNextLifeID();
+
+ SetDefusing( NULL );
+ SetPlanting( NULL );
+
+ m_iLastBlockAreaIndex = -1;
+ m_iLastBlockCapAttempt = -1;
+
+ if ( DODGameRules()->IsBombingTeam( GetTeamNumber() ) )
+ {
+ Hints()->HintMessage( HINT_BOMB_PLANT_MAP );
+ }
+
+ //CollisionProp()->SetSurroundingBoundsType( USE_GAME_CODE );
+
+ // Reset per-life achievement counts
+ HandleHeadshotAchievement( 0 );
+ HandleDeployedMGKillCount( 0 );
+ HandleEnemyWeaponsAchievement( 0 );
+ ResetComboWeaponKill();
+
+ RecalculateAchievementAwardsMask();
+
+ // If we're spawning as a non-player team, flush the stats
+ int iTeam = GetTeamNumber();
+ if ( iTeam != TEAM_ALLIES && iTeam != TEAM_AXIS )
+ {
+ StatEvent_UploadStats();
+ }
+
+ m_StatProperty.SetClassAndTeamForThisLife( m_Shared.PlayerClass(), GetTeamNumber() );
+}
+
+void CDODPlayer::PlayerDeathThink()
+{
+ //overridden, do nothing
+}
+
+void CDODPlayer::CreateRagdollEntity()
+{
+ // If we already have a ragdoll, don't make another one.
+ CDODRagdoll *pRagdoll = dynamic_cast< CDODRagdoll* >( m_hRagdoll.Get() );
+
+ if( pRagdoll )
+ {
+ // MATTTODO: put ragdolls in a queue to disappear
+ // for now remove the old one ..
+ UTIL_Remove( pRagdoll );
+ pRagdoll = NULL;
+ }
+
+ if ( !pRagdoll )
+ {
+ // and create a new one
+ pRagdoll = dynamic_cast< CDODRagdoll* >( CreateEntityByName( "dod_ragdoll" ) );
+ }
+
+ if ( pRagdoll )
+ {
+ pRagdoll->m_hPlayer = this;
+ pRagdoll->m_vecRagdollOrigin = GetAbsOrigin();
+ pRagdoll->m_vecRagdollVelocity = GetAbsVelocity();
+ pRagdoll->m_nModelIndex = m_nModelIndex;
+ pRagdoll->m_nForceBone = m_nForceBone;
+ pRagdoll->m_vecForce = m_vecTotalBulletForce;
+ }
+
+ // ragdolls will be removed on round restart automatically
+ m_hRagdoll = pRagdoll;
+}
+
+// Called when a player is disconnecting
+void CDODPlayer::DestroyRagdoll( void )
+{
+ CDODRagdoll *pRagdoll = dynamic_cast< CDODRagdoll* >( m_hRagdoll.Get() );
+
+ if( pRagdoll )
+ {
+ UTIL_Remove( pRagdoll );
+ }
+}
+
+void CDODPlayer::Event_Killed( const CTakeDamageInfo &info )
+{
+ // allow bots to react
+// TheBots->OnEvent( EVENT_PLAYER_DIED, this, info.GetAttacker() );
+
+ CBaseEntity *pInflictor = info.GetInflictor();
+ CBaseEntity *pKiller = info.GetAttacker();
+ CDODPlayer *pScorer = ToDODPlayer( DODGameRules()->GetDeathScorer( pKiller, pInflictor ) );
+
+ // Do this before we drop the victim's weapons to check the streak
+ if ( GetDeployedKillStreak() >= ACHIEVEMENT_MG_STREAK_IS_DOMINATING )
+ {
+ if ( pScorer && pScorer->GetTeamNumber() != GetTeamNumber() && DODGameRules()->State_Get() == STATE_RND_RUNNING )
+ {
+ pScorer->AwardAchievement( ACHIEVEMENT_DOD_KILL_DOMINATING_MG );
+ }
+ }
+
+ // see if this was a long range kill
+ // distance from scorer to inflictor > 1200
+ if ( pScorer && GetTeamNumber() != pScorer->GetTeamNumber() && pInflictor != pScorer )
+ {
+ const char *killer_weapon_name = STRING( pInflictor->m_iClassname );
+
+ if ( strncmp( killer_weapon_name, "rocket_", 7 ) == 0 )
+ {
+ float flDist = ( pScorer->GetAbsOrigin() - pInflictor->GetAbsOrigin() ).Length();
+
+ if ( flDist > ACHIEVEMENT_LONG_RANGE_ROCKET_DIST && DODGameRules()->State_Get() == STATE_RND_RUNNING )
+ {
+ bool bIsSniperZoomed = false;
+
+ CWeaponDODBase *pWeapon = GetActiveDODWeapon();
+ if( pWeapon && ( pWeapon->GetDODWpnData().m_WeaponType == WPN_TYPE_SNIPER ) )
+ {
+ CWeaponDODBaseGun *pGun = static_cast<CWeaponDODBaseGun *>( pWeapon );
+ bIsSniperZoomed = pGun->IsSniperZoomed();
+ }
+
+ // victim must be deployed sniper or deployed mg
+ if ( bIsSniperZoomed || m_Shared.IsInMGDeploy() )
+ {
+ pScorer->AwardAchievement( ACHIEVEMENT_DOD_LONG_RANGE_ROCKET );
+ }
+ }
+ }
+ }
+
+ DropPrimaryWeapon();
+
+ if ( DODGameRules()->GetPresentDropChance() >= RandomFloat() )
+ {
+ Vector vForward, vRight, vUp;
+ AngleVectors( EyeAngles(), &vForward, &vRight, &vUp );
+
+ CHolidayGift::Create( WorldSpaceCenter(), GetAbsAngles(), EyeAngles(), GetAbsVelocity(), this );
+ }
+
+ FlashlightTurnOff();
+
+ // Just in case the progress bar is on screen, kill it.
+ SetProgressBarTime( 0 );
+
+ // turn off our cap area index
+ HandleSignals();
+
+ //if we have a primed grenade, drop it
+ CWeaponDODBase *pWpn = GetActiveDODWeapon();
+
+ if( pWpn && pWpn->GetDODWpnData().m_WeaponType == WPN_TYPE_GRENADE )
+ {
+ CWeaponDODBaseGrenade *pGrenade = dynamic_cast<CWeaponDODBaseGrenade *>(pWpn);
+
+ if( pGrenade && pGrenade->IsArmed() )
+ {
+ pGrenade->DropGrenade();
+ }
+ }
+
+ if ( pScorer && pScorer != this && pScorer->GetTeamNumber() != GetTeamNumber() )
+ {
+ if ( m_bIsPlanting && m_pPlantTarget )
+ {
+ CDODBombTarget *pTarget = m_pPlantTarget;
+
+ Assert( pTarget );
+
+ if ( pTarget )
+ pTarget->PlantBlocked( pScorer );
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_kill_planter" );
+ if ( event )
+ {
+ event->SetInt( "userid", pScorer->GetUserID() );
+ event->SetInt( "victimid", GetUserID() );
+
+ gameeventmanager->FireEvent( event );
+ }
+ }
+
+ if ( m_bIsDefusing && m_pDefuseTarget && pScorer->GetTeamNumber() != GetTeamNumber() )
+ {
+ // Re-enable if we want to give a defend point to someone who kills a defuser
+
+ //CDODBombTarget *pTarget = m_pDefuseTarget;
+ //pTarget->DefuseBlocked( pScorer );
+
+ IGameEvent *event = gameeventmanager->CreateEvent( "dod_kill_defuser" );
+ if ( event )
+ {
+ event->SetInt( "userid", pScorer->GetUserID() );
+ event->SetInt( "victimid", GetUserID() );
+
+ gameeventmanager->FireEvent( event );
+ }
+ }
+ }
+
+ SetDefusing( NULL );
+ SetPlanting( NULL );
+
+ // show killer in death cam mode
+ // chopped down version of SetObserverTarget without the team check
+ if( info.GetAttacker() && info.GetAttacker()->IsPlayer() )
+ {
+ // set new target
+ m_hObserverTarget.Set( info.GetAttacker() );
+
+ // reset fov to default
+ SetFOV( this, 0 );
+ }
+ else
+ m_hObserverTarget.Set( NULL );
+
+ //update damage info with our accumulated physics force
+ CTakeDamageInfo subinfo = info;
+ subinfo.SetDamageForce( m_vecTotalBulletForce );
+
+ // Note: since we're dead, it won't draw us on the client, but we don't set EF_NODRAW
+ // because we still want to transmit to the clients in our PVS.
+ CreateRagdollEntity();
+
+ State_Transition( STATE_DEATH_ANIM ); // Transition into the dying state.
+ BaseClass::Event_Killed( subinfo );
+
+ OutputDamageGiven();
+ OutputDamageTaken();
+ ResetDamageCounters();
+
+ // stop any voice command or pain sounds that we may be making
+ CPASFilter filter( WorldSpaceCenter() );
+ EmitSound( filter, entindex(), "Voice.StopSounds" );
+
+ ResetBleeding();
+
+ m_flStunDuration = 0.0f;
+ m_flStunMaxAlpha = 0;
+
+ // cancel deafen think
+ SetContextThink( NULL, 0.0, DOD_DEAFEN_CONTEXT );
+
+ // cancel deafen dsp
+ CSingleUserRecipientFilter user( this );
+ enginesound->SetPlayerDSP( user, 0, false );
+
+ CDODPlayer *pAttacker = ToDODPlayer( info.GetAttacker() );
+
+ if( pAttacker && pAttacker->IsPlayer() )
+ {
+ // find the weapon id of the weapon
+
+ DODWeaponID weaponID = WEAPON_NONE;
+
+ CBaseEntity *pInflictor = info.GetInflictor();
+
+ if ( pInflictor == pAttacker )
+ {
+ CWeaponDODBase *pWpn = pAttacker->GetActiveDODWeapon();
+
+ if ( pWpn )
+ {
+ if ( info.GetDamageCustom() & MELEE_DMG_SECONDARYATTACK )
+ weaponID = pWpn->GetAltWeaponID();
+ else
+ weaponID = pWpn->GetStatsWeaponID();
+ }
+ }
+ else
+ {
+ Assert( pInflictor );
+
+ const char *weaponName = STRING( pInflictor->m_iClassname );
+
+ if ( Q_strncmp( weaponName, "rocket_", 7 ) == 0 )
+ {
+ CDODBaseRocket *pRocket = dynamic_cast< CDODBaseRocket *>( pInflictor );
+ if ( pRocket )
+ {
+ weaponID = pRocket->GetEmitterWeaponID();
+ }
+ }
+ else if ( Q_strncmp( weaponName, "grenade_", 8 ) == 0 )
+ {
+ CDODBaseGrenade *pGren = dynamic_cast< CDODBaseGrenade *>( pInflictor );
+ if ( pGren )
+ {
+ weaponID = pGren->GetEmitterWeaponID();
+ }
+ }
+ }
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "dod_stats_player_killed" );
+ if ( event )
+ {
+ event->SetInt( "attacker", pAttacker->GetUserID() );
+ event->SetInt( "victim", GetUserID() );
+ event->SetInt( "weapon", weaponID );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ if ( DODGameRules()->IsInBonusRound() )
+ {
+ pAttacker->Stats_BonusRoundKill();
+ }
+ }
+
+ m_bIsDefusing = false;
+ m_bIsPlanting = false;
+
+ if ( pScorer != this )
+ {
+ StatEvent_WasKilled();
+ }
+
+ StatEvent_UploadStats();
+}
+
+bool CDODPlayer::ShouldInstantRespawn( void )
+{
+ CUtlVector<EHANDLE> *pSpawnPoints = DODGameRules()->GetSpawnPointListForTeam( GetTeamNumber() );
+
+ if ( !pSpawnPoints )
+ return false;
+
+ const float flMaxDistSqr = ( 500*500 );
+ Vector vecOrigin = GetAbsOrigin();
+ int i;
+ int count = pSpawnPoints->Count();
+ for ( i=0;i<count;i++ )
+ {
+ EHANDLE handle = pSpawnPoints->Element(i);
+ if ( handle )
+ {
+ CSpawnPoint *point = dynamic_cast<CSpawnPoint *>( handle.Get() );
+ if ( point && !point->IsDisabled() )
+ {
+ // range
+ Vector vecDist = point->GetAbsOrigin() - vecOrigin;
+ if ( vecDist.LengthSqr() > flMaxDistSqr )
+ {
+ continue;
+ }
+
+ // los
+ if ( FVisible( point ) )
+ {
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODPlayer::InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity )
+{
+ if ( 0 ) //if ( !sv_turbophysics.GetBool() )
+ {
+ BaseClass::InitVCollision( vecAbsOrigin, vecAbsVelocity );
+
+ // Setup the HL2 specific callback.
+ GetPhysicsController()->SetEventHandler( &playerCallback );
+ }
+}
+
+void CDODPlayer::VPhysicsShadowUpdate( IPhysicsObject *pPhysics )
+{
+ if ( 0 ) //if ( !sv_turbophysics.GetBool() )
+ {
+ if ( !CanMove() )
+ return;
+
+ BaseClass::VPhysicsShadowUpdate( pPhysics );
+ }
+}
+
+void CDODPlayer::PushawayThink()
+{
+ // Push physics props out of our way.
+ PerformObstaclePushaway( this );
+ SetNextThink( gpGlobals->curtime + PUSHAWAY_THINK_INTERVAL, DOD_PUSHAWAY_THINK_CONTEXT );
+}
+
+void CDODPlayer::CheatImpulseCommands( int iImpulse )
+{
+ switch( iImpulse )
+ {
+ case 101:
+ {
+ if( sv_cheats->GetBool() )
+ {
+ extern int gEvilImpulse101;
+ gEvilImpulse101 = true;
+
+ GiveAmmo( 1000, DOD_AMMO_COLT );
+ GiveAmmo( 1000, DOD_AMMO_P38 );
+ GiveAmmo( 1000, DOD_AMMO_C96 );
+ GiveAmmo( 1000, DOD_AMMO_GARAND );
+ GiveAmmo( 1000, DOD_AMMO_K98 );
+ GiveAmmo( 1000, DOD_AMMO_M1CARBINE );
+ GiveAmmo( 1000, DOD_AMMO_SPRING );
+ GiveAmmo( 1000, DOD_AMMO_SUBMG );
+ GiveAmmo( 1000, DOD_AMMO_BAR );
+ GiveAmmo( 1000, DOD_AMMO_30CAL );
+ GiveAmmo( 1000, DOD_AMMO_MG42 );
+ GiveAmmo( 1000, DOD_AMMO_ROCKET );
+
+ gEvilImpulse101 = false;
+ }
+ }
+ break;
+
+ default:
+ {
+ BaseClass::CheatImpulseCommands( iImpulse );
+ }
+ }
+}
+
+void CDODPlayer::SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize )
+{
+ BaseClass::SetupVisibility( pViewEntity, pvs, pvssize );
+
+ int area = pViewEntity ? pViewEntity->NetworkProp()->AreaNum() : NetworkProp()->AreaNum();
+ PointCameraSetupVisibility( this, area, pvs, pvssize );
+}
+
+void CDODPlayer::DoAnimationEvent( PlayerAnimEvent_t event, int nData )
+{
+ m_PlayerAnimState->DoAnimationEvent( event, nData );
+ TE_PlayerAnimEvent( this, event, nData ); // Send to any clients who can see this guy.
+}
+
+CBaseEntity *CDODPlayer::GiveNamedItem( const char *pszName, int iSubType )
+{
+ EHANDLE pent;
+
+ pent = CreateEntityByName(pszName);
+ if ( pent == NULL )
+ {
+ Msg( "NULL Ent in GiveNamedItem!\n" );
+ return NULL;
+ }
+
+ pent->SetLocalOrigin( GetLocalOrigin() );
+ pent->AddSpawnFlags( SF_NORESPAWN );
+
+ if ( iSubType )
+ {
+ CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon*>( (CBaseEntity*)pent );
+ if ( pWeapon )
+ {
+ pWeapon->SetSubType( iSubType );
+ }
+ }
+
+ DispatchSpawn( pent );
+
+ if ( pent != NULL && !(pent->IsMarkedForDeletion()) )
+ {
+ pent->Touch( this );
+ }
+
+ return pent;
+}
+
+extern ConVar flashlight;
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+int CDODPlayer::FlashlightIsOn( void )
+{
+ return IsEffectActive( EF_DIMLIGHT );
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CDODPlayer::FlashlightTurnOn( void )
+{
+ if( flashlight.GetInt() > 0 && IsAlive() )
+ {
+ AddEffects( EF_DIMLIGHT );
+ EmitSound( "Player.FlashlightOn" );
+ }
+}
+
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+void CDODPlayer::FlashlightTurnOff( void )
+{
+ if( IsEffectActive(EF_DIMLIGHT) )
+ {
+ RemoveEffects( EF_DIMLIGHT );
+
+ if( m_iHealth > 0 )
+ {
+ EmitSound( "Player.FlashlightOff" );
+ }
+ }
+}
+
+
+void CDODPlayer::PostThink()
+{
+ BaseClass::PostThink();
+
+ if( m_Shared.IsProne() )
+ {
+ SetCollisionBounds( VEC_PRONE_HULL_MIN, VEC_PRONE_HULL_MAX );
+ }
+
+ if ( gpGlobals->curtime > m_fHandleSignalsTime )
+ {
+ m_fHandleSignalsTime = gpGlobals->curtime + 0.1;
+ HandleSignals();
+ }
+ QAngle angles = GetLocalAngles();
+ angles[PITCH] = 0;
+ SetLocalAngles( angles );
+
+ // Store the eye angles pitch so the client can compute its animation state correctly.
+ m_angEyeAngles = EyeAngles();
+
+ m_PlayerAnimState->Update( m_angEyeAngles[YAW], m_angEyeAngles[PITCH] );
+}
+
+void CDODPlayer::HandleCommand_Voice( const char *pcmd )
+{
+ // string should be formatted as "voice_gogogo"
+
+ // go through our list of voice commands and if we find it,
+ // emit a voice command
+
+ int i = 0;
+ while( g_VoiceCommands[i].pszCommandName != NULL )
+ {
+ if( Q_strcmp( pcmd, g_VoiceCommands[i].pszCommandName ) == 0 )
+ {
+ VoiceCommand( i );
+ break;
+ }
+ i++;
+ }
+}
+
+void CDODPlayer::VoiceCommand( int iVoiceCommand )
+{
+ //no voices in observer or when dead
+ if ( !IsAlive() || IsObserver() )
+ return;
+
+ //no voices when game is over
+ if (g_fGameOver)
+ return;
+
+ if (m_flNextVoice > gpGlobals->curtime )
+ return;
+
+ // Emit the voice sound
+ CPASFilter filter( WorldSpaceCenter() );
+
+ // Get the country text to fill into the sound name
+ char *pszCountry = "US";
+
+ if( GetTeamNumber() == TEAM_AXIS )
+ {
+ pszCountry = "German";
+ }
+
+ char szSound[128];
+ Q_snprintf( szSound, sizeof(szSound), "Voice.%s_%s", pszCountry, g_VoiceCommands[iVoiceCommand].pszSoundName );
+
+ EmitSound( filter, entindex(), szSound );
+
+ // Don't show the subtitle to the other team
+ int oppositeTeam = ( GetTeamNumber() == TEAM_ALLIES ) ? TEAM_AXIS : TEAM_ALLIES;
+ filter.RemoveRecipientsByTeam( GetGlobalDODTeam(oppositeTeam) );
+
+ // further reduce the voice command subtitle radius
+ float flDist;
+ float flMaxDist = 1900;
+ Vector vecEmitOrigin = GetAbsOrigin();
+
+ int i;
+ for ( i=1; i<= MAX_PLAYERS; i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByIndex( i );
+
+ if ( !pPlayer )
+ continue;
+
+ flDist = ( pPlayer->GetAbsOrigin() - vecEmitOrigin ).Length2D();
+
+ if ( flDist > flMaxDist )
+ filter.RemoveRecipient( pPlayer );
+ }
+
+ // Send a subtitle to anyone in the PAS
+ UserMessageBegin( filter, "VoiceSubtitle" );
+ WRITE_BYTE( entindex() );
+ WRITE_BYTE( GetTeamNumber() );
+ WRITE_BYTE( iVoiceCommand );
+ MessageEnd();
+
+ PlayerAnimEvent_t iHandSignal = g_VoiceCommands[iVoiceCommand].iHandSignal;
+
+ if( iHandSignal != PLAYERANIMEVENT_HS_NONE )
+ DoAnimationEvent( iHandSignal );
+
+ m_flNextVoice = gpGlobals->curtime + 1.0;
+}
+
+void CDODPlayer::HandleCommand_HandSignal( const char *pcmd )
+{
+ // string should be formatted as "signal_yes"
+
+ int i = 0;
+ while( g_HandSignals[i].pszCommandName != NULL )
+ {
+ if( Q_strcmp( pcmd, g_HandSignals[i].pszCommandName ) == 0 )
+ {
+ HandSignal( i );
+ break;
+ }
+ i++;
+ }
+}
+
+void CDODPlayer::HandSignal( int iSignal )
+{
+ //no hand signals in observer or when dead
+ if ( !IsAlive() || IsObserver() )
+ return;
+
+ //or when game is over
+ if (g_fGameOver)
+ return;
+
+ if (m_flNextHandSignal > gpGlobals->curtime )
+ return;
+
+ int oppositeTeam = ( GetTeamNumber() == TEAM_ALLIES ) ? TEAM_AXIS : TEAM_ALLIES;
+
+ // Emit the voice sound
+ CRecipientFilter filter;
+ filter.AddRecipientsByPVS( WorldSpaceCenter() );
+ filter.RemoveRecipientsByTeam( GetGlobalTeam(oppositeTeam) );
+
+ // Send a subtitle to anyone in the PAS
+ UserMessageBegin( filter, "HandSignalSubtitle" );
+ WRITE_BYTE( entindex() );
+ WRITE_BYTE( iSignal );
+ MessageEnd();
+
+ DoAnimationEvent( g_HandSignals[iSignal].iHandSignal );
+
+ m_flNextHandSignal = gpGlobals->curtime + 1.0;
+}
+
+void CDODPlayer::DropGenericAmmo( void )
+{
+ if ( !IsAlive() )
+ return;
+
+ if( m_bHasGenericAmmo == false )
+ return;
+
+ Vector vForward, vRight, vUp;
+ AngleVectors( EyeAngles(), &vForward, &vRight, &vUp );
+
+ CAmmoBox *pAmmo = CAmmoBox::Create( WorldSpaceCenter(), vec3_angle, this, GetTeamNumber() );
+
+ pAmmo->ApplyAbsVelocityImpulse( vForward * 300 + vUp * 100 );
+
+ m_hLastDroppedAmmoBox = pAmmo;
+
+ m_bHasGenericAmmo = false;
+}
+
+void CDODPlayer::ReturnGenericAmmo( void )
+{
+ //play pickup sound
+ CPASFilter filter( WorldSpaceCenter() );
+ EmitSound( filter, entindex(), "BaseCombatCharacter.AmmoPickup" );
+
+ //allow them to drop generic ammo again
+ m_bHasGenericAmmo = true;
+}
+
+bool CDODPlayer::GiveGenericAmmo( void )
+{
+ //give primary weapon ammo
+ CWeaponDODBase *pWpn = (CWeaponDODBase *)Weapon_GetSlot( WPN_SLOT_PRIMARY );
+
+ if( pWpn )
+ {
+ int nAmmoType = pWpn->GetPrimaryAmmoType();
+
+ int iClipSize = pWpn->GetDODWpnData().iMaxClip1;
+
+ int iAmmoPickupClips = pWpn->GetDODWpnData().m_iAmmoPickupClips;
+
+ if( GiveAmmo( iAmmoPickupClips * iClipSize, nAmmoType, false ) > 0 )
+ {
+ //some ammo was picked up, consume the ammo box
+
+ //play pickup sound
+ CPASFilter filter( WorldSpaceCenter() );
+ EmitSound( filter, entindex(), "BaseCombatCharacter.AmmoPickup" );
+
+ return true;
+ }
+ }
+
+ return false;
+}
+
+ConVar dod_friendlyfiresafezone( "dod_friendlyfiresafezone",
+ "100",
+ FCVAR_ARCHIVE,
+ "Units around a player where they will not damage teammates, even if FF is on",
+ true, 0, false, 0 );
+
+void CDODPlayer::TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator )
+{
+ bool bTakeDamage = true;
+
+ CBasePlayer *pAttacker = (CBasePlayer*)ToBasePlayer( info.GetAttacker() );
+
+ bool bFriendlyFire = DODGameRules()->IsFriendlyFireOn();
+
+ if ( pAttacker && ( GetTeamNumber() == pAttacker->GetTeamNumber() ) )
+ {
+ bTakeDamage = bFriendlyFire;
+
+ // Create a FF-free zone around players for melee and bullets
+ if ( bFriendlyFire && ( info.GetDamageType() & (DMG_SLASH | DMG_BULLET | DMG_CLUB) ) )
+ {
+ float flDist = dod_friendlyfiresafezone.GetFloat();
+
+ Vector vecDist = pAttacker->WorldSpaceCenter() - WorldSpaceCenter();
+
+ if ( vecDist.LengthSqr() < ( flDist*flDist ) )
+ {
+ bTakeDamage = false;
+ }
+ }
+ }
+
+ if ( m_takedamage != DAMAGE_YES )
+ return;
+
+ m_LastHitGroup = ptr->hitgroup;
+ m_LastDamageType = info.GetDamageType();
+
+ Assert( ptr->hitgroup != HITGROUP_GENERIC );
+
+ m_nForceBone = ptr->physicsbone; //Save this bone for ragdoll
+
+ float flDamage = info.GetDamage();
+ float flOriginalDmg = flDamage;
+
+ bool bHeadShot = false;
+
+ //if its an enemy OR ff is on.
+ if( bTakeDamage )
+ {
+ m_Shared.SetSlowedTime( 0.5f );
+ }
+
+ char *szHitbox = NULL;
+
+ if( info.GetDamageType() & DMG_BLAST )
+ {
+ // don't do hitgroup specific grenade damage
+ flDamage *= 1.0;
+ szHitbox = "dmg_blast";
+ }
+ else
+ {
+ switch ( ptr->hitgroup )
+ {
+ case HITGROUP_HEAD:
+ {
+ flDamage *= 2.5; //regular head shot multiplier
+
+ if( bTakeDamage )
+ {
+ Vector dir = vecDir;
+ VectorNormalize(dir);
+
+ if ( info.GetDamageType() & DMG_CLUB )
+ dir *= 800;
+ else
+ dir *= 400;
+
+ dir.z += 100; // add some lift so it pops better.
+
+ PopHelmet( dir, ptr->endpos );
+ }
+
+ szHitbox = "head";
+
+ bHeadShot = true;
+ }
+ break;
+
+ case HITGROUP_CHEST:
+ szHitbox = "chest";
+ break;
+
+ case HITGROUP_STOMACH:
+ szHitbox = "stomach";
+ break;
+
+ case HITGROUP_LEFTARM:
+ flDamage *= 0.75;
+ szHitbox = "left arm";
+ break;
+
+ case HITGROUP_RIGHTARM:
+ flDamage *= 0.75;
+ szHitbox = "right arm";
+ break;
+
+ case HITGROUP_LEFTLEG:
+ case HITGROUP_RIGHTLEG:
+ {
+ //we are slowed for 2 full seconds if we get a leg hit
+ if( bTakeDamage )
+ {
+ //m_bSlowedByHit = true;
+ //m_flUnslowTime = gpGlobals->curtime + 2;
+ m_Shared.SetSlowedTime( 2.0f );
+ }
+
+ flDamage *= 0.75;
+
+ szHitbox = "leg";
+ }
+ break;
+
+ case HITGROUP_GENERIC:
+ szHitbox = "(error - hit generic)";
+ break;
+
+ default:
+ szHitbox = "(error - hit default)";
+ break;
+ }
+ }
+
+ if ( bTakeDamage )
+ {
+ if( dod_debugdamage.GetInt() )
+ {
+ char buf[256];
+
+ Q_snprintf( buf, sizeof(buf), "%s hit %s in the %s hitgroup for %f damage ( %f base damage ) ( %s now has %d health )\n",
+ pAttacker->GetPlayerName(),
+ GetPlayerName(),
+ szHitbox,
+ flDamage,
+ flOriginalDmg,
+ GetPlayerName(),
+ GetHealth() - (int)flDamage );
+
+ // print to server
+ UTIL_LogPrintf( "%s", buf );
+
+ // print to injured
+ ClientPrint( this, HUD_PRINTCONSOLE, buf );
+
+ // print to shooter
+ if ( pAttacker )
+ ClientPrint( pAttacker, HUD_PRINTCONSOLE, buf );
+ }
+
+ // Since this code only runs on the server, make sure it shows the tempents it creates.
+ CDisablePredictionFiltering disabler;
+
+ // This does smaller splotches on the guy and splats blood on the world.
+ TraceBleed( flDamage, vecDir, ptr, info.GetDamageType() );
+
+ CEffectData data;
+ data.m_vOrigin = ptr->endpos;
+ data.m_vNormal = vecDir * -1;
+ data.m_flScale = 4;
+ data.m_fFlags = FX_BLOODSPRAY_ALL;
+ data.m_nEntIndex = ptr->m_pEnt ? ptr->m_pEnt->entindex() : 0;
+ data.m_flMagnitude = flDamage;
+
+ DispatchEffect( "dodblood", data );
+
+ CTakeDamageInfo subInfo = info;
+
+ subInfo.SetDamage( flDamage );
+
+ AddMultiDamage( subInfo, this );
+ }
+}
+
+
+bool CDODPlayer::DropActiveWeapon( void )
+{
+ CWeaponDODBase *pWeapon = GetActiveDODWeapon();
+
+ if( pWeapon )
+ {
+ if( pWeapon->CanDrop() )
+ {
+ DODWeaponDrop( pWeapon, true );
+ return true;
+ }
+ else
+ {
+ int iWeaponType = pWeapon->GetDODWpnData().m_WeaponType;
+ if( iWeaponType == WPN_TYPE_BAZOOKA || iWeaponType == WPN_TYPE_MG )
+ {
+ // they are deployed, cannot drop
+ Hints()->HintMessage( "#game_cannot_drop_while" );
+ }
+ else
+ {
+ // must be a weapon type that cannot be dropped
+ Hints()->HintMessage( "#game_cannot_drop" );
+ }
+
+ return false;
+ }
+ }
+
+ return false;
+}
+
+CWeaponDODBase* CDODPlayer::GetActiveDODWeapon() const
+{
+ return dynamic_cast< CWeaponDODBase* >( GetActiveWeapon() );
+}
+
+void CDODPlayer::PreThink()
+{
+ BaseClass::PreThink();
+
+ if ( m_afButtonLast != m_nButtons )
+ m_flLastMovement = gpGlobals->curtime;
+
+ if ( g_fGameOver )
+ return;
+
+ State_PreThink();
+
+ //Reset bullet force accumulator, only lasts one frame, for ragdoll forces from multiple shots
+ m_vecTotalBulletForce = vec3_origin;
+
+ if ( mp_autokick.GetInt() && !IsBot() && !IsHLTV() )
+ {
+ if ( m_flLastMovement + mp_autokick.GetInt()*60 < gpGlobals->curtime )
+ {
+ UTIL_ClientPrintAll( HUD_PRINTCONSOLE, "#Game_idle_kick", GetPlayerName() );
+ engine->ServerCommand( UTIL_VarArgs( "kickid %d\n", GetUserID() ) );
+ m_flLastMovement = gpGlobals->curtime;
+ }
+ }
+}
+
+
+bool CDODPlayer::DropPrimaryWeapon( void )
+{
+ CWeaponDODBase *pWeapon = (CWeaponDODBase *)Weapon_GetSlot( WPN_SLOT_PRIMARY );
+
+ if( pWeapon )
+ {
+ DODWeaponDrop( pWeapon, false );
+ return true;
+ }
+
+ return false;
+}
+
+bool CDODPlayer::DODWeaponDrop( CBaseCombatWeapon *pWeapon, bool bThrowForward )
+{
+ bool bSuccess = false;
+
+ if ( pWeapon )
+ {
+ Vector vForward;
+
+ AngleVectors( EyeAngles(), &vForward, NULL, NULL );
+ Vector vTossPos = WorldSpaceCenter();
+
+ if( bThrowForward )
+ vTossPos = vTossPos + vForward * 64;
+
+ Weapon_Drop( pWeapon, &vTossPos, NULL );
+
+ pWeapon->SetSolidFlags( FSOLID_NOT_STANDABLE | FSOLID_TRIGGER | FSOLID_USE_TRIGGER_BOUNDS );
+ pWeapon->SetMoveCollide( MOVECOLLIDE_FLY_BOUNCE );
+
+ CWeaponDODBase *pDODWeapon = dynamic_cast< CWeaponDODBase* >( pWeapon );
+
+ if( pDODWeapon )
+ {
+ pDODWeapon->SetDieThink(true);
+
+ pDODWeapon->SetWeaponModelIndex( pDODWeapon->GetDODWpnData().szWorldModel );
+
+ //Find out the index of the ammo type
+ int iAmmoIndex = pDODWeapon->GetPrimaryAmmoType();
+
+ //If it has an ammo type, find out how much the player has
+ if( iAmmoIndex != -1 )
+ {
+ int iAmmoToDrop = GetAmmoCount( iAmmoIndex );
+
+ //Add this much to the dropped weapon
+ pDODWeapon->SetExtraAmmoCount( iAmmoToDrop );
+
+ //Remove all ammo of this type from the player
+ SetAmmoCount( 0, iAmmoIndex );
+ }
+ }
+
+ //=========================================
+ // Teleport the weapon to the player's hand
+ //=========================================
+ int iBIndex = -1;
+ int iWeaponBoneIndex = -1;
+
+ CStudioHdr *hdr = pWeapon->GetModelPtr();
+ // If I have a hand, set the weapon position to my hand bone position.
+ if ( hdr && hdr->numbones() > 0 )
+ {
+ // Assume bone zero is the root
+ for ( iWeaponBoneIndex = 0; iWeaponBoneIndex < hdr->numbones(); ++iWeaponBoneIndex )
+ {
+ iBIndex = LookupBone( hdr->pBone( iWeaponBoneIndex )->pszName() );
+ // Found one!
+ if ( iBIndex != -1 )
+ {
+ break;
+ }
+ }
+
+ if ( iWeaponBoneIndex == hdr->numbones() )
+ return true;
+
+ if ( iBIndex == -1 )
+ {
+ iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" );
+ }
+ }
+ else
+ {
+ iBIndex = LookupBone( "ValveBiped.Bip01_R_Hand" );
+ }
+
+ if ( iBIndex != -1)
+ {
+ Vector origin;
+ QAngle angles;
+ matrix3x4_t transform;
+
+ // Get the transform for the weapon bonetoworldspace in the NPC
+ GetBoneTransform( iBIndex, transform );
+
+ // find offset of root bone from origin in local space
+ // Make sure we're detached from hierarchy before doing this!!!
+ pWeapon->StopFollowingEntity();
+ pWeapon->SetAbsOrigin( Vector( 0, 0, 0 ) );
+ pWeapon->SetAbsAngles( QAngle( 0, 0, 0 ) );
+ pWeapon->InvalidateBoneCache();
+ matrix3x4_t rootLocal;
+ pWeapon->GetBoneTransform( iWeaponBoneIndex, rootLocal );
+
+ // invert it
+ matrix3x4_t rootInvLocal;
+ MatrixInvert( rootLocal, rootInvLocal );
+
+ matrix3x4_t weaponMatrix;
+ ConcatTransforms( transform, rootInvLocal, weaponMatrix );
+ MatrixAngles( weaponMatrix, angles, origin );
+
+ // trace the bounding box of the weapon in the desired position
+ trace_t tr;
+ Vector mins, maxs;
+
+ // not exactly correct bounds, we haven't rotated them to match the attachment
+ pWeapon->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs );
+
+ UTIL_TraceHull( WorldSpaceCenter(), origin, mins, maxs, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr );
+
+ if ( tr.fraction < 1.0 )
+ origin = WorldSpaceCenter();
+
+ pWeapon->Teleport( &origin, &angles, NULL );
+
+ //Have to teleport the physics object as well
+
+ IPhysicsObject *pWeaponPhys = pWeapon->VPhysicsGetObject();
+
+ if( pWeaponPhys )
+ {
+ Vector vPos;
+ QAngle vAngles;
+ pWeaponPhys->GetPosition( &vPos, &vAngles );
+ pWeaponPhys->SetPosition( vPos, angles, true );
+
+ AngularImpulse angImp(0,0,0);
+ Vector vecAdd = GetAbsVelocity();
+ pWeaponPhys->AddVelocity( &vecAdd, &angImp );
+ }
+
+ m_hLastDroppedWeapon = pWeapon;
+ }
+
+ if ( !GetActiveWeapon() )
+ {
+ // we haven't auto-switched to anything usable
+ // switch to the first weapon we find, even if its empty
+
+ CBaseCombatWeapon *pCheck;
+
+ for ( int i = 0 ; i < WeaponCount(); ++i )
+ {
+ pCheck = GetWeapon( i );
+ if ( !pCheck || !pCheck->CanDeploy() )
+ {
+ continue;
+ }
+
+ Weapon_Switch( pCheck );
+ break;
+ }
+ }
+
+ bSuccess = true;
+ }
+
+ return bSuccess;
+}
+
+extern int g_iHelmetModels[NUM_HELMETS];
+
+void CDODPlayer::PopHelmet( Vector vecDir, Vector vecForceOrigin )
+{
+ int iPlayerClass = m_Shared.PlayerClass();
+
+ CDODTeam *pTeam = GetGlobalDODTeam( GetTeamNumber() );
+ const CDODPlayerClassInfo &pClassInfo = pTeam->GetPlayerClassInfo( iPlayerClass );
+
+ // See if they already lost their helmet
+ if( GetBodygroup( BODYGROUP_HELMET ) == pClassInfo.m_iHairGroup )
+ return;
+
+ // Nope.. take it off
+ SetBodygroup( BODYGROUP_HELMET, pClassInfo.m_iHairGroup );
+
+ // Add the velocity of the player
+ vecDir += GetAbsVelocity();
+
+ //CDisablePredictionFiltering disabler;
+
+ EntityMessageBegin( this, true );
+ WRITE_BYTE( DOD_PLAYER_POP_HELMET );
+ WRITE_VEC3COORD( vecDir );
+ WRITE_VEC3COORD( vecForceOrigin );
+ WRITE_SHORT( g_iHelmetModels[pClassInfo.m_iDropHelmet] );
+ MessageEnd();
+}
+
+bool CDODPlayer::BumpWeapon( CBaseCombatWeapon *pBaseWeapon )
+{
+ CWeaponDODBase *pWeapon = dynamic_cast< CWeaponDODBase* >( pBaseWeapon );
+ if ( !pWeapon )
+ {
+ Assert( false );
+ return false;
+ }
+
+ CBaseCombatCharacter *pOwner = pWeapon->GetOwner();
+
+ // Can I have this weapon type?
+ if ( pOwner || !Weapon_CanUse( pWeapon ) || !g_pGameRules->CanHavePlayerItem( this, pWeapon ) )
+ {
+ extern int gEvilImpulse101;
+ if ( gEvilImpulse101 )
+ {
+ UTIL_Remove( pWeapon );
+ }
+ return false;
+ }
+
+ // Don't let the player fetch weapons through walls
+ if( !pWeapon->FVisible( this ) && !(GetFlags() & FL_NOTARGET) )
+ {
+ return false;
+ }
+
+ /*
+ CBaseCombatWeapon *pOwnedWeapon = Weapon_OwnsThisType( pWeapon->GetClassname() );
+
+ if( pOwnedWeapon != NULL && ( pWeapon->GetWpnData().iFlags & ITEM_FLAG_EXHAUSTIBLE ) )
+ {
+ // its an item that we can hold several of, and use up
+
+ // Just give one ammo
+ if( GiveAmmo( 1, pOwnedWeapon->GetPrimaryAmmoType(), true ) > 0 )
+ return true;
+ else
+ return false;
+ }
+ */
+
+ // ----------------------------------------
+ // If I already have it just take the ammo
+ // ----------------------------------------
+ if (Weapon_OwnsThisType( pWeapon->GetClassname(), pWeapon->GetSubType()))
+ {
+ if( Weapon_EquipAmmoOnly( pWeapon ) )
+ {
+ // Only remove me if I have no ammo left
+ if ( pWeapon->HasPrimaryAmmo() )
+ return false;
+
+ UTIL_Remove( pWeapon );
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ pWeapon->CheckRespawn();
+
+ pWeapon->AddSolidFlags( FSOLID_NOT_SOLID );
+ pWeapon->AddEffects( EF_NODRAW );
+
+ Weapon_Equip( pWeapon );
+
+ int iExtraAmmo = pWeapon->GetExtraAmmoCount();
+
+ if( iExtraAmmo )
+ {
+ //Find out the index of the ammo
+ int iAmmoIndex = pWeapon->GetPrimaryAmmoType();
+
+ if( iAmmoIndex != -1 )
+ {
+ //Remove the extra ammo from the weapon
+ pWeapon->SetExtraAmmoCount(0);
+
+ //Give it to the player
+ SetAmmoCount( iExtraAmmo, iAmmoIndex );
+ }
+ }
+
+ return true;
+}
+
+
+bool CDODPlayer::HandleCommand_JoinClass( int iClass )
+{
+ Assert( GetTeamNumber() != TEAM_SPECTATOR );
+ Assert( GetTeamNumber() != TEAM_UNASSIGNED );
+
+ if( GetTeamNumber() == TEAM_SPECTATOR )
+ return false;
+
+ if( iClass == PLAYERCLASS_UNDEFINED )
+ return false; //they typed in something weird
+
+ int iOldPlayerClass = m_Shared.DesiredPlayerClass();
+
+ // See if we're joining the class we already are
+ if( iClass == iOldPlayerClass )
+ return true;
+
+ if( !DODGameRules()->IsPlayerClassOnTeam( iClass, GetTeamNumber() ) )
+ return false;
+
+ const char *classname = DODGameRules()->GetPlayerClassName( iClass, GetTeamNumber() );
+
+ if( DODGameRules()->CanPlayerJoinClass( this, iClass ) )
+ {
+ m_Shared.SetDesiredPlayerClass( iClass ); //real class value is set when the player spawns
+
+ if( State_Get() == STATE_PICKINGCLASS )
+ State_Transition( STATE_OBSERVER_MODE );
+
+ if( iClass == PLAYERCLASS_RANDOM )
+ {
+ if( IsAlive() )
+ {
+ ClientPrint(this, HUD_PRINTTALK, "#game_respawn_asrandom" );
+ }
+ else
+ {
+ ClientPrint(this, HUD_PRINTTALK, "#game_spawn_asrandom" );
+ }
+ }
+ else
+ {
+ if( IsAlive() )
+ {
+ ClientPrint(this, HUD_PRINTTALK, "#game_respawn_as", classname );
+ }
+ else
+ {
+ ClientPrint(this, HUD_PRINTTALK, "#game_spawn_as", classname );
+ }
+ }
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_changeclass" );
+ if ( event )
+ {
+ event->SetInt( "userid", GetUserID() );
+ event->SetInt( "class", iClass );
+
+ gameeventmanager->FireEvent( event );
+ }
+
+ TallyLatestTimePlayedPerClass( GetTeamNumber(), iOldPlayerClass );
+
+ // if we're near a respawn point and can see it, just spawn us right away
+ if ( ShouldInstantRespawn() )
+ {
+ DODRespawn();
+ m_fNextSuicideTime = gpGlobals->curtime + 1.0;
+
+ // if we dropped our primary weapon, and noone else picked it up, destroy it
+ CBaseCombatWeapon *pWeapon = dynamic_cast<CBaseCombatWeapon *>( m_hLastDroppedWeapon.Get() );
+ if ( pWeapon )
+ {
+ if ( !pWeapon->GetOwner() )
+ {
+ UTIL_Remove( m_hLastDroppedWeapon.Get() );
+ }
+ }
+
+ // if we dropped our primary weapon, and noone else picked it up, destroy it
+ CAmmoBox *pAmmo = dynamic_cast<CAmmoBox *>( m_hLastDroppedAmmoBox.Get() );
+ if ( pAmmo )
+ {
+ UTIL_Remove( pAmmo );
+ }
+ }
+ }
+ else
+ {
+ ClientPrint(this, HUD_PRINTTALK, "#game_class_limit", classname );
+ ShowClassSelectMenu();
+ }
+
+ // if they missed the last wave while choosing class
+ // then restart it now
+ DODGameRules()->CreateOrJoinRespawnWave( this );
+
+ return true;
+}
+
+void CDODPlayer::ShowClassSelectMenu()
+{
+ if ( GetTeamNumber() == TEAM_ALLIES )
+ {
+ ShowViewPortPanel( PANEL_CLASS_ALLIES );
+ }
+ else if ( GetTeamNumber() == TEAM_AXIS )
+ {
+ ShowViewPortPanel( PANEL_CLASS_AXIS );
+ }
+}
+
+void CDODPlayer::CheckRotateIntroCam( void )
+{
+ // Update whatever intro camera it's at.
+ if( m_pIntroCamera && (gpGlobals->curtime >= m_fIntroCamTime) )
+ {
+ MoveToNextIntroCamera();
+ }
+}
+
+int CDODPlayer::GetHealthAsString( char *pDest, int iDestSize )
+{
+ Q_snprintf( pDest, iDestSize, "%d", GetHealth() );
+ return Q_strlen(pDest);
+}
+
+int CDODPlayer::GetLastPlayerIDAsString( char *pDest, int iDestSize )
+{
+ Q_snprintf( pDest, iDestSize, "last player id'd" );
+ return Q_strlen(pDest);
+}
+
+int CDODPlayer::GetClosestPlayerHealthAsString( char *pDest, int iDestSize )
+{
+ Q_snprintf( pDest, iDestSize, "some guy's health" );
+ return Q_strlen(pDest);
+}
+
+int CDODPlayer::GetPlayerClassAsString( char *pDest, int iDestSize )
+{
+ int team = GetTeamNumber();
+
+ if( team == TEAM_ALLIES || team == TEAM_AXIS )
+ {
+ const char *pszClassName = DODGameRules()->GetPlayerClassName( m_Shared.PlayerClass(), GetTeamNumber() );
+
+ Q_snprintf( pDest, iDestSize, "%s", pszClassName );
+ return Q_strlen(pDest);
+ }
+ else
+ {
+ pDest[0] = '\0';
+ return 0;
+ }
+}
+
+int CDODPlayer::GetNearestLocationAsString( char *pDest, int iDestSize )
+{
+ float flMinDist = FLT_MAX;
+ float flDist;
+
+ const char *pLocationName = "";
+
+ CBaseEntity *pEnt = gEntList.FindEntityByClassname( NULL, "dod_control_point" );
+
+ while( pEnt )
+ {
+ Vector vecDelta = GetAbsOrigin() - pEnt->GetAbsOrigin();
+ flDist = vecDelta.Length();
+
+ if( flDist < flMinDist )
+ {
+ CControlPoint *pPoint = static_cast< CControlPoint* >( pEnt );
+ pLocationName = pPoint->GetName();
+ flMinDist = flDist;
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "dod_control_point" );
+ }
+
+ pEnt = gEntList.FindEntityByClassname( NULL, "dod_location" );
+
+ while( pEnt )
+ {
+ Vector vecDelta = GetAbsOrigin() - pEnt->GetAbsOrigin();
+ flDist = vecDelta.Length();
+
+ if( flDist < flMinDist )
+ {
+ CDODLocation *pPoint = static_cast< CDODLocation* >( pEnt );
+ pLocationName = pPoint->GetName();
+ flMinDist = flDist;
+ }
+
+ pEnt = gEntList.FindEntityByClassname( pEnt, "dod_location" );
+ }
+
+ Q_snprintf( pDest, iDestSize, "%s", pLocationName );
+ return Q_strlen(pDest);
+}
+
+int CDODPlayer::GetTimeleftAsString( char *pDest, int iDestSize )
+{
+ if( !DODGameRules()->IsGameUnderTimeLimit() )
+ {
+ Q_snprintf( pDest, iDestSize, "-:--" );
+ return 4;
+ }
+ else
+ {
+
+ int iTimeLeft = DODGameRules()->GetTimeLeft();
+
+ if ( iTimeLeft < 0 )
+ {
+ Q_snprintf( pDest, iDestSize, "0:00" );
+ return 4;
+ }
+
+ int iMinutes = iTimeLeft / 60;
+ int iSeconds = iTimeLeft % 60;
+
+ Q_snprintf( pDest, iDestSize, "%d:%.02d", iMinutes, iSeconds );
+ return Q_strlen(pDest);
+ }
+}
+
+// Copy the result into pDest
+// return value is number of chars copied
+int CDODPlayer::GetStringForEscapeSequence( char c, char *pDest, int iDestSize )
+{
+ int iCharsCopied = 0;
+
+ switch( c )
+ {
+ case 't':
+ iCharsCopied = GetTimeleftAsString( pDest, iDestSize );
+ break;
+ case 'h':
+ iCharsCopied = GetHealthAsString( pDest, iDestSize );
+ break;
+ case 'l':
+ iCharsCopied = GetNearestLocationAsString( pDest, iDestSize );
+ break;
+ case 'c':
+ iCharsCopied = GetPlayerClassAsString( pDest, iDestSize );
+ break;
+ /*case 'i':
+ iCharsCopied = GetLastPlayerIDAsString( pDest, iDestSize );
+ break;
+ case 'r':
+ iCharsCopied = GetClosestPlayerHealthAsString( pDest, iDestSize );
+ break;
+ */
+ default:
+ iCharsCopied = 0;
+ break;
+ }
+
+ return iCharsCopied;
+}
+
+void CDODPlayer::CheckChatText( char *p, int bufsize )
+{
+ //Look for escape sequences and replace
+
+ char *buf = new char[bufsize];
+ int pos = 0;
+
+ // Parse say text for escape sequences
+ for ( char *pSrc = p; pSrc != NULL && *pSrc != 0 && pos < bufsize-1; pSrc++ )
+ {
+ if ( *pSrc == '%' )
+ {
+ char szSubst[32];
+
+ int iResult = GetStringForEscapeSequence( *(pSrc+1), szSubst, sizeof(szSubst) );
+
+ if( iResult )
+ {
+ buf[pos] = '\0';
+ Q_strncat( buf, szSubst, bufsize, COPY_ALL_CHARACTERS );
+
+ pos = MIN( pos + Q_strlen(szSubst), bufsize-1 );
+
+ pSrc++;
+ }
+ else
+ {
+ //just copy in the '%'
+ buf[pos] = *pSrc;
+ pos++;
+ }
+ }
+ else
+ {
+ // copy each char across
+ buf[pos] = *pSrc;
+ pos++;
+ }
+ }
+
+ buf[pos] = '\0';
+
+ // copy buf back into p
+ Q_strncpy( p, buf, bufsize );
+
+ delete[] buf;
+
+ const char *pReadyCheck = p;
+
+ DODGameRules()->CheckChatForReadySignal( this, pReadyCheck );
+}
+
+bool CDODPlayer::ClientCommand( const CCommand &args )
+{
+ const char *pcmd = args[0];
+ if ( FStrEq( pcmd, "jointeam" ) )
+ {
+ if ( args.ArgC() < 2 )
+ {
+ Warning( "Player sent bad jointeam syntax\n" );
+ }
+
+ int iTeam = atoi( args[1] );
+ HandleCommand_JoinTeam( iTeam );
+ return true;
+ }
+ else if( !Q_strncmp( pcmd, "cls_", 4 ) )
+ {
+ CDODTeam *pTeam = GetGlobalDODTeam( GetTeamNumber() );
+
+ Assert( pTeam );
+
+ int iClassIndex = PLAYERCLASS_UNDEFINED;
+
+ if( pTeam->IsClassOnTeam( pcmd, iClassIndex ) )
+ {
+ HandleCommand_JoinClass( iClassIndex );
+ }
+ else
+ {
+ DevMsg( "player tried to join a class that isn't on this team ( %s )\n", pcmd );
+ ShowClassSelectMenu();
+ }
+ return true;
+ }
+ else if ( FStrEq( pcmd, "spectate" ) )
+ {
+ // instantly join spectators
+ HandleCommand_JoinTeam( TEAM_SPECTATOR );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "joingame" ) )
+ {
+ // player just closed MOTD dialog
+ if ( m_iPlayerState == STATE_WELCOME )
+ {
+ State_Transition( STATE_PICKINGTEAM );
+ }
+
+ return true;
+ }
+ else if ( FStrEq( pcmd, "joinclass" ) )
+ {
+ if ( args.ArgC() < 2 )
+ {
+ Warning( "Player sent bad joinclass syntax\n" );
+ }
+
+ int iClass = atoi( args[1] );
+ HandleCommand_JoinClass( iClass );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "dropammo" ) )
+ {
+ DropGenericAmmo();
+ return true;
+ }
+ else if ( FStrEq( pcmd, "drop" ) )
+ {
+ DropActiveWeapon();
+ return true;
+ }
+ else if( !Q_strncmp( pcmd, "voice_", 6 ) )
+ {
+ HandleCommand_Voice( pcmd );
+ return true;
+ }
+ else if( !Q_strncmp( pcmd, "signal_", 7 ) )
+ {
+ HandleCommand_HandSignal( pcmd );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "extendfreeze" ) )
+ {
+ m_flDeathTime += 2.0f;
+ return true;
+ }
+
+#if defined ( DEBUG )
+ else if ( FStrEq( pcmd, "debug" ) )
+ {
+ DODGameRules()->WriteStatsFile( "1.xml" );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "test_stun" ) )
+ {
+ Vector vecForward;
+ AngleVectors( EyeAngles(), &vecForward );
+
+ trace_t tr;
+ UTIL_TraceLine( EyePosition(), EyePosition() + vecForward * 1000, MASK_SOLID, NULL, &tr );
+
+ float flStunAmount = 100;
+ float flStunRadius = 100;
+
+ if ( args.ArgC() > 1 )
+ {
+ flStunAmount = atof( args[1] );
+ }
+
+ if ( args.ArgC() > 2 )
+ {
+ flStunRadius = atof( args[2] );
+ }
+
+ if ( tr.fraction < 1.0 )
+ {
+ CTakeDamageInfo info( this, this, vec3_origin, tr.endpos, flStunAmount, DMG_STUN );
+ DODGameRules()->RadiusStun( info, tr.endpos, flStunRadius );
+ }
+
+ return true;
+ }
+ else if ( FStrEq( pcmd, "switch_prim" ) )
+ {
+ CWeaponDODBase *pWpn = (CWeaponDODBase *)Weapon_GetSlot( WPN_SLOT_PRIMARY );
+
+ Weapon_Switch( pWpn );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "switch_sec" ) )
+ {
+ CWeaponDODBase *pWpn = (CWeaponDODBase *)Weapon_GetSlot( WPN_SLOT_SECONDARY );
+
+ Weapon_Switch( pWpn );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "switch_melee" ) )
+ {
+ CWeaponDODBase *pWpn = (CWeaponDODBase *)Weapon_GetSlot( WPN_SLOT_MELEE );
+
+ Weapon_Switch( pWpn );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "switch_gren" ) )
+ {
+ CWeaponDODBase *pWpn = (CWeaponDODBase *)Weapon_GetSlot( WPN_SLOT_GRENADES );
+
+ Weapon_Switch( pWpn );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "switch_tnt" ) )
+ {
+ CWeaponDODBase *pWpn = (CWeaponDODBase *)Weapon_GetSlot( WPN_SLOT_BOMB );
+
+ Weapon_Switch( pWpn );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "nextwpn" ) )
+ {
+ CBaseCombatWeapon *pWpn = GetActiveWeapon();
+
+ SwitchToNextBestWeapon( pWpn );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "smoke" ) )
+ {
+ CWeaponDODBase *pWpn = (CWeaponDODBase *)Weapon_GetSlot( 3 );
+
+ Weapon_Switch( pWpn );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "resetents" ) )
+ {
+ DODGameRules()->CleanUpMap();
+ return true;
+ }
+ else if( FStrEq( pcmd, "pop" ) )
+ {
+ Vector vecDir(0,0,200);
+
+ if ( args.ArgC() > 1 )
+ {
+ vecDir.z = atof( args[1] );
+ }
+
+ PopHelmet( vecDir, vec3_origin );
+ return true;
+ }
+ else if ( FStrEq( pcmd, "bandage" ) )
+ {
+ if ( sv_cheats->GetBool() )
+ {
+ Bandage();
+ }
+ return true;
+ }
+ else if ( FStrEq( pcmd, "printstats" ) )
+ {
+ PrintLifetimeStats();
+ return true;
+ }
+
+#endif //_DEBUG
+
+ return BaseClass::ClientCommand( args );
+}
+
+// returns true if the selection has been handled and the player's menu
+// can be closed...false if the menu should be displayed again
+bool CDODPlayer::HandleCommand_JoinTeam( int team )
+{
+ CDODGameRules *mp = DODGameRules();
+ int iOldTeam = GetTeamNumber();
+ int iOldPlayerClass = m_Shared.DesiredPlayerClass();
+
+ if ( !GetGlobalTeam( team ) )
+ {
+ Warning( "HandleCommand_JoinTeam( %d ) - invalid team index.\n", team );
+ return false;
+ }
+
+ // If we already died and changed teams once, deny
+ if( m_bTeamChanged && team != TEAM_SPECTATOR && iOldTeam != TEAM_SPECTATOR )
+ {
+ ClientPrint( this, HUD_PRINTCENTER, "game_switch_teams_once" );
+ return true;
+ }
+
+ if ( team == TEAM_UNASSIGNED )
+ {
+ // Attempt to auto-select a team, may set team to T, CT or SPEC
+ team = mp->SelectDefaultTeam();
+
+ if ( team == TEAM_UNASSIGNED )
+ {
+ // still team unassigned, try to kick a bot if possible
+
+ ClientPrint( this, HUD_PRINTTALK, "#All_Teams_Full" );
+
+ team = TEAM_SPECTATOR;
+ }
+ }
+
+ if ( team == iOldTeam )
+ return true; // we wouldn't change the team
+
+ if ( mp->TeamFull( team ) )
+ {
+ if ( team == TEAM_ALLIES )
+ {
+ ClientPrint( this, HUD_PRINTTALK, "#Allies_Full" );
+ }
+ else if ( team == TEAM_AXIS )
+ {
+ ClientPrint( this, HUD_PRINTTALK, "#Axis_Full" );
+ }
+
+ ShowViewPortPanel( PANEL_TEAM );
+ return false;
+ }
+
+ if ( team == TEAM_SPECTATOR )
+ {
+ // Prevent this if the cvar is set
+ if ( !mp_allowspectators.GetInt() && !IsHLTV() )
+ {
+ ClientPrint( this, HUD_PRINTTALK, "#Cannot_Be_Spectator" );
+ ShowViewPortPanel( PANEL_TEAM );
+ return false;
+ }
+
+ ChangeTeam( TEAM_SPECTATOR );
+
+ return true;
+ }
+
+ // If the code gets this far, the team is not TEAM_UNASSIGNED
+
+ // Player is switching to a new team (It is possible to switch to the
+ // same team just to choose a new appearance)
+
+ if (mp->TeamStacked( team, GetTeamNumber() ))//players are allowed to change to their own team so they can just change their model
+ {
+ // The specified team is full
+ ClientPrint(
+ this,
+ HUD_PRINTCENTER,
+ ( team == TEAM_ALLIES ) ? "#Allies_full" : "#Axis_full" );
+
+ ShowViewPortPanel( PANEL_TEAM );
+ return false;
+ }
+
+ // Show the appropriate Choose Appearance menu
+ // This must come before ClientKill() for CheckWinConditions() to function properly
+
+ // Switch their actual team...
+ ChangeTeam( team );
+
+ DODGameRules()->CreateOrJoinRespawnWave( this );
+
+ // Force them to choose a new class
+ m_Shared.SetDesiredPlayerClass( PLAYERCLASS_UNDEFINED );
+ m_Shared.SetPlayerClass( PLAYERCLASS_UNDEFINED );
+
+ TallyLatestTimePlayedPerClass( iOldTeam, iOldPlayerClass );
+
+ return true;
+}
+
+void CDODPlayer::State_Transition( DODPlayerState newState )
+{
+ State_Leave();
+ State_Enter( newState );
+}
+
+
+void CDODPlayer::State_Enter( DODPlayerState newState )
+{
+ m_iPlayerState = newState;
+ m_pCurStateInfo = State_LookupInfo( newState );
+
+ if ( dod_playerstatetransitions.GetInt() == -1 || dod_playerstatetransitions.GetInt() == entindex() )
+ {
+ if ( m_pCurStateInfo )
+ Msg( "ShowStateTransitions: entering '%s'\n", m_pCurStateInfo->m_pStateName );
+ else
+ Msg( "ShowStateTransitions: entering #%d\n", newState );
+ }
+
+ // Initialize the new state.
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnEnterState )
+ (this->*m_pCurStateInfo->pfnEnterState)();
+}
+
+void CDODPlayer::State_Leave()
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnLeaveState )
+ {
+ (this->*m_pCurStateInfo->pfnLeaveState)();
+ }
+}
+
+
+void CDODPlayer::State_PreThink()
+{
+ if ( m_pCurStateInfo && m_pCurStateInfo->pfnPreThink )
+ {
+ (this->*m_pCurStateInfo->pfnPreThink)();
+ }
+}
+
+
+CDODPlayerStateInfo* CDODPlayer::State_LookupInfo( DODPlayerState state )
+{
+ // This table MUST match the
+ static CDODPlayerStateInfo playerStateInfos[] =
+ {
+ { STATE_ACTIVE, "STATE_ACTIVE", &CDODPlayer::State_Enter_ACTIVE, NULL, &CDODPlayer::State_PreThink_ACTIVE },
+ { STATE_WELCOME, "STATE_WELCOME", &CDODPlayer::State_Enter_WELCOME, NULL, &CDODPlayer::State_PreThink_WELCOME },
+ { STATE_PICKINGTEAM, "STATE_PICKINGTEAM", &CDODPlayer::State_Enter_PICKINGTEAM, NULL, &CDODPlayer::State_PreThink_PICKING },
+ { STATE_PICKINGCLASS, "STATE_PICKINGCLASS", &CDODPlayer::State_Enter_PICKINGCLASS, NULL, &CDODPlayer::State_PreThink_PICKING },
+ { STATE_DEATH_ANIM, "STATE_DEATH_ANIM", &CDODPlayer::State_Enter_DEATH_ANIM, NULL, &CDODPlayer::State_PreThink_DEATH_ANIM },
+ { STATE_OBSERVER_MODE, "STATE_OBSERVER_MODE", &CDODPlayer::State_Enter_OBSERVER_MODE, NULL, &CDODPlayer::State_PreThink_OBSERVER_MODE }
+ };
+
+ for ( int i=0; i < ARRAYSIZE( playerStateInfos ); i++ )
+ {
+ if ( playerStateInfos[i].m_iPlayerState == state )
+ return &playerStateInfos[i];
+ }
+
+ return NULL;
+}
+
+void CDODPlayer::PhysObjectSleep()
+{
+ IPhysicsObject *pObj = VPhysicsGetObject();
+ if ( pObj )
+ pObj->Sleep();
+}
+
+void CDODPlayer::PhysObjectWake()
+{
+ IPhysicsObject *pObj = VPhysicsGetObject();
+ if ( pObj )
+ pObj->Wake();
+}
+
+void CDODPlayer::State_Enter_WELCOME()
+{
+ SetMoveType( MOVETYPE_NONE );
+ AddSolidFlags( FSOLID_NOT_SOLID );
+
+ PhysObjectSleep();
+
+ // Show info panel
+ if ( IsBot() )
+ {
+ // If they want to auto join a team for debugging, pretend they clicked the button.
+ CCommand args;
+ args.Tokenize( "joingame" );
+ ClientCommand( args );
+ }
+ else
+ {
+ const ConVar *hostname = cvar->FindVar( "hostname" );
+ const char *title = (hostname) ? hostname->GetString() : "MESSAGE OF THE DAY";
+
+ KeyValues *data = new KeyValues("data");
+ data->SetString( "title", title ); // info panel title
+ data->SetString( "type", "1" ); // show userdata from stringtable entry
+ data->SetString( "msg", "motd" ); // use this stringtable entry
+ data->SetInt( "cmd", TEXTWINDOW_CMD_CHANGETEAM ); // exec this command if panel closed
+ data->SetBool( "unload", sv_motd_unload_on_dismissal.GetBool() );
+
+ ShowViewPortPanel( PANEL_INFO, true, data );
+
+ data->deleteThis();
+ }
+}
+
+
+void CDODPlayer::State_PreThink_WELCOME()
+{
+ // Verify some state.
+ Assert( GetMoveType() == MOVETYPE_NONE );
+ Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
+ Assert( GetAbsVelocity().Length() == 0 );
+
+ CheckRotateIntroCam();
+}
+
+
+void CDODPlayer::State_Enter_PICKINGTEAM()
+{
+ ShowViewPortPanel( PANEL_TEAM );
+}
+
+void CDODPlayer::State_PreThink_PICKING()
+{
+}
+
+void CDODPlayer::State_Enter_DEATH_ANIM()
+{
+ if ( HasWeapons() )
+ {
+ // we drop the guns here because weapons that have an area effect and can kill their user
+ // will sometimes crash coming back from CBasePlayer::Killed() if they kill their owner because the
+ // player class sometimes is freed. It's safer to manipulate the weapons once we know
+ // we aren't calling into any of their code anymore through the player pointer.
+ PackDeadPlayerItems();
+ }
+
+ // Used for a timer.
+ m_flDeathTime = gpGlobals->curtime;
+
+ StartObserverMode( OBS_MODE_DEATHCAM ); // go to observer mode
+
+ RemoveEffects( EF_NODRAW ); // still draw player body
+
+ DODGameRules()->CreateOrJoinRespawnWave( this );
+
+ m_bAbortFreezeCam = false;
+
+ m_bPlayedFreezeCamSound = false;
+}
+
+#define DOD_DEATH_ANIMATION_TIME 1.6f
+#define DOD_FREEZECAM_LENGTH 3.4f
+
+void CDODPlayer::State_PreThink_DEATH_ANIM()
+{
+ // If the anim is done playing, go to the next state (waiting for a keypress to
+ // either respawn the guy or put him into observer mode).
+ if ( GetFlags() & FL_ONGROUND )
+ {
+ float flForward = GetAbsVelocity().Length() - 20;
+ if (flForward <= 0)
+ {
+ SetAbsVelocity( vec3_origin );
+ }
+ else
+ {
+ Vector vAbsVel = GetAbsVelocity();
+ VectorNormalize( vAbsVel );
+ vAbsVel *= flForward;
+ SetAbsVelocity( vAbsVel );
+ }
+ }
+
+ if ( dod_freezecam.GetBool() )
+ {
+ // important! freeze end time must match DEATH_CAM_TIME
+ // or else players will start missing respawn waves.
+
+ float flFreezeEnd = (m_flDeathTime + DOD_DEATH_ANIMATION_TIME + DOD_FREEZECAM_LENGTH );
+
+ if ( !m_bPlayedFreezeCamSound && GetObserverTarget() && GetObserverTarget() != this )
+ {
+ // Start the sound so that it ends at the freezecam lock on time
+ float flFreezeSoundLength = 0.3;
+ float flFreezeSoundTime = m_flDeathTime + DOD_DEATH_ANIMATION_TIME - flFreezeSoundLength + 0.4f /* travel time */;
+ if ( gpGlobals->curtime >= flFreezeSoundTime )
+ {
+ CSingleUserRecipientFilter filter( this );
+ EmitSound_t params;
+ params.m_flSoundTime = 0;
+ params.m_pSoundName = "Player.FreezeCam";
+ EmitSound( filter, entindex(), params );
+
+ m_bPlayedFreezeCamSound = true;
+ }
+ }
+
+ if ( gpGlobals->curtime >= (m_flDeathTime + DOD_DEATH_ANIMATION_TIME ) ) // allow 2 seconds death animation / death cam
+ {
+ if ( GetObserverTarget() && GetObserverTarget() != this )
+ {
+ if ( !m_bAbortFreezeCam && gpGlobals->curtime < flFreezeEnd )
+ {
+ if ( GetObserverMode() != OBS_MODE_FREEZECAM )
+ {
+ StartObserverMode( OBS_MODE_FREEZECAM );
+ PhysObjectSleep();
+ }
+ return;
+ }
+ }
+
+ if ( GetObserverMode() == OBS_MODE_FREEZECAM )
+ {
+ // If we're in freezecam, and we want out, abort.
+ if ( m_bAbortFreezeCam )
+ {
+ m_lifeState = LIFE_DEAD;
+
+ StopAnimation();
+
+ IncrementInterpolationFrame();
+
+ if ( GetMoveType() != MOVETYPE_NONE && (GetFlags() & FL_ONGROUND) )
+ SetMoveType( MOVETYPE_NONE );
+
+ State_Transition( STATE_OBSERVER_MODE );
+ }
+ }
+
+ // Don't allow anyone to respawn until freeze time is over, even if they're not
+ // in freezecam. This prevents players skipping freezecam to spawn faster.
+
+ if ( gpGlobals->curtime >= flFreezeEnd )
+ {
+ m_lifeState = LIFE_DEAD;
+
+ StopAnimation();
+
+ IncrementInterpolationFrame();
+
+ if ( GetMoveType() != MOVETYPE_NONE && (GetFlags() & FL_ONGROUND) )
+ SetMoveType( MOVETYPE_NONE );
+
+ State_Transition( STATE_OBSERVER_MODE );
+ }
+ }
+ }
+ else
+ {
+ if ( gpGlobals->curtime >= (m_flDeathTime + DEATH_CAM_TIME ) ) // allow x seconds death animation / death cam
+ {
+ m_lifeState = LIFE_DEAD;
+
+ StopAnimation();
+
+ IncrementInterpolationFrame();
+
+ if ( GetMoveType() != MOVETYPE_NONE && (GetFlags() & FL_ONGROUND) )
+ SetMoveType( MOVETYPE_NONE );
+
+ // Disabled death cam!
+ //StartReplayMode( 8, 8, entindex() );
+
+ State_Transition( STATE_OBSERVER_MODE );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODPlayer::AttemptToExitFreezeCam( void )
+{
+ float flFreezeTravelTime = (m_flDeathTime + DOD_DEATH_ANIMATION_TIME ) + 0.4 /*spec_freeze_traveltime.GetFloat()*/ + 0.5;
+ if ( gpGlobals->curtime < flFreezeTravelTime )
+ return;
+
+ m_bAbortFreezeCam = true;
+}
+
+void CDODPlayer::State_Enter_OBSERVER_MODE()
+{
+ // Always start a spectator session in chase mode
+ m_iObserverLastMode = OBS_MODE_CHASE;
+
+ if( m_hObserverTarget == NULL )
+ {
+ // find a new observer target
+ CheckObserverSettings();
+ }
+
+ // Change our observer target to the player nearest us
+ CTeam *pTeam = GetGlobalTeam( TEAM_ALLIES );
+
+ CBasePlayer *pPlayer;
+ Vector localOrigin = GetAbsOrigin();
+ Vector targetOrigin;
+ float flMinDist = FLT_MAX;
+ float flDist;
+
+ for ( int i=0;i<pTeam->GetNumPlayers();i++ )
+ {
+ pPlayer = pTeam->GetPlayer(i);
+
+ if ( !pPlayer )
+ continue;
+
+ if ( !IsValidObserverTarget(pPlayer) )
+ continue;
+
+ targetOrigin = pPlayer->GetAbsOrigin();
+
+ flDist = ( targetOrigin - localOrigin ).Length();
+
+ if ( flDist < flMinDist )
+ {
+ m_hObserverTarget.Set( pPlayer );
+ flMinDist = flDist;
+ }
+ }
+
+ if ( GetTeamNumber() == TEAM_ALLIES || GetTeamNumber() == TEAM_AXIS )
+ {
+ // we are entering spec while playing ( not on TEAM_SPECTATOR )
+ Hints()->HintMessage( HINT_CLASSMENU, true );
+ }
+
+ StartObserverMode( m_iObserverLastMode );
+
+ PhysObjectSleep();
+}
+
+void CDODPlayer::State_PreThink_OBSERVER_MODE()
+{
+ // Make sure nobody has changed any of our state.
+ Assert( m_takedamage == DAMAGE_NO );
+ Assert( IsSolidFlagSet( FSOLID_NOT_SOLID ) );
+
+ // Must be dead.
+ Assert( m_lifeState == LIFE_DEAD );
+ Assert( pl.deadflag );
+}
+
+void CDODPlayer::State_Enter_PICKINGCLASS()
+{
+ // go to spec mode, if dying keep deathcam
+ if ( GetObserverMode() == OBS_MODE_DEATHCAM )
+ {
+ StartObserverMode( OBS_MODE_DEATHCAM );
+ }
+ else
+ {
+ StartObserverMode( OBS_MODE_ROAMING );
+ }
+
+ PhysObjectSleep();
+
+ ShowClassSelectMenu();
+}
+
+void CDODPlayer::State_Enter_ACTIVE()
+{
+ SetMoveType( MOVETYPE_WALK );
+ RemoveSolidFlags( FSOLID_NOT_SOLID );
+ m_Local.m_iHideHUD = 0;
+ PhysObjectWake();
+}
+
+void CDODPlayer::State_PreThink_ACTIVE()
+{
+ return;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Initialize prone at spawn.
+//-----------------------------------------------------------------------------
+void CDODPlayer::InitProne( void )
+{
+ m_Shared.SetProne( false, true );
+}
+
+void CDODPlayer::InitSprinting( void )
+{
+ m_Shared.SetSprinting( false );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether or not we are allowed to sprint now.
+//-----------------------------------------------------------------------------
+bool CDODPlayer::CanSprint()
+{
+ return (
+ //!IsWalking() && // Not if we're walking
+ !( m_Local.m_bDucked && !m_Local.m_bDucking ) && // Nor if we're ducking
+ (GetWaterLevel() != 3) ); // Certainly not underwater
+}
+
+void CDODPlayer::MoveToNextIntroCamera()
+{
+ m_pIntroCamera = gEntList.FindEntityByClassname( m_pIntroCamera, "point_viewcontrol" );
+
+ // if m_pIntroCamera is NULL we just were at end of list, start searching from start again
+ if(!m_pIntroCamera)
+ m_pIntroCamera = gEntList.FindEntityByClassname(m_pIntroCamera, "point_viewcontrol");
+
+ if( !m_pIntroCamera ) //if there are no cameras find a spawn point and black out the screen
+ {
+ DODGameRules()->GetPlayerSpawnSpot( this );
+ SetAbsAngles( QAngle( 0, 0, 0 ) );
+ m_pIntroCamera = NULL; // never update again
+ }
+ else
+ {
+ Vector vIntroCamera = m_pIntroCamera->GetAbsOrigin();
+
+ QAngle CamAngles = m_pIntroCamera->GetAbsAngles();
+
+ UTIL_SetSize( this, vec3_origin, vec3_origin );
+
+ SetAbsOrigin( vIntroCamera );
+ SetAbsAngles( CamAngles );
+ SnapEyeAngles( CamAngles );
+ SetViewOffset( vec3_origin );
+ m_fIntroCamTime = gpGlobals->curtime + 6;
+ }
+}
+
+CBaseEntity *CDODPlayer::SelectSpawnSpot( CUtlVector<EHANDLE> *pSpawnPoints, int &iLastSpawnIndex )
+{
+ Assert( pSpawnPoints );
+
+ int iNumSpawns = pSpawnPoints->Count();
+
+ CBaseEntity *pSpot = NULL;
+
+ for ( int i=0;i<iNumSpawns;i++ )
+ {
+ int testIndex = ( iLastSpawnIndex + i ) % iNumSpawns;
+
+ EHANDLE handle = pSpawnPoints->Element(testIndex);
+
+ if ( !handle )
+ continue;
+
+ pSpot = handle.Get();
+
+ if ( !pSpot )
+ continue;
+
+ if( g_pGameRules->IsSpawnPointValid( pSpot, this ) )
+ {
+ if ( pSpot->GetAbsOrigin() == Vector( 0, 0, 0 ) )
+ {
+ continue;
+ }
+
+ // if so, go to pSpot
+ iLastSpawnIndex = testIndex;
+ return pSpot;
+ }
+ }
+
+ DevMsg("CDODPlayer::SelectSpawnSpot: couldn't find valid spawn point.\n");
+
+ // If we get here, all spawn points are blocked.
+ // Try the 4 locations around each spawn point
+
+ Assert( pSpot );
+
+ Vector vecForward, vecRight, vecUp;
+
+ Vector mins = VEC_HULL_MIN;
+ Vector maxs = VEC_HULL_MAX;
+ float flHalfWidth = maxs.x;
+ float flTestDist = 3 * flHalfWidth;
+
+ for ( int i=0;i<iNumSpawns;i++ )
+ {
+ int testIndex = ( iLastSpawnIndex + i ) % iNumSpawns;
+
+ EHANDLE handle = pSpawnPoints->Element(testIndex);
+
+ if ( !handle )
+ continue;
+
+ pSpot = handle.Get();
+
+ if ( !pSpot )
+ continue;
+
+ // we know this spot is blocked, but check to the N, E, S, W of it.
+
+ // if we find a clear spot, create a new spawn point there, copied from pSpot
+
+ AngleVectors( pSpot->GetAbsAngles(), &vecForward, &vecRight, &vecUp );
+
+ for( int i=0;i<4;i++ )
+ {
+ Vector origin = pSpot->GetAbsOrigin();
+
+ switch( i )
+ {
+ case 0: origin += vecForward * flTestDist; break;
+ case 1: origin += vecRight * flTestDist; break;
+ case 2: origin -= vecForward * flTestDist; break;
+ case 3: origin -= vecRight * flTestDist; break;
+ }
+
+ Vector vTestMins = origin + mins;
+ Vector vTestMaxs = origin + maxs;
+
+ if ( UTIL_IsSpaceEmpty( this, vTestMins, vTestMaxs ) )
+ {
+ QAngle spotAngles = pSpot->GetAbsAngles();
+
+ // make a new spawnpoint so we don't have to do this a bunch of times
+ pSpot = CreateEntityByName( pSpot->GetClassname() );
+ pSpot->SetAbsOrigin( origin );
+ pSpot->SetAbsAngles( spotAngles );
+
+ // delete it in a while so we don't accumulate entities
+ pSpot->SetThink( &CBaseEntity::SUB_Remove );
+ pSpot->SetNextThink( gpGlobals->curtime + 0.5 );
+
+ Assert( pSpot );
+
+ iLastSpawnIndex = 0;
+
+ return pSpot;
+ }
+ }
+ }
+
+ // if we get here, we're really screwed. Spawning the person is going to stick them
+ // into someone or something, but we really tried hard, I swear.
+ Assert( !"We should never be here" );
+
+ return NULL;
+}
+
+
+CBaseEntity* CDODPlayer::EntSelectSpawnPoint()
+{
+ CBaseEntity *pSpot = NULL;
+
+ switch( GetTeamNumber() )
+ {
+ case TEAM_ALLIES:
+ {
+ CUtlVector<EHANDLE> *pSpawnList = DODGameRules()->GetSpawnPointListForTeam( TEAM_ALLIES );
+ pSpot = SelectSpawnSpot( pSpawnList, g_iLastAlliesSpawnIndex );
+ }
+ break;
+ case TEAM_AXIS:
+ {
+ CUtlVector<EHANDLE> *pSpawnList = DODGameRules()->GetSpawnPointListForTeam( TEAM_AXIS );
+ pSpot = SelectSpawnSpot( pSpawnList, g_iLastAxisSpawnIndex );
+ }
+ break;
+ case TEAM_SPECTATOR:
+ case TEAM_UNASSIGNED:
+ default:
+ {
+ pSpot = CBaseEntity::Instance( INDEXENT(0) );
+ }
+ break;
+ }
+
+ if ( !pSpot )
+ {
+ Warning( "PutClientInServer: no valid spawns on level\n" );
+ return CBaseEntity::Instance( INDEXENT(0) );
+ }
+
+ return pSpot;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Put the player in the specified team
+//-----------------------------------------------------------------------------
+void CDODPlayer::ChangeTeam( int iTeamNum )
+{
+ if ( !GetGlobalTeam( iTeamNum ) )
+ {
+ Warning( "CDODPlayer::ChangeTeam( %d ) - invalid team index.\n", iTeamNum );
+ return;
+ }
+
+ int iOldTeam = GetTeamNumber();
+
+ // if this is our current team, just abort
+ if ( iTeamNum == iOldTeam )
+ return;
+
+ m_bTeamChanged = true;
+
+ // do the team change:
+ BaseClass::ChangeTeam( iTeamNum );
+
+ // update client state
+ if ( iTeamNum == TEAM_UNASSIGNED )
+ {
+ State_Transition( STATE_OBSERVER_MODE );
+ }
+ else if ( iTeamNum == TEAM_SPECTATOR )
+ {
+ RemoveAllItems( true );
+
+ State_Transition( STATE_OBSERVER_MODE );
+
+ StatEvent_UploadStats();
+ m_StatProperty.SetClassAndTeamForThisLife( -1, TEAM_SPECTATOR );
+ }
+ else // active player
+ {
+ if ( !IsDead() )
+ {
+ // Kill player if switching teams while alive
+ CommitSuicide();
+
+ // add 1 to frags to balance out the 1 subtracted for killing yourself
+ // IncrementFragCount( 1 );
+ }
+
+ if( iOldTeam == TEAM_SPECTATOR )
+ SetMoveType( MOVETYPE_NONE );
+
+ // Put up the class selection menu.
+ State_Transition( STATE_PICKINGCLASS );
+ }
+
+ RemoveNemesisRelationships();
+}
+
+void CDODPlayer::DODRespawn( void )
+{
+ // if it's a forced respawn, accumulate the stats now
+ if ( IsAlive() )
+ {
+ StatEvent_UploadStats();
+ }
+
+ RemoveAllItems(true);
+
+ StopObserverMode();
+
+ State_Transition( STATE_ACTIVE );
+ Spawn();
+ m_nButtons = 0;
+ SetNextThink( TICK_NEVER_THINK );
+
+ OutputDamageGiven();
+ OutputDamageTaken();
+ ResetDamageCounters();
+}
+
+bool CDODPlayer::IsReadyToPlay( void )
+{
+ bool bResult = ( ( GetTeamNumber() == TEAM_ALLIES || GetTeamNumber() == TEAM_AXIS ) &&
+ m_Shared.DesiredPlayerClass() != PLAYERCLASS_UNDEFINED );
+
+ return bResult;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Returns whether or not we can switch to the given weapon.
+// Input : pWeapon -
+//-----------------------------------------------------------------------------
+bool CDODPlayer::Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon )
+{
+#if !defined( CLIENT_DLL )
+ IServerVehicle *pVehicle = GetVehicle();
+#else
+ IClientVehicle *pVehicle = GetVehicle();
+#endif
+
+ if (pVehicle && !UsingStandardWeaponsInVehicle())
+ return false;
+
+ if ( !pWeapon->CanDeploy() )
+ return false;
+
+ if ( GetActiveWeapon() )
+ {
+ if ( !GetActiveWeapon()->CanHolster() )
+ return false;
+ }
+
+ return true;
+}
+
+void CDODPlayer::SetScore( int score )
+{
+ m_iScore = score;
+}
+
+void CDODPlayer::AddScore( int num )
+{
+ int n = MAX( 0, num );
+ m_iScore += n;
+}
+
+void CDODPlayer::HandleSignals( void )
+{
+ int changed = m_signals.Update();
+ int state = m_signals.GetState();
+
+ if ( changed & SIGNAL_CAPTUREAREA )
+ {
+ if ( state & SIGNAL_CAPTUREAREA )
+ {
+ //do nothing
+ }
+ else
+ {
+ SetCapAreaIndex(-1);
+ SetCPIndex(-1);
+ m_iCapAreaIconIndex = -1;
+ }
+ }
+}
+
+void CDODPlayer::SetCapAreaIndex( int index )
+{
+ m_iCapAreaIndex = index;
+}
+
+int CDODPlayer::GetCapAreaIndex( void )
+{
+ return m_iCapAreaIndex;
+}
+
+void CDODPlayer::SetCPIndex( int index )
+{
+ m_Shared.SetCPIndex( index );
+}
+
+int CDODPlayer::OnTakeDamage_Alive( const CTakeDamageInfo &info )
+{
+ // set damage type sustained
+ m_bitsDamageType |= info.GetDamageType();
+
+ int iInitialHealth = m_iHealth;
+
+ if ( !CBaseCombatCharacter::OnTakeDamage_Alive( info ) )
+ return 0;
+
+ IGameEvent * event = gameeventmanager->CreateEvent( "player_hurt" );
+
+ if ( event )
+ {
+ event->SetInt("userid", GetUserID() );
+ event->SetInt("health", MAX(0, m_iHealth) );
+ event->SetInt("damage", info.GetDamage() );
+ event->SetInt("hitgroup", m_LastHitGroup );
+
+ CBaseEntity *attacker = info.GetAttacker();
+ const char *weaponName = "";
+ DODWeaponID weaponID = WEAPON_NONE;
+
+ if ( attacker->IsPlayer() )
+ {
+ CBasePlayer *player = ToBasePlayer( attacker );
+ event->SetInt("attacker", player->GetUserID() ); // hurt by other player
+
+ // ff damage
+ // no hint for bomb explosions, it was their own fault!
+ CDODPlayer *pDODPlayer = ToDODPlayer( player );
+ if( pDODPlayer->GetTeamNumber() == GetTeamNumber() && pDODPlayer != this && !FBitSet( info.GetDamageType(), DMG_BOMB ) )
+ {
+ pDODPlayer->Hints()->HintMessage( HINT_FRIEND_INJURED );
+ }
+
+ CBaseEntity *pInflictor = info.GetInflictor();
+ if ( pInflictor )
+ {
+ if ( pInflictor == pDODPlayer )
+ {
+ // If the inflictor is the killer, then it must be their current weapon doing the damage
+ if ( pDODPlayer->GetActiveWeapon() )
+ {
+ CWeaponDODBase *pWeapon = pDODPlayer->GetActiveDODWeapon();
+ weaponName = pWeapon->GetClassname();
+
+ if ( info.GetDamageCustom() & MELEE_DMG_SECONDARYATTACK )
+ weaponID = pWeapon->GetAltWeaponID();
+ else
+ weaponID = pWeapon->GetStatsWeaponID();
+ }
+ }
+ else
+ {
+ weaponName = STRING( pInflictor->m_iClassname ); // it's just that easy
+ }
+ }
+ }
+ else
+ {
+ event->SetInt("attacker", 0 ); // hurt by "world"
+ }
+
+ if ( strncmp( weaponName, "weapon_", 7 ) == 0 )
+ {
+ weaponName += 7;
+ }
+ else if ( strncmp( weaponName, "rocket_", 7 ) == 0 )
+ {
+ weaponName += 7;
+
+ CDODBaseRocket *pRocket = dynamic_cast< CDODBaseRocket *>( info.GetInflictor() );
+ if ( pRocket )
+ {
+ weaponID = pRocket->GetEmitterWeaponID();
+ }
+ }
+ else if ( strncmp( weaponName, "grenade_", 8 ) == 0 )
+ {
+ weaponName += 8;
+
+ CDODBaseGrenade *pGren = dynamic_cast< CDODBaseGrenade *>( info.GetInflictor() );
+ if ( pGren )
+ {
+ weaponID = pGren->GetEmitterWeaponID();
+ }
+ }
+ else if ( Q_stricmp( weaponName, "dod_bomb_target" ) == 0 )
+ {
+ weaponID = WEAPON_NONE;
+ }
+
+ event->SetString( "weapon", weaponName );
+ event->SetInt( "priority", 5 );
+
+ gameeventmanager->FireEvent( event );
+
+#ifndef CLIENT_DLL
+ IGameEvent * stats_event = gameeventmanager->CreateEvent( "dod_stats_player_damage" );
+ if ( stats_event && attacker->IsPlayer() && weaponID != WEAPON_NONE )
+ {
+ CBasePlayer *pAttacker = ToBasePlayer( attacker );
+
+ int iDamage = ( info.GetDamage() + 0.5f ); // round to nearest integer
+
+ stats_event->SetInt( "attacker", pAttacker->GetUserID() );
+ stats_event->SetInt( "victim", GetUserID() );
+ stats_event->SetInt( "weapon", weaponID );
+ stats_event->SetInt( "damage", iDamage );
+
+ // damage_given is the amount of damage applied, not to exceed how much life we have
+ stats_event->SetInt( "damage_given", MIN( iDamage, iInitialHealth ) );
+
+ CBaseEntity *pInflictor = info.GetInflictor();
+ float flDist = ( pInflictor->GetAbsOrigin() - GetAbsOrigin() ).Length();
+ stats_event->SetFloat( "distance", flDist );
+
+ stats_event->SetInt("hitgroup", m_LastHitGroup );
+
+ gameeventmanager->FireEvent( stats_event );
+ }
+#endif //CLIENT_DLL
+ }
+
+ return 1;
+}
+
+ConVar dod_explosionforcescale( "dod_explosionforcescale", "1.0", FCVAR_CHEAT );
+ConVar dod_bulletforcescale( "dod_bulletforcescale", "1.0", FCVAR_CHEAT );
+
+int CDODPlayer::OnTakeDamage( const CTakeDamageInfo &inputInfo )
+{
+ CTakeDamageInfo info = inputInfo;
+
+ CBaseEntity *pInflictor = info.GetInflictor();
+
+ if ( !pInflictor )
+ return 0;
+
+ if ( GetMoveType() == MOVETYPE_NOCLIP )
+ return 0;
+
+ // if the player's team does not match the inflictor's team
+ // the player has changed teams between when they started the attack
+ if( pInflictor->GetTeamNumber() != TEAM_UNASSIGNED &&
+ info.GetAttacker() != NULL &&
+ pInflictor->GetTeamNumber() != info.GetAttacker()->GetTeamNumber() )
+ {
+ info.SetDamage( 0 );
+ info.SetDamageType( 0 );
+ }
+
+ bool bFriendlyFire = DODGameRules()->IsFriendlyFireOn();
+
+ if ( bFriendlyFire ||
+ info.GetAttacker()->GetTeamNumber() != GetTeamNumber() ||
+ pInflictor == this ||
+ info.GetAttacker() == this ||
+ info.GetDamageType() & DMG_BOMB )
+ {
+ // Do special stun damage effect
+ if ( info.GetDamageType() & DMG_STUN )
+ {
+ OnDamageByStun( info );
+ return 0;
+ }
+
+ CDODPlayer *pPlayer = ToDODPlayer( info.GetAttacker() );
+
+ // keep track of amount of damage last sustained
+ m_lastDamageAmount = info.GetDamage();
+ m_LastDamageType = info.GetDamageType();
+
+ if( !FBitSet( info.GetDamageType(), DMG_FALL ) )
+ Pain();
+
+ float flForceScale = 1.0;
+
+ // Do special explosion damage effect
+ if ( info.GetDamageType() & DMG_BLAST )
+ {
+ OnDamagedByExplosion( info );
+ flForceScale = dod_explosionforcescale.GetFloat();
+ }
+
+ if( info.GetDamageType() & DMG_BULLET )
+ {
+ flForceScale = dod_bulletforcescale.GetFloat();
+ }
+
+ // round up!
+ int iDamage = (int)( info.GetDamage() + 0.5 );
+
+ if ( pPlayer )
+ {
+ // Record for the shooter
+ pPlayer->RecordDamageGiven( this, iDamage );
+
+ // And for the victim
+ RecordDamageTaken( pPlayer, iDamage );
+ }
+ else
+ {
+ RecordWorldDamageTaken( iDamage );
+ }
+
+ m_vecTotalBulletForce += info.GetDamageForce() * flForceScale;
+
+ CSingleUserRecipientFilter user( this );
+ user.MakeReliable();
+ UserMessageBegin( user, "Damage" );
+ WRITE_BYTE( (int)info.GetDamage() );
+ WRITE_LONG( info.GetDamageType() );
+ WRITE_VEC3COORD( info.GetInflictor()->WorldSpaceCenter() );
+ MessageEnd();
+
+ gamestats->Event_PlayerDamage( this, info );
+
+ return CBaseCombatCharacter::OnTakeDamage( info );
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+void CDODPlayer::Pain( void )
+{
+ if ( m_LastDamageType & DMG_CLUB)
+ {
+ EmitSound( "Player.MajorPain" );
+ }
+ else if ( m_LastDamageType & DMG_BLAST )
+ {
+ EmitSound( "Player.MajorPain" );
+ }
+ else
+ {
+ EmitSound( "Player.MinorPain" );
+ }
+}
+
+void CDODPlayer::DeathSound( const CTakeDamageInfo &info )
+{
+ if ( m_LastDamageType & DMG_CLUB )
+ {
+ EmitSound( "Player.MegaPain" );
+ }
+ else if ( m_LastDamageType & DMG_BLAST )
+ {
+ EmitSound( "Player.MegaPain" );
+ }
+ else if ( m_LastHitGroup == HITGROUP_HEAD )
+ {
+ EmitSound( "Player.DeathHeadShot" );
+ }
+ else
+ {
+ EmitSound( "Player.MinorPain" );
+ }
+}
+
+void CDODPlayer::OnDamagedByExplosion( const CTakeDamageInfo &info )
+{
+ if ( info.GetDamage() >= 30.0f )
+ {
+ SetContextThink( &CDODPlayer::DeafenThink, gpGlobals->curtime + 0.3, DOD_DEAFEN_CONTEXT );
+
+ // The blast will naturally blow the temp ent helmet away
+ PopHelmet( info.GetDamagePosition(), info.GetDamageForce() );
+ }
+}
+
+ConVar dod_stun_min_pitch( "dod_stun_min_pitch", "30", FCVAR_CHEAT );
+ConVar dod_stun_max_pitch( "dod_stun_max_pitch", "50", FCVAR_CHEAT );
+ConVar dod_stun_min_yaw( "dod_stun_min_yaw", "120", FCVAR_CHEAT );
+ConVar dod_stun_max_yaw( "dod_stun_max_yaw", "150", FCVAR_CHEAT );
+ConVar dod_stun_min_roll( "dod_stun_min_roll", "15", FCVAR_CHEAT );
+ConVar dod_stun_max_roll( "dod_stun_max_roll", "30", FCVAR_CHEAT );
+
+void CDODPlayer::OnDamageByStun( const CTakeDamageInfo &info )
+{
+ DevMsg( 2, "took %.1f stun damage\n", info.GetDamage() );
+
+ float flPercent = ( info.GetDamage() / 100.0f );
+
+ m_flStunDuration = 0.0; // mark it as dirty so it transmits, incase we get the same value twice
+ m_flStunDuration = 4.0f * flPercent;
+ m_flStunMaxAlpha = 255;
+
+ float flPitch = flPercent * RandomFloat( dod_stun_min_pitch.GetFloat(), dod_stun_max_pitch.GetFloat() ) *
+ ( (RandomInt( 0, 1 )) ? 1 : -1 );
+
+ float flYaw = flPercent * RandomFloat( dod_stun_min_yaw.GetFloat(), dod_stun_max_yaw.GetFloat() )
+ * ( (RandomInt( 0, 1 )) ? 1 : -1 );
+
+ float flRoll = flPercent * RandomFloat( dod_stun_min_roll.GetFloat(), dod_stun_max_roll.GetFloat() )
+ * ( (RandomInt( 0, 1 )) ? 1 : -1 );
+
+ DevMsg( 2, "punch: pitch %.1f yaw %.1f roll %.1f\n", flPitch, flYaw, flRoll );
+
+ SetPunchAngle( QAngle( flPitch, flYaw, flRoll ) );
+}
+
+void CDODPlayer::DeafenThink( void )
+{
+ int effect = random->RandomInt( 32, 34 );
+
+ CSingleUserRecipientFilter user( this );
+ enginesound->SetPlayerDSP( user, effect, false );
+}
+
+//=======================================================
+// Remember this amount of damage that we dealt for stats
+//=======================================================
+void CDODPlayer::RecordDamageGiven( CDODPlayer *pVictim, int iDamageGiven )
+{
+ if ( iDamageGiven <= 0 )
+ return;
+
+ FOR_EACH_LL( m_DamageGivenList, i )
+ {
+ if ( pVictim->GetLifeID() == m_DamageGivenList[i]->GetLifeID() )
+ {
+ m_DamageGivenList[i]->AddDamage( iDamageGiven );
+ return;
+ }
+ }
+
+ CDamageRecord *record = new CDamageRecord( pVictim->GetPlayerName(), pVictim->GetLifeID(), iDamageGiven );
+ int tail = m_DamageGivenList.AddToTail();
+ m_DamageGivenList[tail] = record;
+}
+
+//=======================================================
+// Remember this amount of damage that we took for stats
+//=======================================================
+void CDODPlayer::RecordDamageTaken( CDODPlayer *pAttacker, int iDamageTaken )
+{
+ if ( iDamageTaken <= 0 )
+ return;
+
+ FOR_EACH_LL( m_DamageTakenList, i )
+ {
+ if ( pAttacker->GetLifeID() == m_DamageTakenList[i]->GetLifeID() )
+ {
+ m_DamageTakenList[i]->AddDamage( iDamageTaken );
+ return;
+ }
+ }
+
+ CDamageRecord *record = new CDamageRecord( pAttacker->GetPlayerName(), pAttacker->GetLifeID(), iDamageTaken );
+ int tail = m_DamageTakenList.AddToTail();
+ m_DamageTakenList[tail] = record;
+}
+
+void CDODPlayer::RecordWorldDamageTaken( int iDamageTaken )
+{
+ if ( iDamageTaken <= 0 )
+ return;
+
+ FOR_EACH_LL( m_DamageTakenList, i )
+ {
+ if ( m_DamageTakenList[i]->GetLifeID() == 0 )
+ {
+ m_DamageTakenList[i]->AddDamage( iDamageTaken );
+ return;
+ }
+ }
+
+ CDamageRecord *record = new CDamageRecord( "world", 0, iDamageTaken );
+ int tail = m_DamageTakenList.AddToTail();
+ m_DamageTakenList[tail] = record;
+}
+
+//=======================================================
+// Reset our damage given and taken counters
+//=======================================================
+void CDODPlayer::ResetDamageCounters()
+{
+ m_DamageGivenList.PurgeAndDeleteElements();
+ m_DamageTakenList.PurgeAndDeleteElements();
+}
+
+//=======================================================
+// Output the damage that we dealt to other players
+//=======================================================
+void CDODPlayer::OutputDamageTaken( void )
+{
+ bool bPrintHeader = true;
+ CDamageRecord *pRecord;
+ char buf[64];
+ int msg_dest = HUD_PRINTCONSOLE;
+
+ FOR_EACH_LL( m_DamageTakenList, i )
+ {
+ if( bPrintHeader )
+ {
+ ClientPrint( this, msg_dest, "Player: %s1 - Damage Taken\n", GetPlayerName() );
+ ClientPrint( this, msg_dest, "-------------------------\n" );
+ bPrintHeader = false;
+ }
+ pRecord = m_DamageTakenList[i];
+
+ if( pRecord )
+ {
+ Q_snprintf( buf, sizeof(buf), "( life ID %i ) - %d in %d %s",
+ pRecord->GetLifeID(),
+ pRecord->GetDamage(),
+ pRecord->GetNumHits(),
+ ( pRecord->GetNumHits() == 1 ) ? "hit" : "hits" );
+
+ ClientPrint( this, msg_dest, "Damage Taken from \"%s1\" - %s2\n", pRecord->GetPlayerName(), buf );
+ }
+ }
+}
+
+//=======================================================
+// Output the damage that we took from other players
+//=======================================================
+void CDODPlayer::OutputDamageGiven( void )
+{
+ bool bPrintHeader = true;
+ CDamageRecord *pRecord;
+ char buf[64];
+ int msg_dest = HUD_PRINTCONSOLE;
+
+ FOR_EACH_LL( m_DamageGivenList, i )
+ {
+ if( bPrintHeader )
+ {
+ ClientPrint( this, msg_dest, "Player: %s1 - Damage Given\n", GetPlayerName() );
+ ClientPrint( this, msg_dest, "-------------------------\n" );
+ bPrintHeader = false;
+ }
+
+ pRecord = m_DamageGivenList[i];
+
+ if( pRecord )
+ {
+ Q_snprintf( buf, sizeof(buf), "( life ID %i ) - %d in %d %s",
+ pRecord->GetLifeID(),
+ pRecord->GetDamage(),
+ pRecord->GetNumHits(),
+ ( pRecord->GetNumHits() == 1 ) ? "hit" : "hits" );
+
+ ClientPrint( this, msg_dest, "Damage Given to \"%s1\" - %s2\n", pRecord->GetPlayerName(), buf );
+ }
+ }
+}
+
+// Sets the player's speed - returns true if successful
+bool CDODPlayer::SetSpeed( int speed )
+{
+ DevMsg( 2, "Changing max speed to %d\n", speed );
+
+ SetMaxSpeed( (float)speed );
+
+ return true;
+}
+
+void CDODPlayer::CreateViewModel( int index /*=0*/ )
+{
+ Assert( index >= 0 && index < MAX_VIEWMODELS );
+
+ if ( GetViewModel( index ) )
+ return;
+
+ CDODViewModel *vm = ( CDODViewModel * )CreateEntityByName( "dod_viewmodel" );
+ if ( vm )
+ {
+ vm->SetAbsOrigin( GetAbsOrigin() );
+ vm->SetOwner( this );
+ vm->SetIndex( index );
+ DispatchSpawn( vm );
+ vm->FollowEntity( this, false );
+ m_hViewModel.Set( index, vm );
+ }
+}
+
+void CDODPlayer::ResetScores( void )
+{
+ ResetFragCount();
+ ResetDeathCount();
+ SetScore(0);
+
+ Q_memset( iNumKilledByUnanswered, 0, sizeof( iNumKilledByUnanswered ) );
+}
+
+bool CDODPlayer::SetObserverMode(int mode)
+{
+ // DoD forces mp_forcecamera to be team only
+ mp_forcecamera.SetValue( OBS_ALLOW_TEAM );
+
+ if ( mode < OBS_MODE_NONE || mode > OBS_MODE_ROAMING )
+ return false;
+
+ // Skip over OBS_MODE_ROAMING for dead players
+ if( GetTeamNumber() > TEAM_SPECTATOR )
+ {
+ if( mode == OBS_MODE_ROAMING )
+ mode = OBS_MODE_IN_EYE;
+ }
+
+ if ( m_iObserverMode > OBS_MODE_DEATHCAM )
+ {
+ // remember mode if we were really spectating before
+ m_iObserverLastMode = m_iObserverMode;
+ }
+
+ m_iObserverMode = mode;
+
+ switch ( mode )
+ {
+ case OBS_MODE_NONE:
+ case OBS_MODE_FIXED :
+ case OBS_MODE_DEATHCAM :
+ SetFOV( this, 0 ); // Reset FOV
+ SetViewOffset( vec3_origin );
+ SetMoveType( MOVETYPE_NONE );
+ break;
+
+ case OBS_MODE_CHASE :
+ case OBS_MODE_IN_EYE :
+ // udpate FOV and viewmodels
+ SetObserverTarget( m_hObserverTarget );
+ SetMoveType( MOVETYPE_OBSERVER );
+ break;
+
+ case OBS_MODE_ROAMING :
+ SetFOV( this, 0 ); // Reset FOV
+ SetObserverTarget( m_hObserverTarget );
+ SetViewOffset( vec3_origin );
+ SetMoveType( MOVETYPE_OBSERVER );
+ break;
+
+ }
+
+ CheckObserverSettings();
+
+ return true;
+}
+
+extern ConVar sv_alltalk;
+bool CDODPlayer::CanHearChatFrom( CBasePlayer *pPlayer )
+{
+ // can always hear the console
+ if ( !pPlayer )
+ return true;
+
+ // teammates can always hear each other if alltalk is on
+ if ( sv_alltalk.GetBool() == true )
+ return true;
+
+ // can hear dead people in bonus round and at round end
+ if ( DODGameRules()->IsInBonusRound() || DODGameRules()->State_Get() == STATE_GAME_OVER )
+ return true;
+
+ // alive players cannot hear dead players
+ if ( IsAlive() && !pPlayer->IsAlive() && !( pPlayer->GetTeamNumber() == TEAM_SPECTATOR ) )
+ {
+ return false;
+ }
+
+ return true;
+}
+
+void CDODPlayer::ResetBleeding( void )
+{
+ SetBandager( NULL );
+}
+
+void CDODPlayer::Bandage( void )
+{
+ ResetBleeding();
+
+ TakeHealth( mp_bandage_heal_amount.GetFloat(), 0 );
+
+ // change helmet to bandages
+}
+
+void CDODPlayer::SetBandager( CDODPlayer *pPlayer )
+{
+ m_hBandager = pPlayer;
+}
+
+bool CDODPlayer::IsBeingBandaged( void )
+{
+ return ( m_hBandager.Get() != NULL );
+}
+
+void CDODPlayer::CommitSuicide( bool bExplode /*= false*/, bool bForce /*= false*/ )
+{
+ CommitSuicide( vec3_origin, false, bForce );
+}
+
+void CDODPlayer::CommitSuicide( const Vector &vecForce, bool bExplode /*= false*/, bool bForce /*= false*/ )
+{
+ // If they're suiciding in the spawn, its most likely they're changing class
+ // ( or possible suiciding to avoid being killed in the endround )
+ // On linux the suicide option sometimes arrives before the joinclass so just
+ // reject it here
+ if ( ShouldInstantRespawn() )
+ return;
+
+ if( !IsAlive() )
+ return;
+
+ // prevent suiciding too often
+ if ( m_fNextSuicideTime > gpGlobals->curtime )
+ return;
+
+ // don't let them suicide for 5 seconds after suiciding
+ m_fNextSuicideTime = gpGlobals->curtime + 5;
+
+ // have the player kill themselves
+ CTakeDamageInfo info( this, this, 0, DMG_NEVERGIB );
+ Event_Killed( info );
+ Event_Dying( info );
+}
+
+bool CDODPlayer::StartReplayMode( float fDelay, float fDuration, int iEntity )
+{
+ if ( !BaseClass::StartReplayMode( fDelay, fDuration, iEntity ) )
+ return false;
+
+ CSingleUserRecipientFilter filter( this );
+ filter.MakeReliable();
+
+ UserMessageBegin( filter, "KillCam" );
+ WRITE_BYTE( OBS_MODE_IN_EYE );
+
+ if ( m_hObserverTarget.Get() )
+ {
+ WRITE_BYTE( m_hObserverTarget.Get()->entindex() ); // first target
+ WRITE_BYTE( entindex() ); //second target
+ }
+ else
+ {
+ WRITE_BYTE( entindex() ); // first target
+ WRITE_BYTE( 0 ); //second target
+ }
+ MessageEnd();
+
+ ClientPrint( this, HUD_PRINTCENTER, "Kill Cam Replay" );
+
+ return true;
+}
+
+void CDODPlayer::StopReplayMode()
+{
+ BaseClass::StopReplayMode();
+
+ CSingleUserRecipientFilter filter( this );
+ filter.MakeReliable();
+
+ UserMessageBegin( filter, "KillCam" );
+ WRITE_BYTE( OBS_MODE_NONE );
+ WRITE_BYTE( 0 );
+ WRITE_BYTE( 0 );
+ MessageEnd();
+}
+
+void CDODPlayer::PickUpWeapon( CWeaponDODBase *pWeapon )
+{
+ // if we have a primary weapon and we are allowed to drop it, drop it and
+ // pick up the one we +used
+
+ CWeaponDODBase *pCurrentPrimaryWpn = (CWeaponDODBase *)Weapon_GetSlot( WPN_SLOT_PRIMARY );
+
+ // drop primary if we can
+ if( pCurrentPrimaryWpn )
+ {
+ if ( pCurrentPrimaryWpn->CanDrop() == false )
+ {
+ return;
+ }
+
+ DODWeaponDrop( pCurrentPrimaryWpn, true );
+ }
+
+ // pick up the new one
+ if ( BumpWeapon( pWeapon ) )
+ {
+ pWeapon->OnPickedUp( this );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Override setup bones so that is uses the render angles from
+// the DOD animation state to setup the hitboxes.
+//-----------------------------------------------------------------------------
+void CDODPlayer::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask )
+{
+ VPROF_BUDGET( "CBaseAnimating::SetupBones", VPROF_BUDGETGROUP_SERVER_ANIM );
+
+ // Set the mdl cache semaphore.
+ MDLCACHE_CRITICAL_SECTION();
+
+ // Get the studio header.
+ Assert( GetModelPtr() );
+ CStudioHdr *pStudioHdr = GetModelPtr( );
+
+ Vector pos[MAXSTUDIOBONES];
+ Quaternion q[MAXSTUDIOBONES];
+
+ // Adjust hit boxes based on IK driven offset.
+ Vector adjOrigin = GetAbsOrigin() + Vector( 0, 0, m_flEstIkOffset );
+
+ // FIXME: pass this into Studio_BuildMatrices to skip transforms
+ CBoneBitList boneComputed;
+ if ( m_pIk )
+ {
+ m_iIKCounter++;
+ m_pIk->Init( pStudioHdr, GetAbsAngles(), adjOrigin, gpGlobals->curtime, m_iIKCounter, boneMask );
+ GetSkeleton( pStudioHdr, pos, q, boneMask );
+
+ m_pIk->UpdateTargets( pos, q, pBoneToWorld, boneComputed );
+ CalculateIKLocks( gpGlobals->curtime );
+ m_pIk->SolveDependencies( pos, q, pBoneToWorld, boneComputed );
+ }
+ else
+ {
+ GetSkeleton( pStudioHdr, pos, q, boneMask );
+ }
+
+ CBaseAnimating *pParent = dynamic_cast< CBaseAnimating* >( GetMoveParent() );
+ if ( pParent )
+ {
+ // We're doing bone merging, so do special stuff here.
+ CBoneCache *pParentCache = pParent->GetBoneCache();
+ if ( pParentCache )
+ {
+ BuildMatricesWithBoneMerge(
+ pStudioHdr,
+ m_PlayerAnimState->GetRenderAngles(),
+ adjOrigin,
+ pos,
+ q,
+ pBoneToWorld,
+ pParent,
+ pParentCache );
+
+ return;
+ }
+ }
+
+ Studio_BuildMatrices(
+ pStudioHdr,
+ m_PlayerAnimState->GetRenderAngles(),
+ adjOrigin,
+ pos,
+ q,
+ -1,
+ GetModelScale(), // Scaling
+ pBoneToWorld,
+ boneMask );
+}
+
+
+extern ConVar sv_debug_player_use;
+extern float IntervalDistance( float x, float x0, float x1 );
+
+//-----------------------------------------------------------------------------
+// Purpose: Find which ents are higher priority for receiving our +use
+//-----------------------------------------------------------------------------
+int CDODPlayer::GetPriorityForPickUpEnt( CBaseEntity *pEnt )
+{
+ CDODBombTarget *pBombTarget = dynamic_cast< CDODBombTarget *>( pEnt );
+
+ if ( pBombTarget )
+ {
+ // its a bomb target for planting or defusing, most important
+ return 20;
+ }
+
+ CDODBaseGrenade *pGren = dynamic_cast< CDODBaseGrenade *>( pEnt );
+
+ if ( pGren )
+ {
+ // its a grenade, high priority
+ return 10;
+ }
+
+ CWeaponDODBase *pWeapon = dynamic_cast< CWeaponDODBase *>( pEnt );
+
+ if ( pWeapon )
+ {
+ // weapons are medium priority
+ return 5;
+ }
+
+ return 0;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: like regular FindUseEntity, but picks up grenades before other things
+//-----------------------------------------------------------------------------
+CBaseEntity *CDODPlayer::FindUseEntity()
+{
+ Vector forward, up;
+ EyeVectors( &forward, NULL, &up );
+
+ trace_t tr;
+ // Search for objects in a sphere (tests for entities that are not solid, yet still useable)
+ Vector searchCenter = EyePosition();
+
+ // NOTE: Some debris objects are useable too, so hit those as well
+ // A button, etc. can be made out of clip brushes, make sure it's +useable via a traceline, too.
+ int useableContents = MASK_SOLID | CONTENTS_DEBRIS | CONTENTS_PLAYERCLIP;
+
+ UTIL_TraceLine( searchCenter, searchCenter + forward * 1024, useableContents, this, COLLISION_GROUP_NONE, &tr );
+
+ // try the hit entity if there is one, or the ground entity if there isn't.
+ CBaseEntity *pNearest = NULL;
+ CBaseEntity *pObject = tr.m_pEnt;
+
+ // UNDONE: Might be faster to just fold this range into the sphere query
+ int count = 0;
+ const int NUM_TANGENTS = 7;
+ while ( !IsUseableEntity(pObject, 0) && count < NUM_TANGENTS)
+ {
+ // trace a box at successive angles down
+ // 45 deg, 30 deg, 20 deg, 15 deg, 10 deg, -10, -15
+ const float tangents[NUM_TANGENTS] = { 1, 0.57735026919f, 0.3639702342f, 0.267949192431f, 0.1763269807f, -0.1763269807f, -0.267949192431f };
+ Vector down = forward - tangents[count]*up;
+ VectorNormalize(down);
+ UTIL_TraceHull( searchCenter, searchCenter + down * 72, -Vector(16,16,16), Vector(16,16,16), useableContents, this, COLLISION_GROUP_NONE, &tr );
+ pObject = tr.m_pEnt;
+ count++;
+ }
+ float nearestDot = CONE_90_DEGREES;
+ if ( IsUseableEntity(pObject, 0) )
+ {
+ Vector delta = tr.endpos - tr.startpos;
+ float centerZ = CollisionProp()->WorldSpaceCenter().z;
+ delta.z = IntervalDistance( tr.endpos.z, centerZ + CollisionProp()->OBBMins().z, centerZ + CollisionProp()->OBBMaxs().z );
+ float dist = delta.Length();
+ if ( dist < PLAYER_USE_RADIUS )
+ {
+ if ( sv_debug_player_use.GetBool() )
+ {
+ NDebugOverlay::Line( searchCenter, tr.endpos, 0, 255, 0, true, 30 );
+ NDebugOverlay::Cross3D( tr.endpos, 16, 0, 255, 0, true, 30 );
+ }
+
+ pNearest = pObject;
+ nearestDot = 0;
+ }
+ }
+
+ // check ground entity first
+ // if you've got a useable ground entity, then shrink the cone of this search to 45 degrees
+ // otherwise, search out in a 90 degree cone (hemisphere)
+ if ( GetGroundEntity() && IsUseableEntity(GetGroundEntity(), FCAP_USE_ONGROUND) )
+ {
+ pNearest = GetGroundEntity();
+ nearestDot = CONE_45_DEGREES;
+ }
+
+ int iHighestPriority = GetPriorityForPickUpEnt( pObject );
+
+ for ( CEntitySphereQuery sphere( searchCenter, PLAYER_USE_RADIUS ); ( pObject = sphere.GetCurrentEntity() ) != NULL; sphere.NextEntity() )
+ {
+ if ( !pObject )
+ continue;
+
+ if ( !IsUseableEntity( pObject, FCAP_USE_IN_RADIUS ) )
+ continue;
+
+ // see if it's more roughly in front of the player than previous guess
+ Vector point;
+ pObject->CollisionProp()->CalcNearestPoint( searchCenter, &point );
+
+ Vector dir = point - searchCenter;
+ VectorNormalize(dir);
+ float dot = DotProduct( dir, forward );
+
+ // Need to be looking at the object more or less
+ if ( dot < 0.8 )
+ continue;
+
+ //NEW FOR DOD
+ // if this entity is higher priority than previous ent, use this one
+
+ int iPriority = GetPriorityForPickUpEnt( pObject );
+
+ // if higher priority, always use
+ // within the same priority, use the closer one
+ if ( ( iPriority > iHighestPriority ) || ( iPriority == iHighestPriority && dot > nearestDot ) )
+ {
+ // Since this has purely been a radius search to this point, we now
+ // make sure the object isn't behind glass or a grate.
+ trace_t trCheckOccluded;
+ UTIL_TraceLine( searchCenter, point, useableContents, this, COLLISION_GROUP_NONE, &trCheckOccluded );
+
+ if ( trCheckOccluded.fraction == 1.0 || trCheckOccluded.m_pEnt == pObject )
+ {
+ pNearest = pObject;
+ nearestDot = dot;
+ iHighestPriority = iPriority;
+ }
+ }
+ }
+
+ if ( sv_debug_player_use.GetBool() )
+ {
+ if ( !pNearest )
+ {
+ NDebugOverlay::Line( searchCenter, tr.endpos, 255, 0, 0, true, 30 );
+ NDebugOverlay::Cross3D( tr.endpos, 16, 255, 0, 0, true, 30 );
+ }
+ else
+ {
+ NDebugOverlay::Box( pNearest->WorldSpaceCenter(), Vector(-8, -8, -8), Vector(8, 8, 8), 0, 255, 0, true, 30 );
+ }
+ }
+
+ // Special check for bomb targets, whose radius for +use is larger than other entities
+ if ( pNearest == NULL )
+ {
+ CBaseEntity *pEnt = NULL;
+ float flBestDist = FLT_MAX;
+
+ // If we didn't find anything, check to see if the bomb target is close enough to use.
+ // This is done separately since there might be something blocking our LOS to it
+ // but we might want to use it anyway if it's close enough.
+
+ while( ( pEnt = gEntList.FindEntityByClassname( pEnt, "dod_bomb_target" ) ) != NULL )
+ {
+ CDODBombTarget *pTarget = static_cast<CDODBombTarget *>( pEnt );
+
+ if ( !pTarget->CanPlantHere( this ) )
+ continue;
+
+ Vector pos = WorldSpaceCenter();
+
+ float flDist = ( pos - pTarget->GetAbsOrigin() ).Length();
+
+ Vector toBomb = pTarget->GetAbsOrigin() - EyePosition();
+ toBomb.NormalizeInPlace();
+
+ if ( DotProduct( forward, toBomb ) < 0.8 )
+ {
+ continue;
+ }
+
+ // if we are looking directly at a bomb target and it is within our radius, that automatically wins
+ if ( flDist < flBestDist && flDist < DOD_BOMB_PLANT_RADIUS )
+ {
+ flBestDist = flDist;
+ pNearest = pTarget;
+ }
+ }
+ }
+
+ return pNearest;
+}
+
+void CDODPlayer::PrintLifetimeStats( void )
+{
+ unsigned int i;
+
+ Msg( "\nWeapon Stats\n================\n" );
+
+ Msg( " (shots) (hits) (damage) (avg.dist) (kills) (hits taken) (dmg taken) (times killed) (accuracy)\n" );
+ // ( "* thompson 4 3 110 89.8 1 1 0 0 75.0
+ //Msg( "*%9s %2i %2i %3i %5.1f %2i %2i %3i %2i %3.1f\n",
+
+ for ( i=0;i<WEAPON_MAX;i++ )
+ {
+ if ( m_WeaponStats[i].m_iNumShotsTaken > 0 )
+ {
+ Msg( "* %10s (%2i) %6i %6i %8i %9.1f %7i %9i %9i %9i %9.1f\n",
+ WeaponIDToAlias( i ), i,
+ m_WeaponStats[i].m_iNumShotsTaken,
+ m_WeaponStats[i].m_iNumShotsHit,
+ m_WeaponStats[i].m_iTotalDamageGiven,
+ m_WeaponStats[i].m_flAverageHitDistance,
+ m_WeaponStats[i].m_iNumKills,
+ m_WeaponStats[i].m_iNumHitsTaken,
+ m_WeaponStats[i].m_iTotalDamageTaken,
+ m_WeaponStats[i].m_iTimesKilled,
+ 100.0 * ( (float)m_WeaponStats[i].m_iNumShotsHit / (float)m_WeaponStats[i].m_iNumShotsTaken ) );
+ }
+ }
+
+ Msg( "\nPlayer Stats\n================\n" );
+
+ for( i=0;i<m_KilledPlayers.Count();i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( m_KilledPlayers[i].m_iUserID );
+
+ const char *pName = pPlayer ? pPlayer->GetPlayerName() : m_KilledByPlayers[i].m_szPlayerName;
+
+ Msg( "* Killed Player '%s' %i times ( %i damage )\n",
+ pName,
+ m_KilledPlayers[i].m_iKills,
+ m_KilledPlayers[i].m_iTotalDamage );
+ }
+
+ Msg( "\n" );
+
+ for( i=0;i<m_KilledByPlayers.Count();i++ )
+ {
+ CBasePlayer *pPlayer = UTIL_PlayerByUserId( m_KilledByPlayers[i].m_iUserID );
+
+ Assert( pPlayer && "If this fails, the player has disconnected. Fix this!" );
+
+ const char *pName = pPlayer ? pPlayer->GetPlayerName() : m_KilledByPlayers[i].m_szPlayerName;
+
+ Msg( "* Player '%s' killed you %i times ( %i damage )\n",
+ pName,
+ m_KilledByPlayers[i].m_iKills,
+ m_KilledByPlayers[i].m_iTotalDamage );
+ }
+
+ Msg( "\n" );
+
+ Msg( "* Areas Captured: %i\n", m_iNumAreaCaptures );
+ Msg( "* Areas Defended: %i\n", m_iNumAreaDefenses );
+ Msg( "* Num Bonus Round Kills: %i\n", m_iNumBonusRoundKills );
+
+ Msg( "\n" );
+
+ // time spent as each class
+ Msg( "Time spent as each class:\n" );
+
+ // Make sure to tally the latest time
+ int playerclass = m_Shared.DesiredPlayerClass();
+
+ //evil, re-map -2 to 6 so it goes on the end of the array
+ if ( playerclass == PLAYERCLASS_RANDOM )
+ playerclass = 6;
+
+ /*
+ m_flTimePlayedPerClass[playerclass] += ( gpGlobals->curtime - m_flLastClassChangeTime );
+ m_flLastClassChangeTime = gpGlobals->curtime;
+
+ Msg( "* Rifleman: %.0f\n", m_flTimePlayedPerClass[0] );
+ Msg( "* Assault: %.0f\n", m_flTimePlayedPerClass[1] );
+ Msg( "* Support: %.0f\n", m_flTimePlayedPerClass[2] );
+ Msg( "* Sniper: %.0f\n", m_flTimePlayedPerClass[3] );
+ Msg( "* MG: %.0f\n", m_flTimePlayedPerClass[4] );
+ Msg( "* Rocket: %.0f\n", m_flTimePlayedPerClass[5] );
+ Msg( "* Random: %.0f\n", m_flTimePlayedPerClass[6] );
+
+ Msg( "\n" );
+ Msg( "Total time connected %.0f\n", ( gpGlobals->curtime - m_flConnectTime ) );
+ */
+}
+
+void CDODPlayer::Stats_WeaponFired( int weaponID )
+{
+ // increment shots taken for this weapon
+ m_WeaponStats[weaponID].m_iNumShotsTaken++;
+
+ DODGameRules()->Stats_WeaponFired( weaponID );
+
+ StatEvent_WeaponFired( (DODWeaponID)weaponID );
+}
+
+void CDODPlayer::Stats_WeaponHit( CDODPlayer *pVictim, int weaponID, int iDamage, int iDamageGiven, int hitgroup, float flHitDistance )
+{
+ // distance
+ float flTotalHitDistance = m_WeaponStats[weaponID].m_flAverageHitDistance * m_WeaponStats[weaponID].m_iNumShotsHit;
+ flTotalHitDistance += flHitDistance;
+ m_WeaponStats[weaponID].m_flAverageHitDistance = flTotalHitDistance / ( m_WeaponStats[weaponID].m_iNumShotsHit + 1 );
+
+ // damage
+ m_WeaponStats[weaponID].m_iTotalDamageGiven += iDamageGiven;
+
+ // hitgroup
+ m_WeaponStats[weaponID].m_iBodygroupsHit[hitgroup]++;
+
+ // shots hit
+ m_WeaponStats[weaponID].m_iNumShotsHit++;
+
+ int userID = pVictim->GetUserID();
+
+ // add total damage to the player record
+ int lookup = m_KilledPlayers.Find( userID );
+ if ( lookup == m_KilledPlayers.InvalidIndex() )
+ {
+ // make a new one
+ playerstat_t p;
+ Q_strncpy( p.m_szPlayerName, pVictim->GetPlayerName(), MAX_PLAYER_NAME_LENGTH );
+ p.m_iUserID = userID;
+ p.m_iKills = 0;
+ p.m_iTotalDamage = iDamageGiven;
+
+ m_KilledPlayers.Insert( userID, p );
+ }
+ else
+ {
+ m_KilledPlayers[lookup].m_iTotalDamage += iDamageGiven;
+ }
+
+ DODGameRules()->Stats_WeaponHit( weaponID, flHitDistance );
+
+ StatEvent_WeaponHit( (DODWeaponID)weaponID, ( hitgroup == HITGROUP_HEAD ) );
+}
+
+void CDODPlayer::Stats_HitByWeapon( CDODPlayer *pAttacker, int weaponID, int iDamage, int iDamageGiven, int hitgroup )
+{
+ // damage
+ m_WeaponStats[weaponID].m_iTotalDamageTaken += iDamageGiven;
+
+ // hitgroup
+ m_WeaponStats[weaponID].m_iHitInBodygroups[hitgroup]++;
+
+ // shots hit
+ m_WeaponStats[weaponID].m_iNumHitsTaken++;
+
+ int userID = pAttacker->GetUserID();
+
+ // add total damage to the player record
+ int lookup = m_KilledByPlayers.Find( userID );
+ if ( lookup == m_KilledByPlayers.InvalidIndex() )
+ {
+ // make a new one
+ playerstat_t p;
+ Q_strncpy( p.m_szPlayerName, pAttacker->GetPlayerName(), MAX_PLAYER_NAME_LENGTH );
+ p.m_iUserID = userID;
+ p.m_iKills = 0;
+ p.m_iTotalDamage = iDamageGiven;
+
+ m_KilledByPlayers.Insert( userID, p );
+ }
+ else
+ {
+ m_KilledByPlayers[lookup].m_iTotalDamage += iDamageGiven;
+ }
+}
+
+void CDODPlayer::Stats_KilledPlayer( CDODPlayer *pVictim, int weaponID )
+{
+ m_WeaponStats[weaponID].m_iNumKills++;
+
+ int userID = pVictim->GetUserID();
+
+ // add a kill to the player record
+ int lookup = m_KilledPlayers.Find( userID );
+ if ( lookup == m_KilledPlayers.InvalidIndex() )
+ {
+ // make a new one
+ playerstat_t p;
+ Q_strncpy( p.m_szPlayerName, pVictim->GetPlayerName(), MAX_PLAYER_NAME_LENGTH );
+ p.m_iUserID = userID;
+ p.m_iKills = 1;
+ p.m_iTotalDamage = 0;
+
+ m_KilledPlayers.Insert( userID, p );
+ }
+ else
+ {
+ m_KilledPlayers[lookup].m_iKills++;
+ }
+
+ // Gamerules records kills per team
+ DODGameRules()->Stats_PlayerKill( GetTeamNumber(), m_Shared.PlayerClass() );
+
+ m_iPerRoundKills++;
+
+ StatEvent_KilledPlayer( (DODWeaponID)weaponID );
+}
+
+void CDODPlayer::Stats_KilledByPlayer( CDODPlayer *pAttacker, int weaponID )
+{
+ m_WeaponStats[weaponID].m_iTimesKilled++;
+
+ int userID = pAttacker->GetUserID();
+
+ // add a kill to the player record
+ int lookup = m_KilledByPlayers.Find( userID );
+ if ( lookup == m_KilledByPlayers.InvalidIndex() )
+ {
+ // make a new one
+ playerstat_t p;
+ Q_strncpy( p.m_szPlayerName, pAttacker->GetPlayerName(), MAX_PLAYER_NAME_LENGTH );
+ p.m_iUserID = userID;
+ p.m_iKills = 1;
+ p.m_iTotalDamage = 0;
+
+ m_KilledByPlayers.Insert( userID, p );
+ }
+ else
+ {
+ m_KilledByPlayers[lookup].m_iKills++;
+ }
+}
+
+void CDODPlayer::Stats_AreaDefended()
+{
+ // map count
+ m_iNumAreaDefenses++;
+
+ // round count
+ m_iPerRoundDefenses++;
+}
+
+void CDODPlayer::Stats_AreaCaptured()
+{
+ // map count
+ m_iNumAreaCaptures++;
+
+ // round count
+ m_iPerRoundCaptures++;
+}
+
+void CDODPlayer::Stats_BonusRoundKill()
+{
+ m_iNumBonusRoundKills++;
+}
+
+void CDODPlayer::Stats_BombDetonated()
+{
+ m_iPerRoundBombsDetonated++;
+}
+
+void CDODPlayer::NoteWeaponFired()
+{
+ Assert( m_pCurrentCommand );
+ if( m_pCurrentCommand )
+ {
+ m_iLastWeaponFireUsercmd = m_pCurrentCommand->command_number;
+ }
+}
+
+bool CDODPlayer::WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const
+{
+ // No need to lag compensate at all if we're not attacking in this command and
+ // we haven't attacked recently.
+ if ( !( pCmd->buttons & IN_ATTACK ) && (pCmd->command_number - m_iLastWeaponFireUsercmd > 5) )
+ return false;
+
+ return BaseClass::WantsLagCompensationOnEntity( pPlayer, pCmd, pEntityTransmitBits );
+}
+
+void CDODPlayer::TallyLatestTimePlayedPerClass( int iOldTeam, int iOldPlayerClass )
+{
+ // Tally the time we spent in the last class, for stats purposes
+ if ( iOldPlayerClass != PLAYERCLASS_UNDEFINED && ( iOldTeam == TEAM_ALLIES || iOldTeam == TEAM_AXIS ) )
+ {
+ // store random in the last slot
+ if ( iOldPlayerClass == PLAYERCLASS_RANDOM )
+ iOldPlayerClass = 6;
+
+ Assert( iOldPlayerClass >= 0 && iOldPlayerClass <= 6 );
+ Assert( iOldTeam == TEAM_ALLIES || iOldTeam == TEAM_AXIS );
+
+ if ( iOldTeam == TEAM_ALLIES )
+ {
+ m_flTimePlayedPerClass_Allies[iOldPlayerClass] += ( gpGlobals->curtime - m_flLastClassChangeTime );
+ }
+ else if ( iOldTeam == TEAM_AXIS )
+ {
+ m_flTimePlayedPerClass_Axis[iOldPlayerClass] += ( gpGlobals->curtime - m_flLastClassChangeTime );
+ }
+ }
+
+ m_flLastClassChangeTime = gpGlobals->curtime;
+}
+
+void CDODPlayer::ResetProgressBar( void )
+{
+ SetProgressBarTime( 0 );
+}
+
+void CDODPlayer::SetProgressBarTime( int barTime )
+{
+ m_iProgressBarDuration = barTime;
+ m_flProgressBarStartTime = this->m_flSimulationTime;
+}
+
+void CDODPlayer::SetDefusing( CDODBombTarget *pTarget )
+{
+ bool bIsDefusing = ( pTarget != NULL );
+
+ if ( bIsDefusing && !m_bIsDefusing )
+ {
+ // start defuse sound
+ EmitSound( "Weapon_C4.Disarm" );
+ m_Shared.SetDefusing( true );
+ }
+ else if ( !bIsDefusing && m_bIsDefusing )
+ {
+ // stop defuse sound
+ StopSound( "Weapon_C4.Disarm" );
+ m_Shared.SetDefusing( false );
+ }
+
+ m_bIsDefusing = bIsDefusing;
+
+ m_pDefuseTarget = pTarget;
+}
+
+void CDODPlayer::SetPlanting( CDODBombTarget *pTarget )
+{
+ bool bIsPlanting = ( pTarget != NULL );
+
+ if ( bIsPlanting && !m_bIsPlanting )
+ {
+ // start defuse sound
+ EmitSound( "Weapon_C4.Plant" );
+ m_Shared.SetPlanting( true );
+ }
+ else if ( !bIsPlanting && m_bIsPlanting )
+ {
+ // stop defuse sound
+ StopSound( "Weapon_C4.Plant" );
+ m_Shared.SetPlanting( false );
+ }
+
+ m_bIsPlanting = bIsPlanting;
+ m_pPlantTarget = pTarget;
+}
+
+void CDODPlayer::StoreCaptureBlock( int iAreaIndex, int iCapAttempt )
+{
+ m_iLastBlockAreaIndex = iAreaIndex;
+ m_iLastBlockCapAttempt = iCapAttempt;
+}
+
+// The capture attempt number that was taking place the last time we blocked a capture
+int CDODPlayer::GetLastBlockCapAttempt( void )
+{
+ return m_iLastBlockCapAttempt;
+}
+
+// The index of the area where we last blocked a capture
+int CDODPlayer::GetLastBlockAreaIndex( void )
+{
+ return m_iLastBlockCapAttempt;
+}
+
+// NOTE! This is unused, unless we reenable our collision bounds to use USE_GAME_CODE
+// - This extends our trigger bounds out a long ways and breaks many things, so only
+// do this if you know what you're doing.
+void CDODPlayer::ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs )
+{
+ m_Shared.ComputeWorldSpaceSurroundingBox( pVecWorldMins, pVecWorldMaxs );
+}
+
+// Called when we fire a bullet, with the number of headshots we hit
+void CDODPlayer::HandleHeadshotAchievement( int iNumHeadshots )
+{
+ if ( iNumHeadshots <= 0 )
+ {
+ SetPerLifeCounterKV( "headshots", 0 );
+ }
+ else
+ {
+ if ( DODGameRules()->State_Get() != STATE_RND_RUNNING )
+ return;
+
+ int iHeadshots = GetPerLifeCounterKV( "headshots" ) + iNumHeadshots;
+
+ if ( iHeadshots >= ACHIEVEMENT_NUM_CONSECUTIVE_HEADSHOTS )
+ {
+ AwardAchievement( ACHIEVEMENT_DOD_CONSECUTIVE_HEADSHOTS );
+ }
+
+ SetPerLifeCounterKV( "headshots", iHeadshots );
+ }
+}
+
+void CDODPlayer::HandleDeployedMGKillCount( int iNumDeployedKills )
+{
+ if ( iNumDeployedKills <= 0 )
+ {
+ SetPerLifeCounterKV( "deployed_mg_kills", 0 );
+ }
+ else
+ {
+ if ( DODGameRules()->State_Get() != STATE_RND_RUNNING )
+ return;
+
+ int iKillsFromPos = GetPerLifeCounterKV( "deployed_mg_kills" ) + iNumDeployedKills;
+
+ if ( iKillsFromPos >= ACHIEVEMENT_MG_STREAK_IS_DOMINATING )
+ {
+ AwardAchievement( ACHIEVEMENT_DOD_MG_POSITION_STREAK );
+ }
+
+ SetPerLifeCounterKV( "deployed_mg_kills", iKillsFromPos );
+ }
+}
+
+int CDODPlayer::GetDeployedKillStreak( void )
+{
+ return GetPerLifeCounterKV( "deployed_mg_kills" );
+}
+
+void CDODPlayer::HandleEnemyWeaponsAchievement( int iNumEnemyWpnKills )
+{
+ if ( iNumEnemyWpnKills <= 0 )
+ {
+ SetPerLifeCounterKV( "enemy_wpn_kills", 0 );
+ }
+ else
+ {
+ if ( DODGameRules()->State_Get() != STATE_RND_RUNNING )
+ return;
+
+ int iKills = GetPerLifeCounterKV( "enemy_wpn_kills" ) + iNumEnemyWpnKills;
+
+ if ( iKills >= ACHIEVEMENT_NUM_ENEMY_WPN_KILLS )
+ {
+ AwardAchievement( ACHIEVEMENT_DOD_USE_ENEMY_WEAPONS );
+ }
+
+ SetPerLifeCounterKV( "enemy_wpn_kills", iKills );
+ }
+}
+
+void CDODPlayer::ResetComboWeaponKill( void )
+{
+ SetPerLifeCounterKV( "combo_wpn_mask", 0 );
+}
+
+void CDODPlayer::HandleComboWeaponKill( int iWeaponType )
+{
+ if ( DODGameRules()->State_Get() != STATE_RND_RUNNING )
+ return;
+
+ int iMask = GetPerLifeCounterKV( "combo_wpn_mask" );
+
+ iMask |= iWeaponType;
+
+ SetPerLifeCounterKV( "combo_wpn_mask", iMask );
+
+ const int iRequiredMask = ( WPN_TYPE_MG | WPN_TYPE_SNIPER | WPN_TYPE_RIFLE | WPN_TYPE_SUBMG );
+ const int iExplosiveMask = ( WPN_TYPE_GRENADE | WPN_TYPE_RIFLEGRENADE );
+
+ if ( ( iMask & iRequiredMask ) == iRequiredMask ) // must have all these bits set
+ {
+ if ( ( iMask & iExplosiveMask ) > 0 ) // has one of these bits set
+ {
+ AwardAchievement( ACHIEVEMENT_DOD_WEAPON_MASTERY );
+ }
+ }
+}
+
+void CDODPlayer::PlayUseDenySound()
+{
+ EmitSound( "Player.UseDeny" );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Removes all nemesis relationships between this player and others
+//-----------------------------------------------------------------------------
+void CDODPlayer::RemoveNemesisRelationships()
+{
+ for ( int i = 1 ; i <= gpGlobals->maxClients ; i++ )
+ {
+ CDODPlayer *pPlayer = ToDODPlayer( UTIL_PlayerByIndex( i ) );
+ if ( pPlayer && pPlayer != this )
+ {
+ // we are no longer dominating anyone
+ m_Shared.SetPlayerDominated( pPlayer, false );
+ Q_memset( iNumKilledByUnanswered, 0, sizeof( iNumKilledByUnanswered ) );
+
+ // noone is dominating us anymore
+ pPlayer->m_Shared.SetPlayerDominated( this, false );
+ pPlayer->iNumKilledByUnanswered[entindex()] = 0;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called when we get a new achievement, recalc our awards
+//-----------------------------------------------------------------------------
+void CDODPlayer::OnAchievementEarned( int iAchievement )
+{
+ BaseClass::OnAchievementEarned( iAchievement );
+
+ RecalculateAchievementAwardsMask();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Determine what achievement awards this player has
+//-----------------------------------------------------------------------------
+void CDODPlayer::RecalculateAchievementAwardsMask( void )
+{
+#if !defined(NO_STEAM)
+
+ if ( steamgameserverapicontext->SteamGameServerStats() )
+ {
+ CSteamID steamIDForPlayer;
+ if ( GetSteamID( &steamIDForPlayer ) && steamIDForPlayer.BIndividualAccount() )
+ {
+ steamgameserverapicontext->SteamGameServerStats()->RequestUserStats( steamIDForPlayer );
+ }
+ }
+#endif
+}
+
+#if !defined(NO_STEAM)
+//-----------------------------------------------------------------------------
+// Purpose: Called when Steam returns a user's stats
+//-----------------------------------------------------------------------------
+void CDODPlayer::OnGSStatsReceived( GSStatsReceived_t *pResponse )
+{
+ if ( pResponse->m_eResult != k_EResultOK )
+ return;
+
+ if ( pResponse->m_steamIDUser == GetSteamIDAsUInt64() )
+ {
+ for ( int i = 1; i < NUM_ACHIEVEMENT_AWARDS; i++ )
+ {
+ bool bUnlocked;
+ if ( steamgameserverapicontext->SteamGameServerStats()->GetUserAchievement( pResponse->m_steamIDUser, g_pszAchievementAwards[i], &bUnlocked ) )
+ {
+ if ( bUnlocked )
+ {
+ m_iAchievementAwardsMask |= (1<<i);
+ break;
+ }
+ }
+ }
+ }
+}
+#endif
+
+void CDODPlayer::StatEvent_UploadStats( void )
+{
+ int iSecondsAlive = (int)( gpGlobals->curtime - m_flTimeAsClassAccumulator );
+
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_PLAYTIME, iSecondsAlive );
+
+ m_StatProperty.SendStatsToPlayer( this );
+
+ m_flTimeAsClassAccumulator = gpGlobals->curtime;
+}
+
+void CDODPlayer::StatEvent_KilledPlayer( DODWeaponID iKillingWeapon )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_KILLS );
+
+ m_StatProperty.IncrementWeaponStat( iKillingWeapon, DODSTAT_KILLS );
+}
+
+void CDODPlayer::StatEvent_WasKilled( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_DEATHS );
+}
+
+void CDODPlayer::StatEvent_RoundWin( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_ROUNDSWON );
+}
+
+void CDODPlayer::StatEvent_RoundLoss( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_ROUNDSLOST );
+}
+
+void CDODPlayer::StatEvent_PointCaptured( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_CAPTURES );
+}
+
+void CDODPlayer::StatEvent_CaptureBlocked( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_BLOCKS );
+}
+
+void CDODPlayer::StatEvent_BombPlanted( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_BOMBSPLANTED );
+}
+
+void CDODPlayer::StatEvent_BombDefused( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_BOMBSDEFUSED );
+}
+
+void CDODPlayer::StatEvent_ScoredDomination( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_DOMINATIONS );
+}
+
+void CDODPlayer::StatEvent_ScoredRevenge( void )
+{
+ m_StatProperty.IncrementPlayerClassStat( DODSTAT_REVENGES );
+}
+
+void CDODPlayer::StatEvent_WeaponFired( DODWeaponID iWeaponID )
+{
+ m_StatProperty.IncrementWeaponStat( iWeaponID, DODSTAT_SHOTS_FIRED );
+}
+
+void CDODPlayer::StatEvent_WeaponHit( DODWeaponID iWeaponID, bool bWasHeadshot )
+{
+ m_StatProperty.IncrementWeaponStat( iWeaponID, DODSTAT_SHOTS_HIT );
+
+ if ( bWasHeadshot )
+ {
+ m_StatProperty.IncrementWeaponStat( iWeaponID, DODSTAT_HEADSHOTS );
+ }
+}
+
+
+// CDODPlayerStatProperty
+
+void CDODPlayerStatProperty::SetClassAndTeamForThisLife( int iPlayerClass, int iTeam )
+{
+ m_bRecordingStats = ( ( iTeam == TEAM_ALLIES || iTeam == TEAM_AXIS ) && ( iPlayerClass >= 0 && iPlayerClass <= NUM_DOD_PLAYERCLASSES ) );
+
+ m_iCurrentLifePlayerClass = iPlayerClass;
+ m_iCurrentLifePlayerTeam = iTeam;
+}
+
+void CDODPlayerStatProperty::IncrementPlayerClassStat( DODStatType_t statType, int iValue /* = 1 */ )
+{
+ if ( !m_bRecordingStats )
+ return;
+
+ Assert( m_iCurrentLifePlayerClass >= 0 && m_iCurrentLifePlayerClass <= 5 );
+
+ m_PlayerStatsPerLife.m_iStat[statType] += iValue;
+}
+
+void CDODPlayerStatProperty::IncrementWeaponStat( DODWeaponID iWeaponID, DODStatType_t statType, int iValue /* = 1 */ )
+{
+ if ( !m_bRecordingStats )
+ return;
+
+ Assert( iWeaponID >= 0 && iWeaponID <= WEAPON_MAX );
+ m_WeaponStatsPerLife[iWeaponID].m_iStat[statType] += iValue;
+ m_bWeaponStatsDirty[iWeaponID] = true;
+}
+
+void CDODPlayerStatProperty::ResetPerLifeStats( void )
+{
+ Q_memset( &m_PlayerStatsPerLife, 0, sizeof(m_PlayerStatsPerLife) );
+ Q_memset( &m_WeaponStatsPerLife, 0, sizeof(m_WeaponStatsPerLife) );
+ Q_memset( &m_bWeaponStatsDirty, 0, sizeof(m_bWeaponStatsDirty) );
+}
+
+void CDODPlayerStatProperty::SendStatsToPlayer( CDODPlayer *pPlayer )
+{
+ if ( !m_bRecordingStats )
+ return;
+
+ // make a bit field of all the stats we want to send (all with non-zero values)
+ int iStat;
+ int iSendPlayerBits = 0;
+ for ( iStat = DODSTAT_FIRST; iStat < DODSTAT_MAX; iStat++ )
+ {
+ if ( m_PlayerStatsPerLife.m_iStat[iStat] > 0 )
+ {
+ iSendPlayerBits |= ( 1 << ( iStat - DODSTAT_FIRST ) );
+ }
+ }
+
+ CUtlVector<int> vecWeaponsToSend;
+
+ // for every weapon that we have stats for, set a bit in the weapon mask
+ for ( int iWeapon = WEAPON_NONE; iWeapon < WEAPON_MAX; iWeapon++ )
+ {
+ if ( m_bWeaponStatsDirty[iWeapon] )
+ {
+ vecWeaponsToSend.AddToTail( iWeapon );
+ }
+ }
+
+ if ( iSendPlayerBits == 0 && vecWeaponsToSend.Count() == 0 )
+ {
+ ResetPerLifeStats();
+ return;
+ }
+
+ iStat = DODSTAT_FIRST;
+
+ CSingleUserRecipientFilter filter( (CBasePlayer *)pPlayer );
+ filter.MakeReliable();
+
+ UserMessageBegin( filter, "DODPlayerStatsUpdate" );
+
+ WRITE_BYTE( m_iCurrentLifePlayerClass ); // write the class
+ WRITE_BYTE( m_iCurrentLifePlayerTeam );
+
+ WRITE_LONG( iSendPlayerBits ); // write the bit mask of which stats follow in the message
+
+ // write all the non-zero stats according to the bit mask
+ while ( iSendPlayerBits > 0 )
+ {
+ if ( iSendPlayerBits & 1 )
+ {
+ WRITE_LONG( m_PlayerStatsPerLife.m_iStat[iStat] );
+ }
+ iSendPlayerBits >>= 1;
+ iStat++;
+ }
+
+ // now the weapon bits
+ // how many weapons
+ WRITE_BYTE( vecWeaponsToSend.Count() );
+
+ int i;
+
+ // send the weapons
+ for ( i=0;i<vecWeaponsToSend.Count(); i++ )
+ {
+ WRITE_BYTE( vecWeaponsToSend.Element(i) );
+ }
+
+ // for each weapon that we're sending stats for
+ for ( i=0;i<vecWeaponsToSend.Count(); i++ )
+ {
+ int iWeapon = vecWeaponsToSend.Element(i);
+
+ // what stats does iWeapon want to send?
+ int iWeaponStatBits = 0;
+
+ for ( iStat = DODSTAT_FIRST; iStat < DODSTAT_MAX; iStat++ )
+ {
+ if ( m_WeaponStatsPerLife[iWeapon].m_iStat[iStat] > 0 )
+ {
+ iWeaponStatBits |= ( 1 << ( iStat - DODSTAT_FIRST ) );
+ }
+ }
+
+ // send the mask of stats that we're sending for this weapon
+ WRITE_LONG( iWeaponStatBits );
+
+ // send the stats
+ iStat = DODSTAT_FIRST;
+
+ // send that mask
+ while ( iWeaponStatBits > 0 )
+ {
+ if ( iWeaponStatBits & 1 )
+ {
+ WRITE_LONG( m_WeaponStatsPerLife[iWeapon].m_iStat[iStat] );
+ }
+ iWeaponStatBits >>= 1;
+ iStat++;
+ }
+ }
+
+ MessageEnd();
+
+ ResetPerLifeStats();
+}
diff --git a/game/server/dod/dod_player.h b/game/server/dod/dod_player.h
new file mode 100644
index 0000000..9f687a7
--- /dev/null
+++ b/game/server/dod/dod_player.h
@@ -0,0 +1,661 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Player for HL1.
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DOD_PLAYER_H
+#define DOD_PLAYER_H
+#pragma once
+
+
+#include "basemultiplayerplayer.h"
+#include "server_class.h"
+#include "dod_playeranimstate.h"
+#include "dod_shareddefs.h"
+#include "dod_player_shared.h"
+#include "unisignals.h"
+#include "dod_statmgr.h"
+#include "utlmap.h"
+#include "steam/steam_gameserver.h"
+#include "hintsystem.h"
+
+// Function table for each player state.
+class CDODPlayerStateInfo
+{
+public:
+ DODPlayerState m_iPlayerState;
+ const char *m_pStateName;
+
+ void (CDODPlayer::*pfnEnterState)(); // Init and deinit the state.
+ void (CDODPlayer::*pfnLeaveState)();
+ void (CDODPlayer::*pfnPreThink)(); // Do a PreThink() in this state.
+};
+
+class CDODPlayer;
+
+//=======================================
+//Record of either damage taken or given.
+//Contains the player name that we hurt or that hurt us,
+//and the total damage
+//=======================================
+class CDamageRecord
+{
+public:
+ CDamageRecord( const char *pszName, int iLifeID, int iDamage )
+ {
+ Q_strncpy( m_szPlayerName, pszName, MAX_PLAYER_NAME_LENGTH );
+ m_iDamage = iDamage;
+ m_iNumHits = 1;
+ m_iLifeID = iLifeID;
+ }
+
+ void AddDamage( int iDamage )
+ {
+ m_iDamage += iDamage;
+ m_iNumHits++;
+ }
+
+ char *GetPlayerName( void ) { return m_szPlayerName; }
+ int GetDamage( void ) { return m_iDamage; }
+ int GetNumHits( void ) { return m_iNumHits; }
+ int GetLifeID( void ) { return m_iLifeID; }
+
+private:
+ char m_szPlayerName[MAX_PLAYER_NAME_LENGTH];
+ int m_iLifeID; // life ID of the player when this damage was done
+ int m_iDamage; //how much damage was done
+ int m_iNumHits; //how many hits
+};
+
+#define SIGNAL_CAPTUREAREA (1<<0)
+
+class CDODBombTarget;
+
+class CDODPlayerStatProperty
+{
+ DECLARE_CLASS_NOBASE( CDODPlayerStatProperty );
+
+public:
+ CDODPlayerStatProperty()
+ {
+ m_iCurrentLifePlayerClass = -1;
+ m_bRecordingStats = false;
+ ResetPerLifeStats();
+ }
+
+ ~CDODPlayerStatProperty() {}
+
+ void SetClassAndTeamForThisLife( int iPlayerClass, int iTeam );
+
+ void IncrementPlayerClassStat( DODStatType_t statType, int iValue = 1 );
+
+ void IncrementWeaponStat( DODWeaponID iWeaponID, DODStatType_t statType, int iValue = 1 );
+
+ // reset per life stats
+ void ResetPerLifeStats( void );
+
+ // send this life's worth of data to the client
+ void SendStatsToPlayer( CDODPlayer *pPlayer );
+
+private:
+
+ bool m_bRecordingStats; // not recording until we get a valid class. stop recording when we join spectator
+
+ int m_iCurrentLifePlayerClass;
+ int m_iCurrentLifePlayerTeam;
+
+ // single life's worth of player stats
+ dod_stat_accumulator_t m_PlayerStatsPerLife;
+
+ // single life's worth of weapon stats
+ dod_stat_accumulator_t m_WeaponStatsPerLife[WEAPON_MAX];
+ bool m_bWeaponStatsDirty[WEAPON_MAX];
+};
+
+
+
+
+//=============================================================================
+// >> Day of Defeat player
+//=============================================================================
+class CDODPlayer : public CBaseMultiplayerPlayer
+{
+public:
+ DECLARE_CLASS( CDODPlayer, CBaseMultiplayerPlayer );
+ DECLARE_SERVERCLASS();
+ DECLARE_DATADESC();
+
+ CDODPlayer();
+ ~CDODPlayer();
+
+ static CDODPlayer *CreatePlayer( const char *className, edict_t *ed );
+ static CDODPlayer* Instance( int iEnt );
+
+ // This passes the event to the client's and server's CPlayerAnimState.
+ void DoAnimationEvent( PlayerAnimEvent_t event, int nData = 0 );
+ void SetupBones( matrix3x4_t *pBoneToWorld, int boneMask );
+
+ virtual void Precache();
+ void PrecachePlayerModel( const char *szPlayerModel );
+ virtual void Spawn();
+ virtual void InitialSpawn( void );
+
+ virtual void CheatImpulseCommands( int iImpulse );
+ virtual void PlayerRunCommand( CUserCmd *ucmd, IMoveHelper *moveHelper );
+
+ virtual void PreThink();
+ virtual void PostThink();
+
+ virtual int OnTakeDamage( const CTakeDamageInfo &inputInfo );
+ virtual int OnTakeDamage_Alive( const CTakeDamageInfo &info );
+
+ virtual void Event_Killed( const CTakeDamageInfo &info );
+ virtual void TraceAttack( const CTakeDamageInfo &info, const Vector &vecDir, trace_t *ptr, CDmgAccumulator *pAccumulator );
+ void Pain( void );
+ void OnDamagedByExplosion( const CTakeDamageInfo &info );
+ void OnDamageByStun( const CTakeDamageInfo &info );
+ void DeafenThink( void );
+
+ virtual void UpdateGeigerCounter( void ) {}
+ virtual void CheckTrainUpdate( void ) {}
+
+ virtual void CreateViewModel( int viewmodelindex = 0 );
+
+ virtual bool SetObserverMode(int mode); // sets new observer mode, returns true if successful
+ virtual bool ModeWantsSpectatorGUI( int iMode ) { return ( iMode != OBS_MODE_DEATHCAM && iMode != OBS_MODE_FREEZECAM ); }
+
+
+ // from CBasePlayer
+ void SetupVisibility( CBaseEntity *pViewEntity, unsigned char *pvs, int pvssize );
+
+ CBaseEntity* EntSelectSpawnPoint();
+
+ void ChangeTeam( int iTeamNum );
+
+ bool CanMove( void ) const;
+
+ virtual void SharedSpawn();
+
+ void CheckProneMoveSound( int groundspeed, bool onground );
+ virtual void UpdateStepSound( surfacedata_t *psurface, const Vector &vecOrigin, const Vector &vecVelocity );
+ virtual void PlayStepSound( Vector &vecOrigin, surfacedata_t *psurface, float fvol, bool force );
+
+ virtual const Vector GetPlayerMins( void ) const; // uses local player
+ virtual const Vector GetPlayerMaxs( void ) const; // uses local player
+
+ void DODRespawn( void );
+
+ virtual void SetAnimation( PLAYER_ANIM playerAnim );
+
+ CBaseEntity * GiveNamedItem( const char *pszName, int iSubType = 0 );
+
+ bool Weapon_CanSwitchTo( CBaseCombatWeapon *pWeapon );
+
+ void SetScore( int score );
+ void AddScore( int num );
+ int GetScore( void ) { return m_iScore; }
+ int m_iScore;
+
+ // Simulates a single frame of movement for a player
+ void RunPlayerMove( const QAngle& viewangles, float forwardmove, float sidemove, float upmove, unsigned short buttons, byte impulse, float frametime );
+
+
+ //Damage record functions
+ void RecordDamageTaken( CDODPlayer *pAttacker, int iDamageTaken );
+ void RecordWorldDamageTaken( int iDamageTaken );
+ void RecordDamageGiven( CDODPlayer *pVictim, int iDamageGiven );
+ void ResetDamageCounters(); //Reset all lists
+
+ void OutputDamageTaken( void );
+ void OutputDamageGiven( void );
+
+ // Voice Commands
+ //============
+ void HandleCommand_Voice( const char *pcmd ); // player submitted a raw voice_ command
+ void HandleCommand_HandSignal( const char *pcmd ); // player wants to show a hand signal
+ void VoiceCommand( int iVoiceCommand ); // internal voice command function
+ void HandSignal( int iSignal ); // same for hand signals
+
+ float m_flNextVoice;
+ float m_flNextHandSignal;
+
+ void PopHelmet( Vector vecDir, Vector vecForceOrigin );
+
+ bool DropActiveWeapon( void );
+ bool DropPrimaryWeapon( void );
+ bool DODWeaponDrop( CBaseCombatWeapon *pWeapon, bool bThrowForward );
+ bool BumpWeapon( CBaseCombatWeapon *pBaseWeapon );
+
+ CWeaponDODBase* GetActiveDODWeapon() const;
+
+ virtual void AttemptToExitFreezeCam( void );
+
+ //Generic Ammo
+ //============
+ void DropGenericAmmo( void );
+ void ReturnGenericAmmo( void );
+ bool GiveGenericAmmo( void );
+ bool m_bHasGenericAmmo;
+
+ void ResetBleeding( void );
+ void Bandage( void ); // stops the bleeding
+ void SetBandager( CDODPlayer *pPlayer );
+ bool IsBeingBandaged( void );
+ EHANDLE m_hBandager;
+
+ //Area Signals
+ //============
+ //to determine if the player is in a sandbag trigger
+ CUnifiedSignals m_signals; // Player signals (buy zone, bomb zone, etc.)
+
+ int m_iCapAreaIconIndex; //which area's icon to show - we are not necessarily capping it.
+ int m_iObjectAreaIndex; //if the player is in an object cap area, which one?
+
+ void SetCapAreaIndex( int index );
+ int GetCapAreaIndex( void );
+ void ClearCapAreaIndex() { SetCapAreaIndex(-1); }
+
+ void SetCPIndex( int index );
+
+ float m_fHandleSignalsTime; //time to next check the area signals
+ void HandleSignals( void ); //check if signals need to do anything, like turn icons on or off
+
+ bool ShouldAutoReload( void ) { return m_bAutoReload; }
+ void SetAutoReload( bool bAutoReload ) { m_bAutoReload = bAutoReload; }
+
+ bool ShouldAutoRezoom( void ) { return m_bAutoRezoom; }
+ void SetAutoRezoom( bool bAutoRezoom ) { m_bAutoRezoom = bAutoRezoom; }
+
+ // Hints
+ virtual CHintSystem *Hints( void ) { return &m_Hints; }
+
+ // Reset all scores
+ void ResetScores( void );
+
+ int GetHealthAsString( char *pDest, int iDestSize );
+ int GetLastPlayerIDAsString( char *pDest, int iDestSize );
+ int GetClosestPlayerHealthAsString( char *pDest, int iDestSize );
+ int GetPlayerClassAsString( char *pDest, int iDestSize );
+ int GetNearestLocationAsString( char *pDest, int iDestSize );
+ int GetTimeleftAsString( char *pDest, int iDestSize );
+ int GetStringForEscapeSequence( char c, char *pDest, int iDestSize );
+ virtual void CheckChatText( char *p, int bufsize );
+
+ void PushawayThink();
+
+ void DestroyRagdoll( void );
+
+ virtual bool CanHearChatFrom( CBasePlayer *pPlayer );
+
+ virtual void CommitSuicide( bool bExplode = false, bool bForce = false );
+ virtual void CommitSuicide( const Vector &vecForce, bool bExplode = false, bool bForce = false );
+
+ virtual bool StartReplayMode( float fDelay, float fDuration, int iEntity );
+ virtual void StopReplayMode();
+
+ void PickUpWeapon( CWeaponDODBase *pWeapon );
+
+ int GetPriorityForPickUpEnt( CBaseEntity *pEnt );
+ virtual CBaseEntity *FindUseEntity();
+
+ virtual void ComputeWorldSpaceSurroundingBox( Vector *pVecWorldMins, Vector *pVecWorldMaxs );
+
+ bool ShouldCollide( int collisionGroup, int contentsMask ) const;
+
+ void SetDeathFlags( int iDeathFlags ) { m_iDeathFlags = iDeathFlags; }
+ int GetDeathFlags() { return m_iDeathFlags; }
+
+ void RemoveNemesisRelationships();
+
+ virtual void OnAchievementEarned( int iAchievement );
+ void RecalculateAchievementAwardsMask();
+
+ bool ShouldInstantRespawn( void );
+
+ void StatEvent_UploadStats( void );
+ void StatEvent_KilledPlayer( DODWeaponID iKillingWeapon );
+ void StatEvent_WasKilled( void );
+ void StatEvent_RoundWin( void );
+ void StatEvent_RoundLoss( void );
+ void StatEvent_PointCaptured( void );
+ void StatEvent_CaptureBlocked( void );
+ void StatEvent_BombPlanted( void );
+ void StatEvent_BombDefused( void );
+ void StatEvent_ScoredDomination( void );
+ void StatEvent_ScoredRevenge( void );
+ void StatEvent_WeaponFired( DODWeaponID iWeaponID );
+ void StatEvent_WeaponHit( DODWeaponID iWeaponID, bool bWasHeadshot );
+
+
+// ------------------------------------------------------------------------------------------------ //
+// Player state management.
+// ------------------------------------------------------------------------------------------------ //
+public:
+
+ void State_Transition( DODPlayerState newState );
+ DODPlayerState State_Get() const; // Get the current state.
+
+ void MoveToNextIntroCamera(); //Cycle view through available intro cameras
+
+ bool ClientCommand( const CCommand &args );
+
+ virtual bool IsReadyToPlay( void );
+
+ void FireBullets( const FireBulletsInfo_t &info );
+
+ bool CanAttack( void );
+
+ void SetBazookaDeployed( bool bDeployed ) { m_bBazookaDeployed = bDeployed; }
+
+ // from cbasecombatcharacter
+ virtual void InitVCollision( const Vector &vecAbsOrigin, const Vector &vecAbsVelocity );
+ virtual void VPhysicsShadowUpdate( IPhysicsObject *pPhysics );
+
+ void DeathSound( const CTakeDamageInfo &info );
+
+ Activity TranslateActivity( Activity baseAct, bool *pRequired = NULL );
+
+ CNetworkVar( float, m_flStunDuration );
+ CNetworkVar( float, m_flStunMaxAlpha );
+
+ // Stats Functions
+
+ void Stats_WeaponFired( int weaponID );
+ void Stats_WeaponHit( CDODPlayer *pVictim, int weaponID, int iDamage, int iDamageGiven, int hitgroup, float flHitDistance );
+ void Stats_HitByWeapon( CDODPlayer *pAttacker, int weaponID, int iDamage, int iDamageGiven, int hitgroup );
+ void Stats_KilledPlayer( CDODPlayer *pVictim, int weaponID );
+ void Stats_KilledByPlayer( CDODPlayer *pAttacker, int weaponID );
+ void Stats_AreaDefended( void );
+ void Stats_AreaCaptured( void );
+ void Stats_BonusRoundKill( void );
+ void Stats_BombDetonated( void );
+
+ void PrintLifetimeStats( void );
+
+ // Called whenever this player fires a shot.
+ void NoteWeaponFired();
+ virtual bool WantsLagCompensationOnEntity( const CBasePlayer *pPlayer, const CUserCmd *pCmd, const CBitVec<MAX_EDICTS> *pEntityTransmitBits ) const;
+
+ void TallyLatestTimePlayedPerClass( int iOldTeam, int iOldClass );
+
+ void ResetProgressBar( void );
+ void SetProgressBarTime( int barTime );
+
+ void StoreCaptureBlock( int iAreaIndex, int iCapAttempt );
+ int GetLastBlockCapAttempt( void );
+ int GetLastBlockAreaIndex( void );
+
+public:
+
+ CNetworkVarEmbedded( CDODPlayerShared, m_Shared );
+
+ int m_flNextTimeCheck; // Next time the player can execute a "timeleft" command
+
+ Vector m_lastStandingPos; // used by the gamemovement code for finding ladders
+
+ void SetSprinting( bool bIsSprinting );
+
+ void SetDefusing( CDODBombTarget *pTarget );
+ bool m_bIsDefusing;
+ CHandle<CDODBombTarget> m_pDefuseTarget;
+
+ void SetPlanting( CDODBombTarget *pTarget );
+ bool m_bIsPlanting;
+ CHandle<CDODBombTarget> m_pPlantTarget;
+
+ // Achievements
+ void HandleHeadshotAchievement( int iNumHeadshots );
+ void HandleDeployedMGKillCount( int iNumDeployedKills );
+ int GetDeployedKillStreak( void );
+ void HandleEnemyWeaponsAchievement( int iNumEnemyWpnKills );
+
+ void ResetComboWeaponKill( void );
+ void HandleComboWeaponKill( int iWeaponType );
+
+ virtual void PlayUseDenySound();
+
+ int iNumKilledByUnanswered[MAX_PLAYERS+1]; // how many unanswered kills this player has been dealt by every other player
+
+#if !defined(NO_STEAM)
+ STEAM_GAMESERVER_CALLBACK( CDODPlayer, OnGSStatsReceived, GSStatsReceived_t, m_CallbackGSStatsReceived );
+#endif
+
+private:
+ bool SelectSpawnSpot( const char *pEntClassName, CBaseEntity* &pSpot );
+
+ CBaseEntity *SelectSpawnSpot( CUtlVector<EHANDLE> *pSpawnPoints, int &iLastSpawnIndex );
+
+ // Copyed from EyeAngles() so we can send it to the client.
+ CNetworkQAngle( m_angEyeAngles );
+
+ IDODPlayerAnimState *m_PlayerAnimState;
+
+ int FlashlightIsOn( void );
+ void FlashlightTurnOn( void );
+ void FlashlightTurnOff( void );
+
+ void ShowClassSelectMenu();
+
+ void CheckRotateIntroCam( void );
+
+ void State_Enter( DODPlayerState newState ); // Initialize the new state.
+ void State_Leave(); // Cleanup the previous state.
+ void State_PreThink(); // Update the current state.
+
+ // Specific state handler functions.
+ void State_Enter_WELCOME();
+ void State_PreThink_WELCOME();
+
+ void State_Enter_PICKINGTEAM();
+ void State_Enter_PICKINGCLASS();
+
+ void State_PreThink_PICKING();
+
+ void State_Enter_ACTIVE();
+ void State_PreThink_ACTIVE();
+
+ void State_Enter_OBSERVER_MODE();
+ void State_PreThink_OBSERVER_MODE();
+
+ void State_Enter_DEATH_ANIM();
+ void State_PreThink_DEATH_ANIM();
+
+ virtual void PlayerDeathThink();
+
+ // When the player joins, it cycles their view between trigger_camera entities.
+ // This is the current camera, and the time that we'll switch to the next one.
+ EHANDLE m_pIntroCamera;
+ float m_fIntroCamTime;
+
+ // Find the state info for the specified state.
+ static CDODPlayerStateInfo* State_LookupInfo( DODPlayerState state );
+
+ // This tells us which state the player is currently in (joining, observer, dying, etc).
+ // Each state has a well-defined set of parameters that go with it (ie: observer is movetype_noclip, non-solid,
+ // invisible, etc).
+ CNetworkVar( DODPlayerState, m_iPlayerState );
+ // Tracks our ragdoll entity.
+ CNetworkHandle( CBaseEntity, m_hRagdoll ); // networked entity handle
+
+ float m_flLastMovement; // Time the player last moved, used for mp_autokick
+
+ void InitProne( void );
+
+ void InitSprinting( void );
+ bool IsSprinting( void );
+ bool CanSprint( void );
+
+ int m_iDeathFlags; // death notice flags related to domination/revenge
+
+ CNetworkVar( int, m_iAchievementAwardsMask );
+
+protected:
+
+ void CreateRagdollEntity();
+
+
+ void PhysObjectSleep();
+ void PhysObjectWake();
+
+
+private:
+
+ friend void Bot_Think( CDODPlayer *pBot ); // needs to use the HandleCommand_ stuff.
+ bool HandleCommand_JoinTeam( int iTeam );
+ bool HandleCommand_JoinClass( int iClass );
+
+ CDODPlayerStateInfo *m_pCurStateInfo; // This can be NULL if no state info is defined for m_iPlayerState.
+
+ bool m_bTeamChanged; //have we changed teams this spawn? Used to enforce one team switch per death rule
+
+ float m_flNextStaminaThink; //time to do next stamina gain
+ CNetworkVar( float, m_flStamina ); //stamina for sprinting, jumping etc
+
+ Vector m_vecTotalBulletForce; //Accumulator for bullet force in a single frame
+
+ bool m_bBazookaDeployed;
+
+ //A list of damage given
+ CUtlLinkedList< CDamageRecord *, int > m_DamageGivenList;
+
+ //A list of damage taken
+ CUtlLinkedList< CDamageRecord *, int > m_DamageTakenList;
+
+ bool m_bSlowedByHit;
+ float m_flUnslowTime;
+ int m_iPlayerSpeed; //last updated player max speed
+
+ bool SetSpeed( int speed );
+
+ bool m_bAutoReload; // does the player want to autoreload their weapon when empty
+
+ bool m_bAutoRezoom; // does the player want to re-zoom after each shot for sniper rifles and bazookas
+
+ float m_flIdleTime; // next time we should do a deep idle
+
+ bool m_bIsSprinting;
+
+ CNetworkVar( bool, m_bSpawnInterpCounter );
+
+ CHintSystem m_Hints;
+
+ float m_flMinNextStepSoundTime;
+
+ int m_LastHitGroup; // the last body region that took damage
+ int m_LastDamageType; // the type of damage we last took
+
+ bool m_bPlayingProneMoveSound;
+
+ int m_iCapAreaIndex;
+
+ // Last usercmd we shot a bullet on.
+ int m_iLastWeaponFireUsercmd;
+
+ CNetworkVar( float, m_flProgressBarStartTime );
+ CNetworkVar( int, m_iProgressBarDuration );
+
+ // blocking abuse protection
+ int m_iLastBlockAreaIndex;
+ int m_iLastBlockCapAttempt;
+
+ // Achievements Data
+ int m_iComboWeaponKillMask;
+
+ bool m_bAbortFreezeCam;
+ bool m_bPlayedFreezeCamSound;
+
+ CDODPlayerStatProperty m_StatProperty;
+
+ EHANDLE m_hLastDroppedWeapon;
+ EHANDLE m_hLastDroppedAmmoBox;
+
+ float m_flTimeAsClassAccumulator;
+
+public:
+
+ // LifeID is a unique int assigned to a player each time they spawn
+ int GetLifeID() { return m_iLifeID; }
+ int m_iLifeID;
+
+ // Stats variables
+ //==================
+
+ // stats related to each weapon ( shots taken and given )
+ weaponstat_t m_WeaponStats[MAX_WEAPONS];
+
+ // a list of players I have killed ( by userid )
+ CUtlMap<int, playerstat_t, int> m_KilledPlayers;
+
+ // a list of players that have killed me ( by userid )
+ CUtlMap<int, playerstat_t, int> m_KilledByPlayers;
+
+ // start time - used to calc total time played
+
+ // time played per class
+ float m_flTimePlayedPerClass_Allies[7]; //0-5, 6 is random
+ float m_flTimePlayedPerClass_Axis[7]; //0-5, 6 is random
+ float m_flLastClassChangeTime;
+
+ // area cap stats
+ int m_iNumAreaDefenses;
+ int m_iNumAreaCaptures;
+ int m_iNumBonusRoundKills;
+
+
+ // Per-Round Stats
+ //================
+
+ virtual void ResetPerRoundStats( void )
+ {
+ m_iPerRoundCaptures = 0;
+ m_iPerRoundDefenses = 0;
+ m_iPerRoundBombsDetonated = 0;
+ m_iPerRoundKills = 0;
+ }
+
+ int GetPerRoundCaps( void )
+ {
+ return m_iPerRoundCaptures;
+ }
+
+ int GetPerRoundDefenses( void )
+ {
+ return m_iPerRoundDefenses;
+ }
+
+ int GetPerRoundBombsDetonated( void )
+ {
+ return m_iPerRoundBombsDetonated;
+ }
+
+ int GetPerRoundKills( void )
+ {
+ return m_iPerRoundKills;
+ }
+
+ int m_iPerRoundCaptures; // how many caps this round
+ int m_iPerRoundDefenses; // how many defenses this round
+ int m_iPerRoundBombsDetonated;
+ int m_iPerRoundKills;
+};
+
+
+inline CDODPlayer *ToDODPlayer( CBaseEntity *pEntity )
+{
+ if ( !pEntity || !pEntity->IsPlayer() )
+ return NULL;
+
+#ifdef _DEBUG
+ Assert( dynamic_cast<CDODPlayer*>( pEntity ) != 0 );
+#endif
+ return static_cast< CDODPlayer* >( pEntity );
+}
+
+inline DODPlayerState CDODPlayer::State_Get() const
+{
+ return m_iPlayerState;
+}
+
+#endif //DOD_PLAYER_H
diff --git a/game/server/dod/dod_player_resource.cpp b/game/server/dod/dod_player_resource.cpp
new file mode 100644
index 0000000..bc637f8
--- /dev/null
+++ b/game/server/dod/dod_player_resource.cpp
@@ -0,0 +1,63 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: DOD's custom CPlayerResource
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "dod_player.h"
+#include "player_resource.h"
+#include "dod_player_resource.h"
+#include <coordsize.h>
+
+// Datatable
+IMPLEMENT_SERVERCLASS_ST(CDODPlayerResource, DT_DODPlayerResource)
+ SendPropArray3( SENDINFO_ARRAY3(m_iObjScore), SendPropInt( SENDINFO_ARRAY(m_iObjScore), 12 ) ),
+ SendPropArray3( SENDINFO_ARRAY3(m_iPlayerClass), SendPropInt( SENDINFO_ARRAY(m_iPlayerClass), 4 ) ),
+END_SEND_TABLE()
+
+BEGIN_DATADESC( CDODPlayerResource )
+ // DEFINE_ARRAY( m_iObjScore, FIELD_INTEGER, MAX_PLAYERS+1 ),
+ // DEFINE_ARRAY( m_iPlayerClass, FIELD_INTEGER, MAX_PLAYERS+1 ),
+END_DATADESC()
+
+LINK_ENTITY_TO_CLASS( dod_player_manager, CDODPlayerResource );
+
+CDODPlayerResource::CDODPlayerResource( void )
+{
+
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CDODPlayerResource::UpdatePlayerData( void )
+{
+ int i;
+
+ for ( i = 1; i <= gpGlobals->maxClients; i++ )
+ {
+ CDODPlayer *pPlayer = (CDODPlayer*)UTIL_PlayerByIndex( i );
+
+ if ( pPlayer && pPlayer->IsConnected() )
+ {
+ m_iObjScore.Set( i, pPlayer->GetScore() );
+ m_iPlayerClass.Set( i, pPlayer->m_Shared.PlayerClass() );
+ }
+ }
+
+ BaseClass::UpdatePlayerData();
+}
+
+void CDODPlayerResource::Spawn( void )
+{
+ int i;
+
+ for ( i=0; i < MAX_PLAYERS+1; i++ )
+ {
+ m_iObjScore.Set( i, 0 );
+ m_iPlayerClass.Set( i, PLAYERCLASS_UNDEFINED );
+ }
+
+ BaseClass::Spawn();
+}
diff --git a/game/server/dod/dod_player_resource.h b/game/server/dod/dod_player_resource.h
new file mode 100644
index 0000000..ba47fc5
--- /dev/null
+++ b/game/server/dod/dod_player_resource.h
@@ -0,0 +1,32 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: DOD's custom CPlayerResource
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DOD_PLAYER_RESOURCE_H
+#define DOD_PLAYER_RESOURCE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+class CDODPlayerResource : public CPlayerResource
+{
+ DECLARE_CLASS( CDODPlayerResource, CPlayerResource );
+
+public:
+ DECLARE_SERVERCLASS();
+ DECLARE_DATADESC();
+
+ CDODPlayerResource();
+
+ virtual void UpdatePlayerData( void );
+ virtual void Spawn( void );
+
+protected:
+ CNetworkArray( int, m_iObjScore, MAX_PLAYERS+1 );
+ CNetworkArray( int, m_iPlayerClass, MAX_PLAYERS+1 );
+};
+
+#endif // DOD_PLAYER_RESOURCE_H
diff --git a/game/server/dod/dod_playermove.cpp b/game/server/dod/dod_playermove.cpp
new file mode 100644
index 0000000..f4db915
--- /dev/null
+++ b/game/server/dod/dod_playermove.cpp
@@ -0,0 +1,80 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+#include "player_command.h"
+#include "igamemovement.h"
+#include "in_buttons.h"
+#include "ipredictionsystem.h"
+#include "dod_player.h"
+
+
+static CMoveData g_MoveData;
+CMoveData *g_pMoveData = &g_MoveData;
+
+IPredictionSystem *IPredictionSystem::g_pPredictionSystems = NULL;
+
+
+//-----------------------------------------------------------------------------
+// Sets up the move data for TF2
+//-----------------------------------------------------------------------------
+class CDODPlayerMove : public CPlayerMove
+{
+DECLARE_CLASS( CDODPlayerMove, CPlayerMove );
+
+public:
+ //virtual void StartCommand( CBasePlayer *player, CUserCmd *cmd );
+ virtual void SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move );
+ virtual void FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move );
+};
+
+// PlayerMove Interface
+static CDODPlayerMove g_PlayerMove;
+
+//-----------------------------------------------------------------------------
+// Singleton accessor
+//-----------------------------------------------------------------------------
+CPlayerMove *PlayerMove()
+{
+ return &g_PlayerMove;
+}
+
+//-----------------------------------------------------------------------------
+// Main setup, finish
+//-----------------------------------------------------------------------------
+
+/*
+void CDODPlayerMove::StartCommand( CBasePlayer *player, CUserCmd *cmd )
+{
+ BaseClass::StartCommand( player, cmd );
+}
+*/
+
+//-----------------------------------------------------------------------------
+// Purpose: This is called pre player movement and copies all the data necessary
+// from the player for movement. (Server-side, the client-side version
+// of this code can be found in prediction.cpp.)
+//-----------------------------------------------------------------------------
+void CDODPlayerMove::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move )
+{
+ player->AvoidPhysicsProps( ucmd );
+
+ BaseClass::SetupMove( player, ucmd, pHelper, move );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: This is called post player movement to copy back all data that
+// movement could have modified and that is necessary for future
+// movement. (Server-side, the client-side version of this code can
+// be found in prediction.cpp.)
+//-----------------------------------------------------------------------------
+void CDODPlayerMove::FinishMove( CBasePlayer *player, CUserCmd *ucmd, CMoveData *move )
+{
+ // Call the default FinishMove code.
+ BaseClass::FinishMove( player, ucmd, move );
+}
diff --git a/game/server/dod/dod_riflegrenade_ger.cpp b/game/server/dod/dod_riflegrenade_ger.cpp
new file mode 100644
index 0000000..8100818
--- /dev/null
+++ b/game/server/dod/dod_riflegrenade_ger.cpp
@@ -0,0 +1,72 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_riflegrenade_ger.h"
+#include "dod_shareddefs.h"
+
+#define GRENADE_MODEL "models/Weapons/w_k98_rg_grenade.mdl"
+
+LINK_ENTITY_TO_CLASS( grenade_riflegren_ger, CDODRifleGrenadeGER );
+PRECACHE_WEAPON_REGISTER( grenade_riflegren_ger );
+
+IMPLEMENT_SERVERCLASS_ST( CDODRifleGrenadeGER, DT_DODRifleGrenadeGER )
+END_SEND_TABLE()
+
+CDODRifleGrenadeGER* CDODRifleGrenadeGER::Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer,
+ DODWeaponID weaponID )
+{
+ CDODRifleGrenadeGER *pGrenade = (CDODRifleGrenadeGER*)CBaseEntity::Create( "grenade_riflegren_ger", position, angles, pOwner );
+
+ Assert( pGrenade );
+
+ if( !pGrenade )
+ return NULL;
+
+ IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->AddVelocity( &velocity, &angVelocity );
+ }
+
+ // Who threw this grenade
+ pGrenade->SetThrower( pOwner );
+
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ // How long until we explode
+ pGrenade->SetDetonateTimerLength( timer );
+
+ // Save the weapon id for stats purposes
+ pGrenade->m_EmitterWeaponID = weaponID;
+
+ return pGrenade;
+}
+
+void CDODRifleGrenadeGER::Spawn()
+{
+ SetModel( GRENADE_MODEL );
+ BaseClass::Spawn();
+}
+
+void CDODRifleGrenadeGER::Precache()
+{
+ PrecacheModel( GRENADE_MODEL );
+ PrecacheScriptSound( "HEGrenade.Bounce" );
+ BaseClass::Precache();
+}
+
+//Pass the classname of the exploding version of this grenade.
+char *CDODRifleGrenadeGER::GetExplodingClassname()
+{
+ return "weapon_riflegren_ger_live";
+} \ No newline at end of file
diff --git a/game/server/dod/dod_riflegrenade_ger.h b/game/server/dod/dod_riflegrenade_ger.h
new file mode 100644
index 0000000..a35f5d3
--- /dev/null
+++ b/game/server/dod/dod_riflegrenade_ger.h
@@ -0,0 +1,49 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_RIFLEGRENADE_GER_H
+#define DOD_RIFLEGRENADE_GER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_basegrenade.h"
+
+class CDODRifleGrenadeGER : public CDODBaseGrenade
+{
+public:
+ DECLARE_CLASS( CDODRifleGrenadeGER, CDODBaseGrenade );
+
+ DECLARE_SERVERCLASS();
+
+ // Overrides
+public:
+ CDODRifleGrenadeGER() {}
+ virtual void Spawn();
+ virtual void Precache();
+
+ // Grenade stuff.
+public:
+
+ static CDODRifleGrenadeGER* Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer,
+ DODWeaponID weaponID );
+
+ virtual char *GetExplodingClassname();
+
+ virtual float GetElasticity() { return 0.05; }
+
+private:
+ float m_flDetonateTime;
+};
+
+
+#endif // DOD_RIFLEGRENADE_GER_H
diff --git a/game/server/dod/dod_riflegrenade_us.cpp b/game/server/dod/dod_riflegrenade_us.cpp
new file mode 100644
index 0000000..a6d7db8
--- /dev/null
+++ b/game/server/dod/dod_riflegrenade_us.cpp
@@ -0,0 +1,72 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_riflegrenade_us.h"
+#include "dod_shareddefs.h"
+
+#define GRENADE_MODEL "models/Weapons/w_garand_rg_grenade.mdl"
+
+LINK_ENTITY_TO_CLASS( grenade_riflegren_us, CDODRifleGrenadeUS );
+PRECACHE_WEAPON_REGISTER( grenade_riflegren_us );
+
+IMPLEMENT_SERVERCLASS_ST( CDODRifleGrenadeUS, DT_DODRifleGrenadeUS )
+END_SEND_TABLE()
+
+CDODRifleGrenadeUS* CDODRifleGrenadeUS::Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer,
+ DODWeaponID weaponID )
+{
+ CDODRifleGrenadeUS *pGrenade = (CDODRifleGrenadeUS*)CBaseEntity::Create( "grenade_riflegren_us", position, angles, pOwner );
+
+ Assert( pGrenade );
+
+ if( !pGrenade )
+ return NULL;
+
+ IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->AddVelocity( &velocity, &angVelocity );
+ }
+
+ // Who threw this grenade
+ pGrenade->SetThrower( pOwner );
+
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ // How long until we explode
+ pGrenade->SetDetonateTimerLength( timer );
+
+ // Save the weapon id for stats purposes
+ pGrenade->m_EmitterWeaponID = weaponID;
+
+ return pGrenade;
+}
+
+void CDODRifleGrenadeUS::Spawn()
+{
+ SetModel( GRENADE_MODEL );
+ BaseClass::Spawn();
+}
+
+void CDODRifleGrenadeUS::Precache()
+{
+ PrecacheModel( GRENADE_MODEL );
+ PrecacheScriptSound( "HEGrenade.Bounce" );
+ BaseClass::Precache();
+}
+
+//Pass the classname of the exploding version of this grenade.
+char *CDODRifleGrenadeUS::GetExplodingClassname()
+{
+ return "weapon_riflegren_us_live";
+} \ No newline at end of file
diff --git a/game/server/dod/dod_riflegrenade_us.h b/game/server/dod/dod_riflegrenade_us.h
new file mode 100644
index 0000000..649995d
--- /dev/null
+++ b/game/server/dod/dod_riflegrenade_us.h
@@ -0,0 +1,49 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_RIFLEGRENADE_US_H
+#define DOD_RIFLEGRENADE_US_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_basegrenade.h"
+
+class CDODRifleGrenadeUS : public CDODBaseGrenade
+{
+public:
+ DECLARE_CLASS( CDODRifleGrenadeUS, CDODBaseGrenade );
+
+ DECLARE_NETWORKCLASS();
+
+ // Overrides.
+public:
+ CDODRifleGrenadeUS() {}
+ virtual void Spawn();
+ virtual void Precache();
+
+ // Grenade stuff.
+public:
+
+ static CDODRifleGrenadeUS* Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer,
+ DODWeaponID weaponID );
+
+ virtual char *GetExplodingClassname();
+
+ virtual float GetElasticity() { return 0.05; }
+
+private:
+ float m_flDetonateTime;
+};
+
+
+#endif // DOD_RIFLEGRENADE_US_H
diff --git a/game/server/dod/dod_smokegrenade.cpp b/game/server/dod/dod_smokegrenade.cpp
new file mode 100644
index 0000000..a43fc90
--- /dev/null
+++ b/game/server/dod/dod_smokegrenade.cpp
@@ -0,0 +1,121 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_smokegrenade.h"
+#include "particle_parse.h"
+
+LINK_ENTITY_TO_CLASS( grenade_smoke, CDODSmokeGrenade );
+PRECACHE_WEAPON_REGISTER( grenade_smoke );
+
+BEGIN_DATADESC( CDODSmokeGrenade )
+ DEFINE_THINKFUNC( Think_Emit ),
+ DEFINE_THINKFUNC( Think_Fade ),
+ DEFINE_THINKFUNC( Think_Remove )
+END_DATADESC()
+
+IMPLEMENT_SERVERCLASS_ST( CDODSmokeGrenade, DT_DODSmokeGrenade )
+ SendPropTime(SENDINFO(m_flSmokeSpawnTime) ),
+END_SEND_TABLE()
+
+void CDODSmokeGrenade::Spawn()
+{
+ BaseClass::Spawn();
+
+ SetThink( &CDODSmokeGrenade::Think_Emit );
+ SetNextThink( gpGlobals->curtime + 0.5 );
+
+ m_bInitialSmoke = false;
+ m_flRemoveTime = -1;
+ m_flSmokeSpawnTime = 0;
+}
+
+void CDODSmokeGrenade::Precache()
+{
+ PrecacheScriptSound( "SmokeGrenade.Bounce" );
+ PrecacheParticleSystem( "smokegrenade" );
+ PrecacheParticleSystem( "smokegrenade_jet" );
+ BaseClass::Precache();
+}
+
+void CDODSmokeGrenade::BounceSound( void )
+{
+ EmitSound( "SmokeGrenade.Bounce" );
+}
+
+void CDODSmokeGrenade::Think_Emit( void )
+{
+ // if we're stationary and have not yet created smoke, do so now
+ Vector vel;
+ AngularImpulse a;
+ VPhysicsGetObject()->GetVelocity( &vel, &a );
+
+ if ( vel.Length() < 15.0 && !m_bInitialSmoke )
+ {
+ VPhysicsGetObject()->EnableMotion( false );
+
+ // Smoke Cloud
+ DispatchParticleEffect( "smokegrenade", GetAbsOrigin(), vec3_angle );
+
+ // Smoke Jet
+ DispatchParticleEffect( "smokegrenade_jet", PATTACH_POINT, this, "jet" );
+
+ EmitSound( "BaseSmokeEffect.Sound" );
+
+ m_flRemoveTime = gpGlobals->curtime + 10;
+
+ m_bInitialSmoke = true;
+
+ m_flSmokeSpawnTime = gpGlobals->curtime;
+ }
+
+ // if its past our bedtime, fade out
+ if ( m_flRemoveTime > 0 && gpGlobals->curtime > m_flRemoveTime )
+ {
+ m_nRenderMode = kRenderTransColor;
+ SetThink( &CDODSmokeGrenade::Think_Fade );
+ }
+
+ SetNextThink( gpGlobals->curtime + 0.1 );
+}
+
+// Fade the projectile out over time before making it disappear
+void CDODSmokeGrenade::Think_Fade()
+{
+ m_bFading = true;
+
+ SetNextThink( gpGlobals->curtime );
+
+ color32 c = GetRenderColor();
+ c.a -= 1;
+ SetRenderColor( c.r, c.b, c.g, c.a );
+
+ if ( !c.a )
+ {
+ SetModelName( NULL_STRING );//invisible
+ SetNextThink( gpGlobals->curtime + 10 );
+ SetThink( &CDODSmokeGrenade::Think_Remove ); // Spit out smoke for 10 seconds.
+ SetSolid( SOLID_NONE );
+ }
+}
+
+void CDODSmokeGrenade::Think_Remove()
+{
+ // stop all effects
+ StopParticleEffects( this );
+
+ SetModelName( NULL_STRING );//invisible
+ SetSolid( SOLID_NONE );
+ SetMoveType( MOVETYPE_NONE );
+
+ UTIL_Remove( this );
+}
+
+
+void CDODSmokeGrenade::Detonate( void )
+{
+ // Intentionally blank - our detonate does nothing
+} \ No newline at end of file
diff --git a/game/server/dod/dod_smokegrenade.h b/game/server/dod/dod_smokegrenade.h
new file mode 100644
index 0000000..3a80c16
--- /dev/null
+++ b/game/server/dod/dod_smokegrenade.h
@@ -0,0 +1,42 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_SMOKEGRENADE_H
+#define DOD_SMOKEGRENADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_basegrenade.h"
+
+class CDODSmokeGrenade : public CDODBaseGrenade
+{
+public:
+ DECLARE_CLASS( CDODSmokeGrenade, CDODBaseGrenade );
+ DECLARE_DATADESC();
+
+ DECLARE_SERVERCLASS();
+
+ virtual void Spawn();
+ virtual void Precache();
+ virtual void BounceSound( void );
+ virtual void Detonate();
+
+ virtual bool CanBePickedUp( void ) { return false; }
+
+ void Think_Emit();
+ void Think_Fade();
+ void Think_Remove();
+
+private:
+ bool m_bFading;
+ bool m_bInitialSmoke;
+ float m_flRemoveTime;
+
+ CNetworkVar( float, m_flSmokeSpawnTime );
+};
+
+#endif // DOD_SMOKEGRENADE_H
diff --git a/game/server/dod/dod_smokegrenade_ger.cpp b/game/server/dod/dod_smokegrenade_ger.cpp
new file mode 100644
index 0000000..0963e35
--- /dev/null
+++ b/game/server/dod/dod_smokegrenade_ger.cpp
@@ -0,0 +1,53 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_smokegrenade_ger.h"
+
+#define GRENADE_MODEL "models/Weapons/w_smoke_ger.mdl"
+
+LINK_ENTITY_TO_CLASS( grenade_smoke_ger, CDODSmokeGrenadeGER );
+PRECACHE_WEAPON_REGISTER( grenade_smoke_ger );
+
+CDODSmokeGrenadeGER* CDODSmokeGrenadeGER::Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner )
+{
+ CDODSmokeGrenadeGER *pGrenade = (CDODSmokeGrenadeGER*)CBaseEntity::Create( "grenade_smoke_ger", position, angles, pOwner );
+
+ Assert( pGrenade );
+
+ if( !pGrenade )
+ return NULL;
+
+ IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->AddVelocity( &velocity, &angVelocity );
+ }
+
+ // Who threw this grenade
+ pGrenade->SetThrower( pOwner );
+
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ return pGrenade;
+}
+
+void CDODSmokeGrenadeGER::Spawn()
+{
+ SetModel( GRENADE_MODEL );
+ BaseClass::Spawn();
+}
+
+void CDODSmokeGrenadeGER::Precache()
+{
+ PrecacheModel( GRENADE_MODEL );
+ BaseClass::Precache();
+} \ No newline at end of file
diff --git a/game/server/dod/dod_smokegrenade_ger.h b/game/server/dod/dod_smokegrenade_ger.h
new file mode 100644
index 0000000..96025e7
--- /dev/null
+++ b/game/server/dod/dod_smokegrenade_ger.h
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_SMOKEGRENADE_GER_H
+#define DOD_SMOKEGRENADE_GER_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_smokegrenade.h"
+
+class CDODSmokeGrenadeGER : public CDODSmokeGrenade
+{
+public:
+ DECLARE_CLASS( CDODSmokeGrenadeGER, CDODSmokeGrenade );
+
+ // Overrides.
+public:
+ CDODSmokeGrenadeGER() {}
+ virtual void Spawn();
+ virtual void Precache();
+
+ // Grenade stuff.
+public:
+ static CDODSmokeGrenadeGER* Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner );
+
+ virtual DODWeaponID GetEmitterWeaponID() { return WEAPON_SMOKE_GER; }
+};
+
+#endif // DOD_SMOKEGRENADE_GER_H
diff --git a/game/server/dod/dod_smokegrenade_us.cpp b/game/server/dod/dod_smokegrenade_us.cpp
new file mode 100644
index 0000000..89ea682
--- /dev/null
+++ b/game/server/dod/dod_smokegrenade_us.cpp
@@ -0,0 +1,53 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_smokegrenade_us.h"
+
+#define GRENADE_MODEL "models/Weapons/w_smoke_us.mdl"
+
+LINK_ENTITY_TO_CLASS( grenade_smoke_us, CDODSmokeGrenadeUS );
+PRECACHE_WEAPON_REGISTER( grenade_smoke_us );
+
+CDODSmokeGrenadeUS* CDODSmokeGrenadeUS::Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner )
+{
+ CDODSmokeGrenadeUS *pGrenade = (CDODSmokeGrenadeUS*)CBaseEntity::Create( "grenade_smoke_us", position, angles, pOwner );
+
+ Assert( pGrenade );
+
+ if( !pGrenade )
+ return NULL;
+
+ IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->AddVelocity( &velocity, &angVelocity );
+ }
+
+ // Who threw this grenade
+ pGrenade->SetThrower( pOwner );
+
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ return pGrenade;
+}
+
+void CDODSmokeGrenadeUS::Spawn()
+{
+ SetModel( GRENADE_MODEL );
+ BaseClass::Spawn();
+}
+
+void CDODSmokeGrenadeUS::Precache()
+{
+ PrecacheModel( GRENADE_MODEL );
+ BaseClass::Precache();
+} \ No newline at end of file
diff --git a/game/server/dod/dod_smokegrenade_us.h b/game/server/dod/dod_smokegrenade_us.h
new file mode 100644
index 0000000..2fc8f3e
--- /dev/null
+++ b/game/server/dod/dod_smokegrenade_us.h
@@ -0,0 +1,38 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_SMOKEGRENADE_US_H
+#define DOD_SMOKEGRENADE_US_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_smokegrenade.h"
+
+class CDODSmokeGrenadeUS : public CDODSmokeGrenade
+{
+public:
+ DECLARE_CLASS( CDODSmokeGrenadeUS, CDODSmokeGrenade );
+
+ // Overrides.
+public:
+ CDODSmokeGrenadeUS() {}
+ virtual void Spawn();
+ virtual void Precache();
+
+ // Grenade stuff.
+public:
+ static CDODSmokeGrenadeUS* Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner );
+
+ virtual DODWeaponID GetEmitterWeaponID() { return WEAPON_SMOKE_US; }
+};
+
+#endif // DOD_SMOKEGRENADE_US_H
diff --git a/game/server/dod/dod_statmgr.cpp b/game/server/dod/dod_statmgr.cpp
new file mode 100644
index 0000000..687e564
--- /dev/null
+++ b/game/server/dod/dod_statmgr.cpp
@@ -0,0 +1,82 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Handle stats game events and route them to the appropriate place
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "KeyValues.h"
+#include "dod_statmgr.h"
+#include "dod_player.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+CDODStatManager::CDODStatManager()
+{
+}
+
+bool CDODStatManager::Init()
+{
+ ListenForGameEvent( "player_death" );
+ ListenForGameEvent( "dod_stats_weapon_attack" );
+ ListenForGameEvent( "dod_stats_player_damage" );
+ ListenForGameEvent( "dod_stats_player_killed" );
+
+ return BaseClass::Init();
+}
+
+void CDODStatManager::FireGameEvent( IGameEvent *event )
+{
+ const char *eventName = event->GetName();
+
+ if ( FStrEq( eventName, "dod_stats_weapon_attack" ) )
+ {
+ CDODPlayer *pAttacker = ToDODPlayer( UTIL_PlayerByUserId( event->GetInt("attacker") ) );
+
+ Assert( pAttacker );
+
+ if ( pAttacker )
+ pAttacker->Stats_WeaponFired( event->GetInt("weapon") );
+ }
+ else if ( FStrEq( eventName, "dod_stats_player_damage" ) )
+ {
+ CDODPlayer *pAttacker = ToDODPlayer( UTIL_PlayerByUserId( event->GetInt("attacker") ) );
+
+ int iVictimID = event->GetInt("victim");
+ CDODPlayer *pVictim = ToDODPlayer( UTIL_PlayerByUserId( iVictimID ) );
+
+ // discard damage to teammates or to yourself
+ if ( ( pAttacker == NULL ) || ( pVictim->GetTeamNumber() == pAttacker->GetTeamNumber() ) )
+ return;
+
+ int weaponID = event->GetInt("weapon");
+ int iDamage = event->GetInt("damage");
+ int iDamageGiven = event->GetInt("damage_given");
+ float flDistance = event->GetFloat("distance");
+ int hitgroup = event->GetInt("hitgroup");
+
+ pAttacker->Stats_WeaponHit( pVictim, weaponID, iDamage, iDamageGiven, hitgroup, flDistance );
+ pVictim->Stats_HitByWeapon( pAttacker, weaponID, iDamage, iDamageGiven, hitgroup );
+ }
+ else if ( FStrEq( eventName, "dod_stats_player_killed" ) )
+ {
+ int iVictimID = event->GetInt("victim");
+ CDODPlayer *pVictim = ToDODPlayer( UTIL_PlayerByUserId( iVictimID ) );
+
+ CDODPlayer *pAttacker = ToDODPlayer( UTIL_PlayerByUserId( event->GetInt("attacker") ) );
+
+ // discard kills to teammates or to yourself
+ if ( ( pAttacker == NULL ) || ( pVictim->GetTeamNumber() == pAttacker->GetTeamNumber() ) )
+ return;
+
+ int weaponID = event->GetInt("weapon");
+
+ pAttacker->Stats_KilledPlayer( pVictim, weaponID );
+ pVictim->Stats_KilledByPlayer( pAttacker, weaponID );
+ }
+}
+
+CDODStatManager g_DODStatMgr;
diff --git a/game/server/dod/dod_statmgr.h b/game/server/dod/dod_statmgr.h
new file mode 100644
index 0000000..6c34cac
--- /dev/null
+++ b/game/server/dod/dod_statmgr.h
@@ -0,0 +1,78 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_STATMGR_H
+#define DOD_STATMGR_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "GameEventListener.h"
+#include <igamesystem.h>
+
+// Structure that holds all information related to how a player related to a
+//
+typedef struct
+{
+ // shots taken
+ int m_iNumShotsTaken;
+
+ // shots hit
+ int m_iNumShotsHit;
+
+ // total damage given
+ int m_iTotalDamageGiven;
+
+ // average distance of hit shots
+ float m_flAverageHitDistance;
+
+ // total kills
+ int m_iNumKills;
+
+ // times we hit each hitgroup
+ int m_iBodygroupsHit[7];
+
+ // times hit by this weapon
+ int m_iNumHitsTaken;
+
+ // total damage given to us
+ int m_iTotalDamageTaken;
+
+ // times killed by this weapon
+ int m_iTimesKilled;
+
+ // times we were hit in each hitgroup
+ int m_iHitInBodygroups[8];
+} weaponstat_t;
+
+// Can be used to represent a victim and how many times we killed him,
+// or as an attacker, how many times we were killed by this person
+typedef struct
+{
+ char m_szPlayerName[MAX_PLAYER_NAME_LENGTH]; // when we add this player, store the player name
+ int m_iUserID; // player that did the killing
+ int m_iKills; // number of kills
+ int m_iTotalDamage; // amount of damage
+} playerstat_t;
+
+class CDODStatManager : public CGameEventListener, public CAutoGameSystem
+{
+private:
+ typedef CBaseGameSystem BaseClass;
+
+public:
+ CDODStatManager();
+
+public: // IGameEventListener Interface
+ virtual void FireGameEvent( IGameEvent * event );
+
+public: // CBaseGameSystem overrides
+ virtual bool Init();
+ //virtual void Shutdown() {}
+};
+
+
+#endif // DOD_STATMGR_H
diff --git a/game/server/dod/dod_stickgrenade.cpp b/game/server/dod/dod_stickgrenade.cpp
new file mode 100644
index 0000000..0b12100
--- /dev/null
+++ b/game/server/dod/dod_stickgrenade.cpp
@@ -0,0 +1,78 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "dod_stickgrenade.h"
+#include "dod_shareddefs.h"
+
+#define GRENADE_MODEL "models/Weapons/w_stick.mdl"
+
+LINK_ENTITY_TO_CLASS( grenade_frag_ger, CDODStickGrenade );
+PRECACHE_WEAPON_REGISTER( grenade_frag_ger );
+
+CDODStickGrenade* CDODStickGrenade::Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer,
+ DODWeaponID weaponID )
+{
+ CDODStickGrenade *pGrenade = (CDODStickGrenade*)CBaseEntity::Create( "grenade_frag_ger", position, angles, pOwner );
+
+ Assert( pGrenade );
+
+ if( !pGrenade )
+ return NULL;
+
+ IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject();
+ if ( pPhysicsObject )
+ {
+ pPhysicsObject->AddVelocity( &velocity, &angVelocity );
+ }
+
+ pGrenade->SetupInitialTransmittedGrenadeVelocity( velocity );
+
+ // Who threw this grenade
+ pGrenade->SetThrower( pOwner );
+
+ pGrenade->ChangeTeam( pOwner->GetTeamNumber() );
+
+ // How long until we explode
+ pGrenade->SetDetonateTimerLength( timer );
+
+ // Save the weapon id for stats purposes
+ pGrenade->m_EmitterWeaponID = weaponID;
+
+ return pGrenade;
+}
+
+void CDODStickGrenade::Spawn()
+{
+ SetModel( GRENADE_MODEL );
+ BaseClass::Spawn();
+}
+
+void CDODStickGrenade::Precache()
+{
+ PrecacheModel( GRENADE_MODEL );
+
+ PrecacheScriptSound( "HEGrenade.Bounce" );
+
+ BaseClass::Precache();
+}
+
+void CDODStickGrenade::BounceSound( void )
+{
+ EmitSound( "HEGrenade.Bounce" );
+}
+
+//Pass the classname of the exploding version of this grenade.
+char *CDODStickGrenade::GetExplodingClassname()
+{
+ return "weapon_frag_ger_live";
+} \ No newline at end of file
diff --git a/game/server/dod/dod_stickgrenade.h b/game/server/dod/dod_stickgrenade.h
new file mode 100644
index 0000000..74c3b58
--- /dev/null
+++ b/game/server/dod/dod_stickgrenade.h
@@ -0,0 +1,50 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef DOD_STICKGRENADE_H
+#define DOD_STICKGRENADE_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#include "dod_basegrenade.h"
+
+class CDODStickGrenade : public CDODBaseGrenade
+{
+public:
+ DECLARE_CLASS( CDODStickGrenade, CDODBaseGrenade );
+
+
+// Overrides.
+public:
+ CDODStickGrenade() {}
+ virtual void Spawn();
+ virtual void Precache();
+ virtual void BounceSound( void );
+
+// Grenade stuff.
+public:
+
+ static CDODStickGrenade* Create(
+ const Vector &position,
+ const QAngle &angles,
+ const Vector &velocity,
+ const AngularImpulse &angVelocity,
+ CBaseCombatCharacter *pOwner,
+ float timer,
+ DODWeaponID weaponID );
+
+ void SetTimer( float timer );
+
+ virtual char *GetExplodingClassname();
+
+private:
+ float m_flDetonateTime;
+ CDODStickGrenade( const CDODStickGrenade & );
+};
+
+
+#endif // DOD_STICKGRENADE_H
diff --git a/game/server/dod/dod_team.cpp b/game/server/dod/dod_team.cpp
new file mode 100644
index 0000000..b274dd1
--- /dev/null
+++ b/game/server/dod/dod_team.cpp
@@ -0,0 +1,168 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Team management class. Contains all the details for a specific team
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "dod_team.h"
+#include "entitylist.h"
+#include "dod_shareddefs.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+
+// Datatable
+IMPLEMENT_SERVERCLASS_ST(CDODTeam, DT_DODTeam)
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( dod_team_manager, CDODTeam );
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a pointer to the specified TF team manager
+//-----------------------------------------------------------------------------
+CDODTeam *GetGlobalDODTeam( int iIndex )
+{
+ return (CDODTeam*)GetGlobalTeam( iIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Needed because this is an entity, but should never be used
+//-----------------------------------------------------------------------------
+void CDODTeam::Init( const char *pName, int iNumber )
+{
+ BaseClass::Init( pName, iNumber );
+
+ // Only detect changes every half-second.
+ NetworkProp()->SetUpdateInterval( 0.75f );
+
+ m_hPlayerClassInfoHandles.Purge();
+}
+
+void CDODTeam::AddPlayerClass( const char *szClassName )
+{
+ PLAYERCLASS_FILE_INFO_HANDLE hPlayerClassInfo;
+
+ if ( ReadPlayerClassDataFromFileForSlot( filesystem, szClassName, &hPlayerClassInfo, GetEncryptionKey() ) )
+ {
+ m_hPlayerClassInfoHandles.AddToTail( hPlayerClassInfo );
+ }
+ else
+ {
+ Assert( !"missing playerclass script file" );
+ Msg( "Missing playerclass script file for class: %s\n", szClassName );
+ }
+}
+
+const CDODPlayerClassInfo &CDODTeam::GetPlayerClassInfo( int iPlayerClass ) const
+{
+ Assert( iPlayerClass >= 0 && iPlayerClass < m_hPlayerClassInfoHandles.Count() );
+
+ const FilePlayerClassInfo_t *pPlayerClassInfo = GetFilePlayerClassInfoFromHandle( m_hPlayerClassInfoHandles[iPlayerClass] );
+ const CDODPlayerClassInfo *pDODInfo;
+
+ #ifdef _DEBUG
+ pDODInfo = dynamic_cast< const CDODPlayerClassInfo* >( pPlayerClassInfo );
+ Assert( pDODInfo );
+ #else
+ pDODInfo = static_cast< const CDODPlayerClassInfo* >( pPlayerClassInfo );
+ #endif
+
+ return *pDODInfo;
+}
+
+bool CDODTeam::IsClassOnTeam( const char *pszClassName, int &iClassNum ) const
+{
+ iClassNum = PLAYERCLASS_UNDEFINED;
+
+ // Random is always on every team
+ if( FStrEq( pszClassName, "cls_random" ) )
+ {
+ iClassNum = PLAYERCLASS_RANDOM;
+ return true;
+ }
+
+ for( int i=0;i<m_hPlayerClassInfoHandles.Count(); i++ )
+ {
+ FilePlayerClassInfo_t *pPlayerClassInfo = GetFilePlayerClassInfoFromHandle( m_hPlayerClassInfoHandles[i] );
+
+ if( stricmp( pszClassName, pPlayerClassInfo->m_szSelectCmd ) == 0 )
+ {
+ iClassNum = i;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CDODTeam::ResetScores( void )
+{
+ SetRoundsWon(0);
+ SetScore(0);
+}
+
+//==================================
+// TEAMS!
+//==================================
+
+// US REGULARS
+//==================
+
+class CDODTeam_Allies : public CDODTeam
+{
+DECLARE_CLASS( CDODTeam_Allies, CDODTeam );
+
+DECLARE_SERVERCLASS();
+
+ virtual void Init( const char *pName, int iNumber )
+ {
+ BaseClass::Init( pName, iNumber );
+
+ int i = 0;
+ while( pszTeamAlliesClasses[i] != NULL )
+ {
+ AddPlayerClass( pszTeamAlliesClasses[i] );
+ i++;
+ }
+ }
+
+ virtual const char *GetTeamName( void ) { return "#Teamname_Allies"; }
+};
+
+IMPLEMENT_SERVERCLASS_ST(CDODTeam_Allies, DT_DODTeam_Allies)
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( dod_team_allies, CDODTeam_Allies );
+
+
+// AXIS REGULAR
+//==================
+
+class CDODTeam_Axis : public CDODTeam
+{
+DECLARE_CLASS( CDODTeam_Axis, CDODTeam );
+
+DECLARE_SERVERCLASS();
+
+ virtual void Init( const char *pName, int iNumber )
+ {
+ BaseClass::Init( pName, iNumber );
+
+ int i = 0;
+ while( pszTeamAxisClasses[i] != NULL )
+ {
+ AddPlayerClass( pszTeamAxisClasses[i] );
+ i++;
+ }
+ }
+
+ virtual const char *GetTeamName( void ) { return "#Teamname_Allies"; }
+};
+
+IMPLEMENT_SERVERCLASS_ST(CDODTeam_Axis, DT_DODTeam_Axis)
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( dod_team_axis, CDODTeam_Axis ); \ No newline at end of file
diff --git a/game/server/dod/dod_team.h b/game/server/dod/dod_team.h
new file mode 100644
index 0000000..31c510f
--- /dev/null
+++ b/game/server/dod/dod_team.h
@@ -0,0 +1,60 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Team management class. Contains all the details for a specific team
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#ifndef DOD_TEAM_H
+#define DOD_TEAM_H
+
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "utlvector.h"
+#include "team.h"
+#include "playerclass_info_parse.h"
+#include "dod_playerclass_info_parse.h"
+#include "dod_shareddefs.h"
+#include "dod_player.h"
+
+typedef CUtlLinkedList< PLAYERCLASS_FILE_INFO_HANDLE, int > PlayerClassInfoList;
+
+//-----------------------------------------------------------------------------
+// Purpose: Team Manager
+//-----------------------------------------------------------------------------
+class CDODTeam : public CTeam
+{
+ DECLARE_CLASS( CDODTeam, CTeam );
+ DECLARE_SERVERCLASS();
+
+public:
+
+ // Initialization
+ virtual void Init( const char *pName, int iNumber );
+
+ CDODPlayerClassInfo const &GetPlayerClassInfo( int iPlayerClass ) const;
+ const unsigned char *GetEncryptionKey( void ) { return g_pGameRules->GetEncryptionKey(); }
+
+ virtual void AddPlayerClass( const char *pszClassName );
+
+ bool IsClassOnTeam( const char *pszClassName, int &iClassNum ) const;
+ int GetNumPlayerClasses( void ) { return m_hPlayerClassInfoHandles.Count(); }
+
+ void ResetScores( void );
+
+ virtual const char *GetTeamName( void ) { return "#Teamname_Spectators"; }
+
+ virtual CDODPlayer *GetDODPlayer( int iIndex ) { return ToDODPlayer(GetPlayer(iIndex)); }
+
+private:
+ CUtlVector < PLAYERCLASS_FILE_INFO_HANDLE > m_hPlayerClassInfoHandles;
+};
+
+
+extern CDODTeam *GetGlobalDODTeam( int iIndex );
+
+
+#endif // TF_TEAM_H
diff --git a/game/server/dod/grenadetrail.cpp b/game/server/dod/grenadetrail.cpp
new file mode 100644
index 0000000..a0dcc2d
--- /dev/null
+++ b/game/server/dod/grenadetrail.cpp
@@ -0,0 +1,109 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#include "cbase.h"
+#include "grenadetrail.h"
+#include "dt_send.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define GRENADETRAIL_ENTITYNAME "env_grenadetrail"
+
+//-----------------------------------------------------------------------------
+//Data table
+//-----------------------------------------------------------------------------
+IMPLEMENT_SERVERCLASS_ST(CGrenadeTrail, DT_GrenadeTrail)
+ SendPropFloat(SENDINFO(m_SpawnRate), 8, 0, 1, 1024),
+ SendPropFloat(SENDINFO(m_ParticleLifetime), 16, SPROP_ROUNDUP, 0.1, 100),
+ SendPropFloat(SENDINFO(m_StopEmitTime), 0, SPROP_NOSCALE),
+ SendPropBool(SENDINFO(m_bEmit) ),
+ SendPropInt(SENDINFO(m_nAttachment), 32 ),
+END_SEND_TABLE()
+
+
+BEGIN_DATADESC( CGrenadeTrail )
+
+ DEFINE_KEYFIELD( m_SpawnRate, FIELD_FLOAT, "spawnrate" ),
+ DEFINE_KEYFIELD( m_ParticleLifetime, FIELD_FLOAT, "lifetime" ),
+ DEFINE_FIELD( m_StopEmitTime, FIELD_TIME ),
+ DEFINE_FIELD( m_bEmit, FIELD_BOOLEAN ),
+ DEFINE_FIELD( m_nAttachment, FIELD_INTEGER ),
+
+END_DATADESC()
+
+
+LINK_ENTITY_TO_CLASS(env_grenadetrail, CGrenadeTrail);
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output :
+//-----------------------------------------------------------------------------
+CGrenadeTrail::CGrenadeTrail()
+{
+ m_SpawnRate = 10;
+ m_ParticleLifetime = 5;
+ m_StopEmitTime = 0; // Don't stop emitting particles
+ m_bEmit = true;
+ m_nAttachment = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose :
+// Input :
+// Output :
+//-----------------------------------------------------------------------------
+void CGrenadeTrail::SetEmit(bool bVal)
+{
+ m_bEmit = bVal;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Output : CGrenadeTrail*
+//-----------------------------------------------------------------------------
+CGrenadeTrail* CGrenadeTrail::CreateGrenadeTrail()
+{
+ CBaseEntity *pEnt = CreateEntityByName(GRENADETRAIL_ENTITYNAME);
+ if(pEnt)
+ {
+ CGrenadeTrail *pTrail = dynamic_cast<CGrenadeTrail*>(pEnt);
+ if(pTrail)
+ {
+ pTrail->Activate();
+ return pTrail;
+ }
+ else
+ {
+ UTIL_Remove(pEnt);
+ }
+ }
+
+ return NULL;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Attach the smoke trail to an entity or point
+// Input : index - entity that has the attachment
+// attachment - point to attach to
+//-----------------------------------------------------------------------------
+void CGrenadeTrail::FollowEntity( CBaseEntity *pEntity, const char *pAttachmentName )
+{
+ // For attachments
+ if ( pAttachmentName && pEntity && pEntity->GetBaseAnimating() )
+ {
+ m_nAttachment = pEntity->GetBaseAnimating()->LookupAttachment( pAttachmentName );
+ }
+ else
+ {
+ m_nAttachment = 0;
+ }
+
+ BaseClass::FollowEntity( pEntity );
+}
diff --git a/game/server/dod/grenadetrail.h b/game/server/dod/grenadetrail.h
new file mode 100644
index 0000000..ddaf6e2
--- /dev/null
+++ b/game/server/dod/grenadetrail.h
@@ -0,0 +1,31 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+#ifndef GRENADE_TRAIL_H
+#define GRENADE_TRAIL_H
+
+#include "baseparticleentity.h"
+
+//==================================================
+// SmokeTrail
+//==================================================
+
+class CGrenadeTrail : public CBaseParticleEntity
+{
+ DECLARE_DATADESC();
+public:
+ DECLARE_CLASS( CGrenadeTrail, CBaseParticleEntity );
+ DECLARE_SERVERCLASS();
+
+ CGrenadeTrail();
+ void SetEmit(bool bVal);
+ void FollowEntity( CBaseEntity *pEntity, const char *pAttachmentName = NULL);
+ static CGrenadeTrail* CreateGrenadeTrail();
+
+public:
+ CNetworkVar( float, m_SpawnRate ); // How many particles per second.
+ CNetworkVar( float, m_ParticleLifetime ); // How long do the particles live?
+ CNetworkVar( bool, m_bEmit );
+ CNetworkVar( float, m_StopEmitTime ); // When do I stop emitting particles?
+ CNetworkVar( int, m_nAttachment );
+};
+
+#endif //GRENADE_TRAIL_H
diff --git a/game/server/dod/holiday_gift.cpp b/game/server/dod/holiday_gift.cpp
new file mode 100644
index 0000000..d25514f
--- /dev/null
+++ b/game/server/dod/holiday_gift.cpp
@@ -0,0 +1,123 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "holiday_gift.h"
+#include "dod_shareddefs.h"
+
+#define CHRISTMAS_MODEL "models/items/dod_gift.mdl"
+
+LINK_ENTITY_TO_CLASS( holiday_gift, CHolidayGift );
+PRECACHE_WEAPON_REGISTER( holiday_gift );
+
+//-----------------------------------------------------------------------------
+CHolidayGift* CHolidayGift::Create( const Vector &position, const QAngle &angles, const QAngle &eyeAngles, const Vector &velocity, CBaseCombatCharacter *pOwner )
+{
+ CHolidayGift *pGift = (CHolidayGift*)CBaseEntity::Create( "holiday_gift", position, angles, pOwner );
+
+ if ( pGift )
+ {
+ pGift->AddSpawnFlags( SF_NORESPAWN );
+
+ Vector vecRight, vecUp;
+ AngleVectors( eyeAngles, NULL, &vecRight, &vecUp );
+
+ // Calculate the initial impulse on the gift.
+ Vector vecImpulse( 0.0f, 0.0f, 0.0f );
+ vecImpulse += vecUp * random->RandomFloat( 0, 0.25 );
+ vecImpulse += vecRight * random->RandomFloat( -0.25, 0.25 );
+ VectorNormalize( vecImpulse );
+ vecImpulse *= random->RandomFloat( 100.0, 150.0 );
+ vecImpulse += velocity;
+
+ // Cap the impulse.
+ float flSpeed = vecImpulse.Length();
+ if ( flSpeed > 300.0 )
+ {
+ VectorScale( vecImpulse, 300.0 / flSpeed, vecImpulse );
+ }
+
+ pGift->SetMoveType( MOVETYPE_FLYGRAVITY );
+ pGift->SetAbsVelocity( vecImpulse * 2.f + Vector(0,0,200) );
+ pGift->SetAbsAngles( QAngle(0,0,0) );
+ pGift->UseClientSideAnimation();
+ pGift->ResetSequence( pGift->LookupSequence("idle") );
+
+ pGift->EmitSound( "Christmas.GiftDrop" );
+
+ pGift->ActivateWhenAtRest();
+ }
+
+ return pGift;
+}
+
+//-----------------------------------------------------------------------------
+void CHolidayGift::Precache()
+{
+ BaseClass::Precache();
+
+ PrecacheModel( CHRISTMAS_MODEL );
+ PrecacheScriptSound( "Christmas.GiftDrop" );
+ PrecacheScriptSound( "Christmas.GiftPickup" );
+}
+
+//-----------------------------------------------------------------------------
+void CHolidayGift::Spawn( void )
+{
+ BaseClass::Spawn();
+
+ SetModel( CHRISTMAS_MODEL );
+
+ // Die in 30 seconds
+ SetContextThink( &CBaseEntity::SUB_Remove, gpGlobals->curtime + 30, "DIE_THINK" );
+ SetContextThink( &CHolidayGift::DropSoundThink, gpGlobals->curtime + 0.2f, "SOUND_THINK" );
+}
+
+//-----------------------------------------------------------------------------
+void CHolidayGift::DropSoundThink( void )
+{
+ EmitSound( "Christmas.GiftDrop" );
+}
+
+//-----------------------------------------------------------------------------
+bool CHolidayGift::MyTouch( CBasePlayer *pPlayer )
+{
+ if( !pPlayer )
+ return false;
+
+ if( !pPlayer->IsAlive() )
+ return false;
+
+ if ( pPlayer->IsBot() )
+ return false;
+
+ if ( ( pPlayer->GetTeamNumber() != TEAM_ALLIES ) && ( pPlayer->GetTeamNumber() != TEAM_AXIS ) )
+ return false;
+
+ // Send a message for the achievement tracking.
+ IGameEvent *event = gameeventmanager->CreateEvent( "christmas_gift_grab" );
+ if ( event )
+ {
+ event->SetInt( "userid", pPlayer->GetUserID() );
+ gameeventmanager->FireEvent( event );
+ }
+ pPlayer->EmitSound( "Christmas.GiftPickup" );
+
+ return true;
+}
+
+//-----------------------------------------------------------------------------
+void CHolidayGift::ItemTouch( CBaseEntity *pOther )
+{
+ if ( pOther->IsWorld() )
+ {
+ Vector absVel = GetAbsVelocity();
+ SetAbsVelocity( Vector( 0,0,absVel.z ) );
+ return;
+ }
+
+ BaseClass::ItemTouch( pOther );
+}
diff --git a/game/server/dod/holiday_gift.h b/game/server/dod/holiday_gift.h
new file mode 100644
index 0000000..f5987bd
--- /dev/null
+++ b/game/server/dod/holiday_gift.h
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef HOLIDAY_GIFT_H
+#define HOLIDAY_GIFT_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+#include "items.h"
+
+
+class CHolidayGift: public CItem
+{
+public:
+ DECLARE_CLASS( CHolidayGift, CItem );
+
+public:
+
+ virtual void Precache();
+ virtual void Spawn( void );
+ virtual bool MyTouch( CBasePlayer *pBasePlayer );
+ virtual void ItemTouch( CBaseEntity *pOther );
+ void DropSoundThink( void );
+
+public:
+
+ static CHolidayGift* Create( const Vector &position, const QAngle &angles, const QAngle &eyeAngles, const Vector &velocity, CBaseCombatCharacter *pOwner );
+};
+
+
+#endif // HOLIDAY_GIFT_H
diff --git a/game/server/dod/rocket_bazooka.cpp b/game/server/dod/rocket_bazooka.cpp
new file mode 100644
index 0000000..5689cbf
--- /dev/null
+++ b/game/server/dod/rocket_bazooka.cpp
@@ -0,0 +1,30 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "rocket_bazooka.h"
+
+LINK_ENTITY_TO_CLASS( rocket_bazooka, CBazookaRocket );
+PRECACHE_WEAPON_REGISTER( rocket_bazooka );
+
+void CBazookaRocket::Spawn( void )
+{
+ SetModel( BAZOOKA_ROCKET_MODEL );
+
+ BaseClass::Spawn();
+}
+
+void CBazookaRocket::Precache( void )
+{
+ PrecacheModel( BAZOOKA_ROCKET_MODEL );
+
+ BaseClass::Precache();
+}
+
+CBazookaRocket *CBazookaRocket::Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner )
+{
+ return static_cast<CBazookaRocket *> ( CDODBaseRocket::Create( "rocket_bazooka", vecOrigin, vecAngles, pOwner ) );
+} \ No newline at end of file
diff --git a/game/server/dod/rocket_bazooka.h b/game/server/dod/rocket_bazooka.h
new file mode 100644
index 0000000..9671d5f
--- /dev/null
+++ b/game/server/dod/rocket_bazooka.h
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//
+//=============================================================================//
+#ifndef ROCKET_BAZOOKA_H
+#define ROCKET_BAZOOKA_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define BAZOOKA_ROCKET_MODEL "models/weapons/w_bazooka_rocket.mdl"
+
+#include "dod_baserocket.h"
+
+class CBazookaRocket : public CDODBaseRocket
+{
+public:
+ DECLARE_CLASS( CBazookaRocket, CDODBaseRocket );
+
+ CBazookaRocket() {}
+
+ virtual void Spawn();
+ virtual void Precache();
+
+ static CBazookaRocket *Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner );
+
+ virtual DODWeaponID GetEmitterWeaponID() { return WEAPON_BAZOOKA; }
+
+private:
+ CBazookaRocket( const CBazookaRocket & );
+};
+
+#endif //ROCKET_BAZOOKA_H \ No newline at end of file
diff --git a/game/server/dod/rocket_pschreck.cpp b/game/server/dod/rocket_pschreck.cpp
new file mode 100644
index 0000000..3272fca
--- /dev/null
+++ b/game/server/dod/rocket_pschreck.cpp
@@ -0,0 +1,30 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#include "cbase.h"
+#include "rocket_pschreck.h"
+
+LINK_ENTITY_TO_CLASS( rocket_pschreck, CPschreckRocket );
+PRECACHE_WEAPON_REGISTER( rocket_pschreck );
+
+void CPschreckRocket::Spawn( void )
+{
+ SetModel( PSCHRECK_ROCKET_MODEL );
+
+ BaseClass::Spawn();
+}
+
+void CPschreckRocket::Precache( void )
+{
+ PrecacheModel( PSCHRECK_ROCKET_MODEL );
+
+ BaseClass::Precache();
+}
+
+CPschreckRocket *CPschreckRocket::Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner )
+{
+ return static_cast<CPschreckRocket *> ( CDODBaseRocket::Create( "rocket_pschreck", vecOrigin, vecAngles, pOwner ) );
+} \ No newline at end of file
diff --git a/game/server/dod/rocket_pschreck.h b/game/server/dod/rocket_pschreck.h
new file mode 100644
index 0000000..d0c950c
--- /dev/null
+++ b/game/server/dod/rocket_pschreck.h
@@ -0,0 +1,36 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#ifndef ROCKET_PSCHRECK_H
+#define ROCKET_PSCHRECK_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+#define PSCHRECK_ROCKET_MODEL "models/weapons/w_panzerschreck_rocket.mdl"
+
+#include "dod_baserocket.h"
+
+class CPschreckRocket : public CDODBaseRocket
+{
+public:
+ DECLARE_CLASS( CPschreckRocket, CDODBaseRocket );
+
+ CPschreckRocket() {}
+
+ virtual void Spawn();
+ virtual void Precache();
+
+ static CPschreckRocket *Create( const Vector &vecOrigin, const QAngle &vecAngles, CBaseEntity *pOwner );
+
+ virtual DODWeaponID GetEmitterWeaponID() { return WEAPON_PSCHRECK; }
+
+private:
+ CPschreckRocket( const CPschreckRocket & );
+};
+
+#endif //ROCKET_PSCHRECK_H \ No newline at end of file
diff --git a/game/server/dod/te_firebullets.cpp b/game/server/dod/te_firebullets.cpp
new file mode 100644
index 0000000..503ed73
--- /dev/null
+++ b/game/server/dod/te_firebullets.cpp
@@ -0,0 +1,114 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $Workfile: $
+// $Date: $
+//
+//-----------------------------------------------------------------------------
+// $Log: $
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "basetempentity.h"
+
+
+#define NUM_BULLET_SEED_BITS 8
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Display's a blood sprite
+//-----------------------------------------------------------------------------
+class CTEFireBullets : public CBaseTempEntity
+{
+public:
+ DECLARE_CLASS( CTEFireBullets, CBaseTempEntity );
+ DECLARE_SERVERCLASS();
+
+ CTEFireBullets( const char *name );
+ virtual ~CTEFireBullets( void );
+
+ virtual void Create( IRecipientFilter& filter, float delay = 0.0f );
+
+
+public:
+ CNetworkVar( int, m_iPlayer );
+ CNetworkVector( m_vecOrigin );
+ CNetworkQAngle( m_vecAngles );
+ CNetworkVar( int, m_iWeaponID );
+ CNetworkVar( int, m_iMode );
+ CNetworkVar( int, m_iSeed );
+ CNetworkVar( float, m_flSpread );
+
+};
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : *name -
+//-----------------------------------------------------------------------------
+CTEFireBullets::CTEFireBullets( const char *name ) :
+ CBaseTempEntity( name )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTEFireBullets::~CTEFireBullets( void )
+{
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+// Input : msg_dest -
+// delay -
+// *origin -
+// *recipient -
+//-----------------------------------------------------------------------------
+void CTEFireBullets::Create( IRecipientFilter& filter, float delay )
+{
+ engine->PlaybackTempEntity( filter, delay,
+ (void *)this, GetServerClass()->m_pTable, GetServerClass()->m_ClassID );
+}
+
+IMPLEMENT_SERVERCLASS_ST_NOBASE(CTEFireBullets, DT_TEFireBullets)
+ SendPropVector( SENDINFO(m_vecOrigin), -1, SPROP_COORD ),
+ SendPropAngle( SENDINFO_VECTORELEM( m_vecAngles, 0 ), 13, 0 ),
+ SendPropAngle( SENDINFO_VECTORELEM( m_vecAngles, 1 ), 13, 0 ),
+ SendPropInt( SENDINFO( m_iWeaponID ), 5, SPROP_UNSIGNED ), // max 31 weapons
+ SendPropInt( SENDINFO( m_iMode ), 1, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iSeed ), NUM_BULLET_SEED_BITS, SPROP_UNSIGNED ),
+ SendPropInt( SENDINFO( m_iPlayer ), 6, SPROP_UNSIGNED ), // max 64 players, see MAX_PLAYERS
+ SendPropFloat( SENDINFO( m_flSpread ), 10, 0, 0, 1 ),
+END_SEND_TABLE()
+
+
+// Singleton
+static CTEFireBullets g_TEFireBullets( "FireBullets" );
+
+
+void TE_FireBullets(
+ int iPlayerIndex,
+ const Vector &vOrigin,
+ const QAngle &vAngles,
+ int iWeaponID,
+ int iMode,
+ int iSeed,
+ float flSpread )
+{
+ CPASFilter filter( vOrigin );
+ filter.UsePredictionRules();
+
+ g_TEFireBullets.m_iPlayer = iPlayerIndex-1;
+ g_TEFireBullets.m_vecOrigin = vOrigin;
+ g_TEFireBullets.m_vecAngles = vAngles;
+ g_TEFireBullets.m_iSeed = iSeed;
+ g_TEFireBullets.m_flSpread = flSpread;
+ g_TEFireBullets.m_iMode = iMode;
+ g_TEFireBullets.m_iWeaponID = iWeaponID;
+
+ Assert( iSeed < (1 << NUM_BULLET_SEED_BITS) );
+
+ g_TEFireBullets.Create( filter, 0 );
+}
diff --git a/game/server/dod/te_firebullets.h b/game/server/dod/te_firebullets.h
new file mode 100644
index 0000000..4dd85f4
--- /dev/null
+++ b/game/server/dod/te_firebullets.h
@@ -0,0 +1,25 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+#ifndef TE_FIREBULLETS_H
+#define TE_FIREBULLETS_H
+#ifdef _WIN32
+#pragma once
+#endif
+
+
+void TE_FireBullets(
+ int iPlayerIndex,
+ const Vector &vOrigin,
+ const QAngle &vAngles,
+ int iWeaponID,
+ int iMode,
+ int iSeed,
+ float flSpread
+ );
+
+
+#endif // TE_FIREBULLETS_H
diff --git a/game/server/dod/unisignals.h b/game/server/dod/unisignals.h
new file mode 100644
index 0000000..047ecd9
--- /dev/null
+++ b/game/server/dod/unisignals.h
@@ -0,0 +1,76 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+//=============================================================================//
+
+
+#ifndef __UNISIGNALS_H__
+#define __UNISIGNALS_H__
+
+
+
+// Unified signals allow state updating to be performed at one point in the
+// calling code. The sequence of events is:
+//
+// 1. Signal( whatever ), Signal( whatever ), etc.
+// 2. ( GetState() & Update() ) to get only the changes since the last update
+// 3. Goto 1
+//
+// Or, alternately:
+//
+// 1. Signal( whatever ), Signal( whatever ), etc.
+// 2. Update()
+// 3. GetState()
+// 4. Goto 1
+
+
+class CUnifiedSignals
+{
+public:
+ inline CUnifiedSignals();
+ inline int Update(); // Returns a mask for the changed state bits; signals are cleared after this update is performed
+ inline void Signal( int flSignal ); // ORed combo of BPSIG_xxx bits
+ inline int GetState(); // Returns the current state (ORed combo of BPSIG_xxx flags)
+
+private:
+ int m_flSignal; // States to trigger
+ int m_flState; // States after the previous update
+};
+
+
+
+inline CUnifiedSignals::CUnifiedSignals()
+{
+ m_flSignal = 0;
+ m_flState = 0;
+}
+
+
+
+inline int CUnifiedSignals::Update()
+{
+ int old = m_flState;
+ m_flState = m_flSignal;
+ m_flSignal = 0;
+
+ return m_flState ^ old;
+}
+
+
+
+inline void CUnifiedSignals::Signal( int flSignal )
+{
+ m_flSignal |= flSignal;
+}
+
+
+
+inline int CUnifiedSignals::GetState()
+{
+ return m_flState;
+}
+
+
+
+#endif // __UNISIGNALS_H__