diff options
Diffstat (limited to 'game/server/dod')
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__ |