diff options
Diffstat (limited to 'mp/src/game/server')
46 files changed, 881 insertions, 333 deletions
diff --git a/mp/src/game/server/AI_ResponseSystem.h b/mp/src/game/server/AI_ResponseSystem.h index a34255b2..a7b3a797 100644 --- a/mp/src/game/server/AI_ResponseSystem.h +++ b/mp/src/game/server/AI_ResponseSystem.h @@ -18,6 +18,7 @@ abstract_class IResponseFilter { public: + virtual ~IResponseFilter(){} virtual bool IsValidResponse( ResponseType_t type, const char *pszValue ) = 0; }; diff --git a/mp/src/game/server/BaseAnimatingOverlay.cpp b/mp/src/game/server/BaseAnimatingOverlay.cpp index 36534bc0..84be6fba 100644 --- a/mp/src/game/server/BaseAnimatingOverlay.cpp +++ b/mp/src/game/server/BaseAnimatingOverlay.cpp @@ -928,6 +928,25 @@ void CBaseAnimatingOverlay::SetLayerCycle( int iLayer, float flCycle, float flPr //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- +void CBaseAnimatingOverlay::SetLayerCycle( int iLayer, float flCycle, float flPrevCycle, float flLastEventCheck ) +{ + if (!IsValidLayer( iLayer )) + return; + + if (!m_AnimOverlay[iLayer].m_bLooping) + { + flCycle = clamp( flCycle, 0.0f, 1.0f ); + flPrevCycle = clamp( flPrevCycle, 0.0f, 1.0f ); + } + m_AnimOverlay[iLayer].m_flCycle = flCycle; + m_AnimOverlay[iLayer].m_flPrevCycle = flPrevCycle; + m_AnimOverlay[iLayer].m_flLastEventCheck = flLastEventCheck; + m_AnimOverlay[iLayer].MarkActive( ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- float CBaseAnimatingOverlay::GetLayerCycle( int iLayer ) { if (!IsValidLayer( iLayer )) diff --git a/mp/src/game/server/BaseAnimatingOverlay.h b/mp/src/game/server/BaseAnimatingOverlay.h index ac112cbd..5184eac3 100644 --- a/mp/src/game/server/BaseAnimatingOverlay.h +++ b/mp/src/game/server/BaseAnimatingOverlay.h @@ -165,6 +165,7 @@ public: void SetLayerCycle( int iLayer, float flCycle ); void SetLayerCycle( int iLayer, float flCycle, float flPrevCycle ); + void SetLayerCycle( int iLayer, float flCycle, float flPrevCycle, float flLastEventCheck ); float GetLayerCycle( int iLayer ); void SetLayerPlaybackRate( int iLayer, float flPlaybackRate ); diff --git a/mp/src/game/server/CommentarySystem.cpp b/mp/src/game/server/CommentarySystem.cpp index f846a029..677b4d84 100644 --- a/mp/src/game/server/CommentarySystem.cpp +++ b/mp/src/game/server/CommentarySystem.cpp @@ -839,7 +839,7 @@ void CC_CommentaryChanged( IConVar *pConVar, const char *pOldString, float flOld g_CommentarySystem.SetCommentaryMode( var.GetBool() ); } } -ConVar commentary("commentary", "0", FCVAR_ARCHIVE | FCVAR_ARCHIVE_XBOX, "Desired commentary mode state.", CC_CommentaryChanged ); +ConVar commentary( "commentary", "0", FCVAR_NONE, "Desired commentary mode state.", CC_CommentaryChanged ); //----------------------------------------------------------------------------- // Purpose: We need to revert back any convar changes that are made by the diff --git a/mp/src/game/server/GameStats_BasicStatsFunctions.cpp b/mp/src/game/server/GameStats_BasicStatsFunctions.cpp index 5656b6b7..47e3de77 100644 --- a/mp/src/game/server/GameStats_BasicStatsFunctions.cpp +++ b/mp/src/game/server/GameStats_BasicStatsFunctions.cpp @@ -155,7 +155,7 @@ bool BasicGameStats_t::ParseFromBuffer( CUtlBuffer& buf, int iBufferStatsVersion for ( int i = 0; i < c; ++i ) { char mapname[ 256 ]; - buf.GetString( mapname, sizeof( mapname ) ); + buf.GetString( mapname ); BasicGameStatsRecord_t *rec = FindOrAddRecordForMap( mapname ); bool valid= rec->ParseFromBuffer( buf, iBufferStatsVersion ); diff --git a/mp/src/game/server/ai_activity.cpp b/mp/src/game/server/ai_activity.cpp index e7ba1761..3f5b5b49 100644 --- a/mp/src/game/server/ai_activity.cpp +++ b/mp/src/game/server/ai_activity.cpp @@ -1686,6 +1686,11 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_MP_ATTACK_SWIM_GRENADE_ITEM2 ); ADD_ACTIVITY_TO_SR( ACT_MP_ATTACK_AIRWALK_GRENADE_ITEM2 ); + // Passtime + ADD_ACTIVITY_TO_SR( ACT_MP_STAND_PASSTIME ); + ADD_ACTIVITY_TO_SR( ACT_MP_RUN_PASSTIME ); + ADD_ACTIVITY_TO_SR( ACT_MP_CROUCHWALK_PASSTIME ); + // Flinches ADD_ACTIVITY_TO_SR( ACT_MP_GESTURE_FLINCH ); ADD_ACTIVITY_TO_SR( ACT_MP_GESTURE_FLINCH_PRIMARY ); @@ -1826,6 +1831,7 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_MP_DOUBLEJUMP_CROUCH_ITEM1 ); ADD_ACTIVITY_TO_SR( ACT_MP_DOUBLEJUMP_CROUCH_ITEM2 ); ADD_ACTIVITY_TO_SR( ACT_MP_DOUBLEJUMP_CROUCH_LOSERSTATE ); + ADD_ACTIVITY_TO_SR( ACT_MP_DOUBLEJUMP_CROUCH_PASSTIME ); ADD_ACTIVITY_TO_SR( ACT_MP_GESTURE_VC_HANDMOUTH ); ADD_ACTIVITY_TO_SR( ACT_MP_GESTURE_VC_FINGERPOINT ); @@ -1887,6 +1893,11 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_MP_STUN_MIDDLE ); ADD_ACTIVITY_TO_SR( ACT_MP_STUN_END ); + ADD_ACTIVITY_TO_SR( ACT_MP_PASSTIME_THROW_BEGIN ); + ADD_ACTIVITY_TO_SR( ACT_MP_PASSTIME_THROW_MIDDLE ); + ADD_ACTIVITY_TO_SR( ACT_MP_PASSTIME_THROW_END ); + ADD_ACTIVITY_TO_SR( ACT_MP_PASSTIME_THROW_CANCEL ); + ADD_ACTIVITY_TO_SR( ACT_VM_UNUSABLE ); ADD_ACTIVITY_TO_SR( ACT_VM_UNUSABLE_TO_USABLE ); ADD_ACTIVITY_TO_SR( ACT_VM_USABLE_TO_UNUSABLE ); @@ -2186,4 +2197,40 @@ void CAI_BaseNPC::InitDefaultActivitySR(void) ADD_ACTIVITY_TO_SR( ACT_BOT_GESTURE_FLINCH ); ADD_ACTIVITY_TO_SR( ACT_BOT_PANIC_START ); ADD_ACTIVITY_TO_SR( ACT_BOT_PANIC_END ); + + ADD_ACTIVITY_TO_SR( ACT_ENGINEER_REVOLVER_DRAW ); + ADD_ACTIVITY_TO_SR( ACT_ENGINEER_REVOLVER_IDLE ); + ADD_ACTIVITY_TO_SR( ACT_ENGINEER_REVOLVER_PRIMARYATTACK ); + ADD_ACTIVITY_TO_SR( ACT_ENGINEER_REVOLVER_RELOAD ); + + ADD_ACTIVITY_TO_SR( ACT_KART_IDLE ); + ADD_ACTIVITY_TO_SR( ACT_KART_ACTION_SHOOT ); + ADD_ACTIVITY_TO_SR( ACT_KART_ACTION_DASH ); + ADD_ACTIVITY_TO_SR( ACT_KART_JUMP_START ); + ADD_ACTIVITY_TO_SR( ACT_KART_JUMP_FLOAT ); + ADD_ACTIVITY_TO_SR( ACT_KART_JUMP_LAND ); + ADD_ACTIVITY_TO_SR( ACT_KART_IMPACT ); + ADD_ACTIVITY_TO_SR( ACT_KART_IMPACT_BIG ); + ADD_ACTIVITY_TO_SR( ACT_KART_GESTURE_POSITIVE ); + ADD_ACTIVITY_TO_SR( ACT_KART_GESTURE_NEGATIVE ); + + ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_DRAW ); + ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_IDLE ); + ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_FIRE_START ); + ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_FIRE_IDLE ); + ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_PULL_START ); + ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_PULL_IDLE ); + ADD_ACTIVITY_TO_SR( ACT_GRAPPLE_PULL_END ); + + ADD_ACTIVITY_TO_SR( ACT_PRIMARY_VM_INSPECT_START ); + ADD_ACTIVITY_TO_SR( ACT_PRIMARY_VM_INSPECT_IDLE ); + ADD_ACTIVITY_TO_SR( ACT_PRIMARY_VM_INSPECT_END ); + + ADD_ACTIVITY_TO_SR( ACT_SECONDARY_VM_INSPECT_START ); + ADD_ACTIVITY_TO_SR( ACT_SECONDARY_VM_INSPECT_IDLE ); + ADD_ACTIVITY_TO_SR( ACT_SECONDARY_VM_INSPECT_END ); + + ADD_ACTIVITY_TO_SR( ACT_MELEE_VM_INSPECT_START ); + ADD_ACTIVITY_TO_SR( ACT_MELEE_VM_INSPECT_IDLE ); + ADD_ACTIVITY_TO_SR( ACT_MELEE_VM_INSPECT_END ); } diff --git a/mp/src/game/server/baseanimating.cpp b/mp/src/game/server/baseanimating.cpp index 34bf6378..9999c496 100644 --- a/mp/src/game/server/baseanimating.cpp +++ b/mp/src/game/server/baseanimating.cpp @@ -470,7 +470,6 @@ void CBaseAnimating::StudioFrameAdvanceManual( float flInterval ) if ( !pStudioHdr ) return; - UpdateModelScale(); m_flAnimTime = gpGlobals->curtime; m_flPrevAnimTime = m_flAnimTime - flInterval; float flCycleRate = GetSequenceCycleRate( pStudioHdr, GetSequence() ) * m_flPlaybackRate; @@ -490,8 +489,6 @@ void CBaseAnimating::StudioFrameAdvance() return; } - UpdateModelScale(); - if ( !m_flPrevAnimTime ) { m_flPrevAnimTime = m_flAnimTime; @@ -631,7 +628,7 @@ void CBaseAnimating::InputSetModelScale( inputdata_t &inputdata ) int CBaseAnimating::SelectWeightedSequence ( Activity activity ) { Assert( activity != ACT_INVALID ); - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); return ::SelectWeightedSequence( GetModelPtr(), activity, GetSequence() ); } @@ -639,16 +636,23 @@ int CBaseAnimating::SelectWeightedSequence ( Activity activity ) int CBaseAnimating::SelectWeightedSequence ( Activity activity, int curSequence ) { Assert( activity != ACT_INVALID ); - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); return ::SelectWeightedSequence( GetModelPtr(), activity, curSequence ); } +int CBaseAnimating::SelectWeightedSequenceFromModifiers( Activity activity, CUtlSymbol *pActivityModifiers, int iModifierCount ) +{ + Assert( activity != ACT_INVALID ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); + return GetModelPtr()->SelectWeightedSequenceFromModifiers( activity, pActivityModifiers, iModifierCount ); +} + //========================================================= // ResetActivityIndexes //========================================================= void CBaseAnimating::ResetActivityIndexes ( void ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); ::ResetActivityIndexes( GetModelPtr() ); } @@ -657,7 +661,7 @@ void CBaseAnimating::ResetActivityIndexes ( void ) //========================================================= void CBaseAnimating::ResetEventIndexes ( void ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); ::ResetEventIndexes( GetModelPtr() ); } @@ -669,7 +673,7 @@ void CBaseAnimating::ResetEventIndexes ( void ) //========================================================= int CBaseAnimating::SelectHeaviestSequence ( Activity activity ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); return ::SelectHeaviestSequence( GetModelPtr(), activity ); } @@ -681,7 +685,7 @@ int CBaseAnimating::SelectHeaviestSequence ( Activity activity ) //----------------------------------------------------------------------------- int CBaseAnimating::LookupActivity( const char *label ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); return ::LookupActivity( GetModelPtr(), label ); } @@ -689,7 +693,7 @@ int CBaseAnimating::LookupActivity( const char *label ) //========================================================= int CBaseAnimating::LookupSequence( const char *label ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); return ::LookupSequence( GetModelPtr(), label ); } @@ -729,7 +733,7 @@ float CBaseAnimating::GetSequenceMoveYaw( int iSequence ) { Vector vecReturn; - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); ::GetSequenceLinearMotion( GetModelPtr(), iSequence, GetPoseParameterArray(), &vecReturn ); if (vecReturn.Length() > 0) @@ -765,7 +769,7 @@ float CBaseAnimating::GetSequenceMoveDist( CStudioHdr *pStudioHdr, int iSequence //----------------------------------------------------------------------------- void CBaseAnimating::GetSequenceLinearMotion( int iSequence, Vector *pVec ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); ::GetSequenceLinearMotion( GetModelPtr(), iSequence, GetPoseParameterArray(), pVec ); } @@ -912,7 +916,7 @@ void CBaseAnimating::ResetSequenceInfo ( ) //========================================================= bool CBaseAnimating::IsValidSequence( int iSequence ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); CStudioHdr* pstudiohdr = GetModelPtr( ); if (iSequence < 0 || iSequence >= pstudiohdr->GetNumSeq()) { @@ -1779,7 +1783,7 @@ void CBaseAnimating::SetupBones( matrix3x4_t *pBoneToWorld, int boneMask ) MDLCACHE_CRITICAL_SECTION(); - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); CStudioHdr *pStudioHdr = GetModelPtr( ); @@ -2087,7 +2091,7 @@ void CBaseAnimating::GetEyeballs( Vector &origin, QAngle &angles ) //========================================================= int CBaseAnimating::FindTransitionSequence( int iCurrentSequence, int iGoalSequence, int *piDir ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); if (piDir == NULL) { @@ -2136,7 +2140,7 @@ void CBaseAnimating::SetBodygroup( int iGroup, int iValue ) { // SetBodygroup is not supported on pending dynamic models. Wait for it to load! // XXX TODO we could buffer up the group and value if we really needed to. -henryg - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); int newBody = m_nBody; ::SetBodygroup( GetModelPtr( ), newBody, iGroup, iValue ); m_nBody = newBody; @@ -2735,7 +2739,7 @@ void CBaseAnimating::InitBoneControllers ( void ) // FIXME: rename //========================================================= float CBaseAnimating::SetBoneController ( int iController, float flValue ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); CStudioHdr *pmodel = (CStudioHdr*)GetModelPtr(); @@ -2752,7 +2756,7 @@ float CBaseAnimating::SetBoneController ( int iController, float flValue ) //========================================================= float CBaseAnimating::GetBoneController ( int iController ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); CStudioHdr *pmodel = (CStudioHdr*)GetModelPtr(); @@ -2943,7 +2947,7 @@ void CBaseAnimating::SetHitboxSet( int setnum ) //----------------------------------------------------------------------------- void CBaseAnimating::SetHitboxSetByName( const char *setname ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); m_nHitboxSet = FindHitboxSetByName( GetModelPtr(), setname ); } @@ -2962,7 +2966,7 @@ int CBaseAnimating::GetHitboxSet( void ) //----------------------------------------------------------------------------- const char *CBaseAnimating::GetHitboxSetName( void ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); return ::GetHitboxSetName( GetModelPtr(), m_nHitboxSet ); } @@ -2972,7 +2976,7 @@ const char *CBaseAnimating::GetHitboxSetName( void ) //----------------------------------------------------------------------------- int CBaseAnimating::GetHitboxSetCount( void ) { - Assert( GetModelPtr() ); + AssertMsg( GetModelPtr(), "GetModelPtr NULL. %s", STRING(GetEntityName()) ? STRING(GetEntityName()) : "" ); return ::GetHitboxSetCount( GetModelPtr() ); } @@ -3303,6 +3307,7 @@ void CBaseAnimating::SetModelScale( float scale, float change_duration /*= 0.0f* mvs->m_flModelScaleGoal = scale; mvs->m_flModelScaleStartTime = gpGlobals->curtime; mvs->m_flModelScaleFinishTime = mvs->m_flModelScaleStartTime + change_duration; + SetContextThink( &CBaseAnimating::UpdateModelScale, gpGlobals->curtime, "UpdateModelScaleThink" ); } else { @@ -3341,6 +3346,11 @@ void CBaseAnimating::UpdateModelScale() } RefreshCollisionBounds(); + + if ( frac < 1.f ) + { + SetContextThink( &CBaseAnimating::UpdateModelScale, gpGlobals->curtime, "UpdateModelScaleThink" ); + } } void CBaseAnimating::RefreshCollisionBounds( void ) diff --git a/mp/src/game/server/baseanimating.h b/mp/src/game/server/baseanimating.h index 75300e8e..0550683e 100644 --- a/mp/src/game/server/baseanimating.h +++ b/mp/src/game/server/baseanimating.h @@ -108,6 +108,7 @@ public: void ResetEventIndexes ( void ); int SelectWeightedSequence ( Activity activity ); int SelectWeightedSequence ( Activity activity, int curSequence ); + int SelectWeightedSequenceFromModifiers( Activity activity, CUtlSymbol *pActivityModifiers, int iModifierCount ); int SelectHeaviestSequence ( Activity activity ); int LookupActivity( const char *label ); int LookupSequence ( const char *label ); @@ -436,10 +437,14 @@ inline CStudioHdr *CBaseAnimating::GetModelPtr( void ) return NULL; #ifdef _DEBUG - // GetModelPtr() is often called before OnNewModel() so go ahead and set it up first chance. - static IDataCacheSection *pModelCache = datacache->FindSection( "ModelData" ); - AssertOnce( pModelCache->IsFrameLocking() ); + if ( !HushAsserts() ) + { + // GetModelPtr() is often called before OnNewModel() so go ahead and set it up first chance. + static IDataCacheSection *pModelCache = datacache->FindSection( "ModelData" ); + AssertOnce( pModelCache->IsFrameLocking() ); + } #endif + if ( !m_pStudioHdr && GetModel() ) { LockStudioHdr(); diff --git a/mp/src/game/server/basecombatcharacter.cpp b/mp/src/game/server/basecombatcharacter.cpp index 9f6a8674..71d6c844 100644 --- a/mp/src/game/server/basecombatcharacter.cpp +++ b/mp/src/game/server/basecombatcharacter.cpp @@ -2280,8 +2280,8 @@ CBaseCombatWeapon *CBaseCombatCharacter::Weapon_GetWpnForAmmo( int iAmmoIndex ) //----------------------------------------------------------------------------- bool CBaseCombatCharacter::Weapon_CanUse( CBaseCombatWeapon *pWeapon ) { - acttable_t *pTable = pWeapon->ActivityList(); - int actCount = pWeapon->ActivityListCount(); + int actCount = 0; + acttable_t *pTable = pWeapon->ActivityList( actCount ); if( actCount < 1 ) { diff --git a/mp/src/game/server/baseentity.cpp b/mp/src/game/server/baseentity.cpp index 7a7a7e6c..9bd525a4 100644 --- a/mp/src/game/server/baseentity.cpp +++ b/mp/src/game/server/baseentity.cpp @@ -86,6 +86,7 @@ bool CBaseEntity::sm_bDisableTouchFuncs = false; // Disables PhysicsTouch and Ph bool CBaseEntity::sm_bAccurateTriggerBboxChecks = true; // set to false for legacy behavior in ep1 int CBaseEntity::m_nPredictionRandomSeed = -1; +int CBaseEntity::m_nPredictionRandomSeedServer = -1; CBasePlayer *CBaseEntity::m_pPredictionPlayer = NULL; // Used to make sure nobody calls UpdateTransmitState directly. @@ -1440,10 +1441,10 @@ int CBaseEntity::OnTakeDamage( const CTakeDamageInfo &info ) //----------------------------------------------------------------------------- // Purpose: Scale damage done and call OnTakeDamage //----------------------------------------------------------------------------- -void CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo ) +int CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo ) { if ( !g_pGameRules ) - return; + return 0; bool bHasPhysicsForceDamage = !g_pGameRules->Damage_NoPhysicsForce( inputInfo.GetDamageType() ); if ( bHasPhysicsForceDamage && inputInfo.GetDamageType() != DMG_GENERIC ) @@ -1475,12 +1476,12 @@ void CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo ) // Make sure our damage filter allows the damage. if ( !PassesDamageFilter( inputInfo )) { - return; + return 0; } if( !g_pGameRules->AllowDamage(this, inputInfo) ) { - return; + return 0; } if ( PhysIsInCallback() ) @@ -1502,8 +1503,9 @@ void CBaseEntity::TakeDamage( const CTakeDamageInfo &inputInfo ) //Msg("%s took %.2f Damage, at %.2f\n", GetClassname(), info.GetDamage(), gpGlobals->curtime ); - OnTakeDamage( info ); + return OnTakeDamage( info ); } + return 0; } //----------------------------------------------------------------------------- @@ -6089,7 +6091,7 @@ void CBaseEntity::SetLocalAngles( const QAngle& angles ) { Warning( "Bad SetLocalAngles(%f,%f,%f) on %s\n", angles.x, angles.y, angles.z, GetDebugName() ); } - Assert( false ); + AssertMsg( false, "Bad SetLocalAngles(%f,%f,%f) on %s\n", angles.x, angles.y, angles.z, GetDebugName() ); return; } diff --git a/mp/src/game/server/baseentity.h b/mp/src/game/server/baseentity.h index 7261e0a8..42c0cdf2 100644 --- a/mp/src/game/server/baseentity.h +++ b/mp/src/game/server/baseentity.h @@ -904,7 +904,7 @@ public: virtual int OnTakeDamage( const CTakeDamageInfo &info ); // This is what you should call to apply damage to an entity. - void TakeDamage( const CTakeDamageInfo &info ); + int TakeDamage( const CTakeDamageInfo &info ); virtual void AdjustDamageDirection( const CTakeDamageInfo &info, Vector &dir, CBaseEntity *pEnt ) {} virtual int TakeHealth( float flHealth, int bitsDamageType ); @@ -1748,6 +1748,7 @@ private: // randon number generators to spit out the same random numbers on both sides for a particular // usercmd input. static int m_nPredictionRandomSeed; + static int m_nPredictionRandomSeedServer; static CBasePlayer *m_pPredictionPlayer; // FIXME: Make hierarchy a member of CBaseEntity @@ -1761,7 +1762,7 @@ private: public: // Accessors for above - static int GetPredictionRandomSeed( void ); + static int GetPredictionRandomSeed( bool bUseUnSyncedServerPlatTime = false ); static void SetPredictionRandomSeed( const CUserCmd *cmd ); static CBasePlayer *GetPredictionPlayer( void ); static void SetPredictionPlayer( CBasePlayer *player ); @@ -1799,6 +1800,8 @@ public: { return s_bAbsQueriesValid; } + + virtual bool ShouldBlockNav() const { return true; } }; // Send tables exposed in this module. diff --git a/mp/src/game/server/baseflex.cpp b/mp/src/game/server/baseflex.cpp index 5c435d73..b760ed54 100644 --- a/mp/src/game/server/baseflex.cpp +++ b/mp/src/game/server/baseflex.cpp @@ -113,7 +113,7 @@ CBaseFlex::CBaseFlex( void ) : CBaseFlex::~CBaseFlex( void ) { m_LocalToGlobal.RemoveAll(); - Assert( m_SceneEvents.Count() == 0 ); + AssertMsg( m_SceneEvents.Count() == 0, "m_ScenesEvent.Count != 0: %d", m_SceneEvents.Count() ); } void CBaseFlex::SetModel( const char *szModelName ) @@ -508,7 +508,7 @@ bool CBaseFlex::HandleStartSequenceSceneEvent( CSceneEventInfo *info, CChoreoSce float seq_duration = SequenceDuration( info->m_nSequence ); float flCycle = dt / seq_duration; flCycle = flCycle - (int)flCycle; // loop - SetLayerCycle( info->m_iLayer, flCycle, flCycle ); + SetLayerCycle( info->m_iLayer, flCycle, flCycle, 0.f ); SetLayerPlaybackRate( info->m_iLayer, 0.0 ); } diff --git a/mp/src/game/server/client.cpp b/mp/src/game/server/client.cpp index f500ad5e..eced1df1 100644 --- a/mp/src/game/server/client.cpp +++ b/mp/src/game/server/client.cpp @@ -57,6 +57,60 @@ extern bool IsInCommentaryMode( void ); ConVar *sv_cheats = NULL; +enum eAllowPointServerCommand { + eAllowNever, + eAllowOfficial, + eAllowAlways +}; + +#ifdef TF_DLL +// The default value here should match the default of the convar +eAllowPointServerCommand sAllowPointServerCommand = eAllowOfficial; +#else +eAllowPointServerCommand sAllowPointServerCommand = eAllowAlways; +#endif // TF_DLL + +void sv_allow_point_servercommand_changed( IConVar *pConVar, const char *pOldString, float flOldValue ) +{ + ConVarRef var( pConVar ); + if ( !var.IsValid() ) + { + return; + } + + const char *pNewValue = var.GetString(); + if ( V_strcasecmp ( pNewValue, "always" ) == 0 ) + { + sAllowPointServerCommand = eAllowAlways; + } +#ifdef TF_DLL + else if ( V_strcasecmp ( pNewValue, "official" ) == 0 ) + { + sAllowPointServerCommand = eAllowOfficial; + } +#endif // TF_DLL + else + { + sAllowPointServerCommand = eAllowNever; + } +} + +ConVar sv_allow_point_servercommand ( "sv_allow_point_servercommand", +#ifdef TF_DLL + // The default value here should match the default of the convar + "official", +#else + // Other games may use this in their official maps, and only TF exposes IsValveMap() currently + "always", +#endif // TF_DLL + FCVAR_NONE, + "Allow use of point_servercommand entities in map. Potentially dangerous for untrusted maps.\n" + " disallow : Always disallow\n" +#ifdef TF_DLL + " official : Allowed for valve maps only\n" +#endif // TF_DLL + " always : Allow for all maps", sv_allow_point_servercommand_changed ); + void ClientKill( edict_t *pEdict, const Vector &vecForce, bool bExplode = false ) { CBasePlayer *pPlayer = static_cast<CBasePlayer*>( GetContainingEntity( pEdict ) ); @@ -569,7 +623,22 @@ void CPointServerCommand::InputCommand( inputdata_t& inputdata ) if ( !inputdata.value.String()[0] ) return; - engine->ServerCommand( UTIL_VarArgs( "%s\n", inputdata.value.String() ) ); + bool bAllowed = ( sAllowPointServerCommand == eAllowAlways ); +#ifdef TF_DLL + if ( sAllowPointServerCommand == eAllowOfficial ) + { + bAllowed = TFGameRules() && TFGameRules()->IsValveMap(); + } +#endif // TF_DLL + + if ( bAllowed ) + { + engine->ServerCommand( UTIL_VarArgs( "%s\n", inputdata.value.String() ) ); + } + else + { + Warning( "point_servercommand usage blocked by sv_allow_point_servercommand setting\n" ); + } } BEGIN_DATADESC( CPointServerCommand ) @@ -600,7 +669,7 @@ void CC_DrawLine( const CCommand &args ) static ConCommand drawline("drawline", CC_DrawLine, "Draws line between two 3D Points.\n\tGreen if no collision\n\tRed is collides with something\n\tArguments: x1 y1 z1 x2 y2 z2", FCVAR_CHEAT); //------------------------------------------------------------------------------ -// Purpose : Draw a cross at a points. +// Purpose : Draw a cross at a points. // Input : // Output : //------------------------------------------------------------------------------ diff --git a/mp/src/game/server/doors.h b/mp/src/game/server/doors.h index 7658482c..9b485fe2 100644 --- a/mp/src/game/server/doors.h +++ b/mp/src/game/server/doors.h @@ -150,6 +150,8 @@ public: bool ShouldLoopMoveSound( void ) { return m_bLoopMoveSound; } bool m_bLoopMoveSound; // Move sound loops until stopped + virtual bool ShouldBlockNav() const OVERRIDE { return false; } + private: void ChainUse( void ); ///< Chains +use on through to m_ChainTarget void ChainTouch( CBaseEntity *pOther ); ///< Chains touch on through to m_ChainTarget diff --git a/mp/src/game/server/episodic/ep2_gamestats.h b/mp/src/game/server/episodic/ep2_gamestats.h index cc36301c..cef1f839 100644 --- a/mp/src/game/server/episodic/ep2_gamestats.h +++ b/mp/src/game/server/episodic/ep2_gamestats.h @@ -215,7 +215,7 @@ public: { Ep2LevelStats_t::EntityDeathsLump_t data; char npcName[ 512 ]; - LoadBuffer.GetString( npcName, sizeof( npcName ) ); + LoadBuffer.GetString( npcName ); LoadBuffer.Get( &data, sizeof( data ) ); pItem->m_dictEntityDeaths.Insert( npcName, data ); } @@ -229,7 +229,7 @@ public: { Ep2LevelStats_t::WeaponLump_t data; char weaponName[ 512 ]; - LoadBuffer.GetString( weaponName, sizeof( weaponName ) ); + LoadBuffer.GetString( weaponName ); LoadBuffer.Get( &data, sizeof( data ) ); pItem->m_dictWeapons.Insert( weaponName, data ); } @@ -240,7 +240,7 @@ public: Assert( pItem ); Ep2LevelStats_t::SaveGameInfo_t *info = &pItem->m_SaveGameInfo; char sz[ 512 ]; - LoadBuffer.GetString( sz, sizeof( sz ) ); + LoadBuffer.GetString( sz ); info->m_sCurrentSaveFile = sz; info->m_nCurrentSaveFileTime = LoadBuffer.GetInt(); int c = LoadBuffer.GetInt(); @@ -277,7 +277,7 @@ public: { Ep2LevelStats_t::GenericStatsLump_t data; char pchStatName[ 512 ]; - LoadBuffer.GetString( pchStatName, sizeof( pchStatName ) ); + LoadBuffer.GetString( pchStatName ); LoadBuffer.Get( &data, sizeof( data ) ); pItem->m_dictGeneric.Insert( pchStatName, data ); } diff --git a/mp/src/game/server/func_break.cpp b/mp/src/game/server/func_break.cpp index d0b815e2..e7043ea9 100644 --- a/mp/src/game/server/func_break.cpp +++ b/mp/src/game/server/func_break.cpp @@ -817,8 +817,6 @@ void CBreakable::VPhysicsCollision( int index, gamevcollisionevent_t *pEvent ) //----------------------------------------------------------------------------- int CBreakable::OnTakeDamage( const CTakeDamageInfo &info ) { - Vector vecTemp; - CTakeDamageInfo subInfo = info; // If attacker can't do at least the min required damage to us, don't take any damage from them @@ -832,8 +830,6 @@ int CBreakable::OnTakeDamage( const CTakeDamageInfo &info ) return 1; } - vecTemp = subInfo.GetInflictor()->GetAbsOrigin() - WorldSpaceCenter(); - if (!IsBreakable()) return 0; diff --git a/mp/src/game/server/gameinterface.cpp b/mp/src/game/server/gameinterface.cpp index b20daf08..f6bbebb4 100644 --- a/mp/src/game/server/gameinterface.cpp +++ b/mp/src/game/server/gameinterface.cpp @@ -99,6 +99,7 @@ #include "tf_gamerules.h" #include "tf_lobby.h" #include "player_vs_environment/tf_population_manager.h" +#include "workshop/maps_workshop.h" extern ConVar tf_mm_trusted; extern ConVar tf_mm_servermode; @@ -559,11 +560,11 @@ void DrawAllDebugOverlays( void ) CServerGameDLL g_ServerGameDLL; // INTERFACEVERSION_SERVERGAMEDLL_VERSION_8 is compatible with the latest since we're only adding things to the end, so expose that as well. -EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL008, INTERFACEVERSION_SERVERGAMEDLL_VERSION_8, g_ServerGameDLL ); +//EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL008, INTERFACEVERSION_SERVERGAMEDLL_VERSION_8, g_ServerGameDLL ); EXPOSE_SINGLE_INTERFACE_GLOBALVAR(CServerGameDLL, IServerGameDLL, INTERFACEVERSION_SERVERGAMEDLL, g_ServerGameDLL); // When bumping the version to this interface, check that our assumption is still valid and expose the older version in the same way -COMPILE_TIME_ASSERT( INTERFACEVERSION_SERVERGAMEDLL_INT == 9 ); +COMPILE_TIME_ASSERT( INTERFACEVERSION_SERVERGAMEDLL_INT == 10 ); bool CServerGameDLL::DLLInit( CreateInterfaceFn appSystemFactory, CreateInterfaceFn physicsFactory, CreateInterfaceFn fileSystemFactory, @@ -1081,9 +1082,7 @@ bool g_bCheckForChainedActivate; { \ if ( bCheck ) \ { \ - char msg[ 1024 ]; \ - Q_snprintf( msg, sizeof( msg ), "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) ); \ - AssertMsg( g_bReceivedChainedActivate == true, msg ); \ + AssertMsg( g_bReceivedChainedActivate, "Entity (%i/%s/%s) failed to call base class Activate()\n", pClass->entindex(), pClass->GetClassname(), STRING( pClass->GetEntityName() ) ); \ } \ g_bCheckForChainedActivate = false; \ } @@ -1100,7 +1099,7 @@ void CServerGameDLL::ServerActivate( edict_t *pEdictList, int edictCount, int cl if ( gEntList.ResetDeleteList() != 0 ) { - Msg( "ERROR: Entity delete queue not empty on level start!\n" ); + Msg( "%s", "ERROR: Entity delete queue not empty on level start!\n" ); } for ( CBaseEntity *pClass = gEntList.FirstEnt(); pClass != NULL; pClass = gEntList.NextEnt(pClass) ) @@ -1150,6 +1149,7 @@ void CServerGameDLL::ServerActivate( edict_t *pEdictList, int edictCount, int cl void CServerGameDLL::GameServerSteamAPIActivated( void ) { #ifndef NO_STEAM + steamgameserverapicontext->Clear(); steamgameserverapicontext->Init(); if ( steamgameserverapicontext->SteamGameServer() && engine->IsDedicatedServer() ) { @@ -1160,6 +1160,7 @@ void CServerGameDLL::GameServerSteamAPIActivated( void ) #ifdef TF_DLL GCClientSystem()->GameServerActivate(); InventoryManager()->GameServerSteamAPIActivated(); + TFMapsWorkshop()->GameServerSteamAPIActivated(); #endif } @@ -1937,6 +1938,52 @@ void CServerGameDLL::Status( void (*print) (const char *fmt, ...) ) } //----------------------------------------------------------------------------- +void CServerGameDLL::PrepareLevelResources( /* in/out */ char *pszMapName, size_t nMapNameSize, + /* in/out */ char *pszMapFile, size_t nMapFileSize ) +{ +#ifdef TF_DLL + TFMapsWorkshop()->PrepareLevelResources( pszMapName, nMapNameSize, pszMapFile, nMapFileSize ); +#endif // TF_DLL +} + +//----------------------------------------------------------------------------- +IServerGameDLL::ePrepareLevelResourcesResult +CServerGameDLL::AsyncPrepareLevelResources( /* in/out */ char *pszMapName, size_t nMapNameSize, + /* in/out */ char *pszMapFile, size_t nMapFileSize, + float *flProgress /* = NULL */ ) +{ +#ifdef TF_DLL + return TFMapsWorkshop()->AsyncPrepareLevelResources( pszMapName, nMapNameSize, pszMapFile, nMapFileSize, flProgress ); +#endif // TF_DLL + + if ( flProgress ) + { + *flProgress = 1.f; + } + return IServerGameDLL::ePrepareLevelResources_Prepared; +} + +//----------------------------------------------------------------------------- +IServerGameDLL::eCanProvideLevelResult CServerGameDLL::CanProvideLevel( /* in/out */ char *pMapName, int nMapNameMax ) +{ +#ifdef TF_DLL + return TFMapsWorkshop()->OnCanProvideLevel( pMapName, nMapNameMax ); +#endif // TF_DLL + return IServerGameDLL::eCanProvideLevel_CannotProvide; +} + +//----------------------------------------------------------------------------- +bool CServerGameDLL::IsManualMapChangeOkay( const char **pszReason ) +{ + if ( GameRules() ) + { + return GameRules()->IsManualMapChangeOkay( pszReason ); + } + + return true; +} + +//----------------------------------------------------------------------------- // Purpose: Called during a transition, to build a map adjacency list //----------------------------------------------------------------------------- void CServerGameDLL::BuildAdjacentMapList( void ) @@ -2648,7 +2695,7 @@ void CServerGameClients::ClientActive( edict_t *pEdict, bool bLoadGame ) #if defined( TF_DLL ) Assert( pPlayer ); - if ( pPlayer && !pPlayer->IsFakeClient() ) + if ( pPlayer && !pPlayer->IsFakeClient() && !pPlayer->IsHLTV() && !pPlayer->IsReplay() ) { CSteamID steamID; if ( pPlayer->GetSteamID( &steamID ) ) @@ -2657,7 +2704,10 @@ void CServerGameClients::ClientActive( edict_t *pEdict, bool bLoadGame ) } else { - Log("WARNING: ClientActive, but we don't know his SteamID?\n"); + if ( !pPlayer->IsReplay() && !pPlayer->IsHLTV() ) + { + Log("WARNING: ClientActive, but we don't know his SteamID?\n"); + } } } #endif @@ -2731,7 +2781,10 @@ void CServerGameClients::ClientDisconnect( edict_t *pEdict ) } else { - Log("WARNING: ClientDisconnected, but we don't know his SteamID?\n"); + if ( !player->IsReplay() && !player->IsHLTV() ) + { + Log("WARNING: ClientDisconnected, but we don't know his SteamID?\n"); + } } } #endif diff --git a/mp/src/game/server/gameinterface.h b/mp/src/game/server/gameinterface.h index 91d6534c..3092d4f3 100644 --- a/mp/src/game/server/gameinterface.h +++ b/mp/src/game/server/gameinterface.h @@ -139,7 +139,19 @@ public: virtual const char *GetServerBrowserGameData() OVERRIDE; // Called to add output to the status command - virtual void Status( void (*print) (const char *fmt, ...) ); + virtual void Status( void (*print) (const char *fmt, ...) ) OVERRIDE; + + virtual void PrepareLevelResources( /* in/out */ char *pszMapName, size_t nMapNameSize, + /* in/out */ char *pszMapFile, size_t nMapFileSize ) OVERRIDE; + + virtual ePrepareLevelResourcesResult AsyncPrepareLevelResources( /* in/out */ char *pszMapName, size_t nMapNameSize, + /* in/out */ char *pszMapFile, size_t nMapFileSize, + float *flProgress = NULL ) OVERRIDE; + + virtual eCanProvideLevelResult CanProvideLevel( /* in/out */ char *pMapName, int nMapNameMax ) OVERRIDE; + + // Called to see if the game server is okay with a manual changelevel or map command + virtual bool IsManualMapChangeOkay( const char **pszReason ) OVERRIDE; private: diff --git a/mp/src/game/server/items.h b/mp/src/game/server/items.h index 5e7672c6..bc1bb77e 100644 --- a/mp/src/game/server/items.h +++ b/mp/src/game/server/items.h @@ -82,9 +82,10 @@ public: DECLARE_DATADESC(); protected: virtual void ComeToRest( void ); + bool m_bActivateWhenAtRest; private: - bool m_bActivateWhenAtRest; + COutputEvent m_OnPlayerTouch; COutputEvent m_OnCacheInteraction; diff --git a/mp/src/game/server/nav_area.cpp b/mp/src/game/server/nav_area.cpp index 335e6c3b..656f675d 100644 --- a/mp/src/game/server/nav_area.cpp +++ b/mp/src/game/server/nav_area.cpp @@ -3707,7 +3707,7 @@ static Vector FindPositionInArea( CNavArea *area, NavCornerType corner ) pos = cornerPos + Vector( area->GetSizeX()*0.5f*multX, area->GetSizeY()*0.5f*multY, 0.0f ); if ( !area->IsOverlapping( pos ) ) { - AssertMsg( false, UTIL_VarArgs( "A Hiding Spot can't be placed on its area at (%.0f %.0f %.0f)", cornerPos.x, cornerPos.y, cornerPos.z) ); + AssertMsg( false, "A Hiding Spot can't be placed on its area at (%.0f %.0f %.0f)", cornerPos.x, cornerPos.y, cornerPos.z ); // Just pull the position to a small offset pos = cornerPos + Vector( 1.0f*multX, 1.0f*multY, 0.0f ); @@ -4285,6 +4285,9 @@ bool CNavArea::ComputeLighting( void ) //-------------------------------------------------------------------------------------------------------------- CON_COMMAND_F( nav_update_lighting, "Recomputes lighting values", FCVAR_CHEAT ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + int numComputed = 0; if ( args.ArgC() == 2 ) { diff --git a/mp/src/game/server/nav_area.h b/mp/src/game/server/nav_area.h index 3defa888..0c97a662 100644 --- a/mp/src/game/server/nav_area.h +++ b/mp/src/game/server/nav_area.h @@ -823,14 +823,9 @@ inline bool CNavArea::IsDegenerate( void ) const //-------------------------------------------------------------------------------------------------------------- inline CNavArea *CNavArea::GetAdjacentArea( NavDirType dir, int i ) const { - for( int iter = 0; iter < m_connect[dir].Count(); ++iter ) - { - if (i == 0) - return m_connect[dir][iter].area; - --i; - } - - return NULL; + if ( ( i < 0 ) || ( i >= m_connect[dir].Count() ) ) + return NULL; + return m_connect[dir][i].area; } //-------------------------------------------------------------------------------------------------------------- diff --git a/mp/src/game/server/nav_entities.cpp b/mp/src/game/server/nav_entities.cpp index 83a7dd83..04951113 100644 --- a/mp/src/game/server/nav_entities.cpp +++ b/mp/src/game/server/nav_entities.cpp @@ -91,8 +91,7 @@ void CFuncNavCost::Spawn( void ) // chop space-delimited string into individual tokens if ( tags ) { - char *buffer = new char [ strlen( tags ) + 1 ]; - Q_strcpy( buffer, tags ); + char *buffer = V_strdup ( tags ); for( char *token = strtok( buffer, " " ); token; token = strtok( NULL, " " ) ) { diff --git a/mp/src/game/server/nav_file.cpp b/mp/src/game/server/nav_file.cpp index 7de114ec..ea3bf5ad 100644 --- a/mp/src/game/server/nav_file.cpp +++ b/mp/src/game/server/nav_file.cpp @@ -1318,12 +1318,11 @@ const CUtlVector< Place > *CNavMesh::GetPlacesFromNavFile( bool *hasUnnamedPlace if ( IsX360() ) { // 360 has compressed NAVs - CLZMA lzma; - if ( lzma.IsCompressed( (unsigned char *)fileBuffer.Base() ) ) + if ( CLZMA::IsCompressed( (unsigned char *)fileBuffer.Base() ) ) { - int originalSize = lzma.GetActualSize( (unsigned char *)fileBuffer.Base() ); + int originalSize = CLZMA::GetActualSize( (unsigned char *)fileBuffer.Base() ); unsigned char *pOriginalData = new unsigned char[originalSize]; - lzma.Uncompress( (unsigned char *)fileBuffer.Base(), pOriginalData ); + CLZMA::Uncompress( (unsigned char *)fileBuffer.Base(), pOriginalData ); fileBuffer.AssumeMemory( pOriginalData, originalSize, originalSize, CUtlBuffer::READ_ONLY ); } } @@ -1411,12 +1410,11 @@ NavErrorType CNavMesh::Load( void ) if ( IsX360() ) { // 360 has compressed NAVs - CLZMA lzma; - if ( lzma.IsCompressed( (unsigned char *)fileBuffer.Base() ) ) + if ( CLZMA::IsCompressed( (unsigned char *)fileBuffer.Base() ) ) { - int originalSize = lzma.GetActualSize( (unsigned char *)fileBuffer.Base() ); + int originalSize = CLZMA::GetActualSize( (unsigned char *)fileBuffer.Base() ); unsigned char *pOriginalData = new unsigned char[originalSize]; - lzma.Uncompress( (unsigned char *)fileBuffer.Base(), pOriginalData ); + CLZMA::Uncompress( (unsigned char *)fileBuffer.Base(), pOriginalData ); fileBuffer.AssumeMemory( pOriginalData, originalSize, originalSize, CUtlBuffer::READ_ONLY ); } } diff --git a/mp/src/game/server/nav_generate.cpp b/mp/src/game/server/nav_generate.cpp index be9ce6b5..e10d01ea 100644 --- a/mp/src/game/server/nav_generate.cpp +++ b/mp/src/game/server/nav_generate.cpp @@ -1229,9 +1229,12 @@ void CNavMesh::RemoveOverlappingObstacleTopAreas() static void CommandNavCheckStairs( void ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + TheNavMesh->MarkStairAreas(); } -static ConCommand nav_check_stairs( "nav_check_stairs", CommandNavCheckStairs, "Update the nav mesh STAIRS attribute" ); +static ConCommand nav_check_stairs( "nav_check_stairs", CommandNavCheckStairs, "Update the nav mesh STAIRS attribute", FCVAR_CHEAT ); //-------------------------------------------------------------------------------------------------------------- /** @@ -1445,6 +1448,9 @@ bool CNavArea::TestStairs( void ) //-------------------------------------------------------------------------------------------------------------- CON_COMMAND_F( nav_test_stairs, "Test the selected set for being on stairs", FCVAR_CHEAT ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + int count = 0; const NavAreaVector &selectedSet = TheNavMesh->GetSelectedSet(); @@ -4932,5 +4938,8 @@ void CNavMesh::PostProcessCliffAreas() CON_COMMAND_F( nav_gen_cliffs_approx, "Mark cliff areas, post-processing approximation", FCVAR_CHEAT ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + TheNavMesh->PostProcessCliffAreas(); } diff --git a/mp/src/game/server/nav_mesh.cpp b/mp/src/game/server/nav_mesh.cpp index 4c38a6f5..09326aaf 100644 --- a/mp/src/game/server/nav_mesh.cpp +++ b/mp/src/game/server/nav_mesh.cpp @@ -1700,6 +1700,9 @@ static ConCommand nav_clear_selected_set( "nav_clear_selected_set", CommandNavCl //---------------------------------------------------------------------------------- CON_COMMAND_F( nav_dump_selected_set_positions, "Write the (x,y,z) coordinates of the centers of all selected nav areas to a file.", FCVAR_GAMEDLL | FCVAR_CHEAT ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + const NavAreaVector &selectedSet = TheNavMesh->GetSelectedSet(); CUtlBuffer fileBuffer( 4096, 1024*1024, CUtlBuffer::TEXT_BUFFER ); @@ -1732,6 +1735,9 @@ CON_COMMAND_F( nav_dump_selected_set_positions, "Write the (x,y,z) coordinates o //---------------------------------------------------------------------------------- CON_COMMAND_F( nav_show_dumped_positions, "Show the (x,y,z) coordinate positions of the given dump file.", FCVAR_GAMEDLL | FCVAR_CHEAT ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + CUtlBuffer fileBuffer( 4096, 1024*1024, CUtlBuffer::TEXT_BUFFER ); // filename is local to game dir for Steam, so we need to prepend game dir for regular file save @@ -1764,6 +1770,9 @@ CON_COMMAND_F( nav_show_dumped_positions, "Show the (x,y,z) coordinate positions //---------------------------------------------------------------------------------- CON_COMMAND_F( nav_select_larger_than, "Select nav areas where both dimensions are larger than the given size.", FCVAR_GAMEDLL | FCVAR_CHEAT ) { + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + if ( args.ArgC() > 1 ) { float minSize = atof( args[1] ); @@ -2665,6 +2674,9 @@ void CNavMesh::CommandNavMarkWalkable( void ) { Vector pos; + if ( !UTIL_IsCommandIssuedByServerAdmin() ) + return; + if (nav_edit.GetBool()) { // we are in edit mode, use the edit cursor's location diff --git a/mp/src/game/server/networkstringtable_gamedll.h b/mp/src/game/server/networkstringtable_gamedll.h index 9dbce97a..20965aa7 100644 --- a/mp/src/game/server/networkstringtable_gamedll.h +++ b/mp/src/game/server/networkstringtable_gamedll.h @@ -28,7 +28,7 @@ class CStringTableSaveRestoreOps; #define MAX_CHOREO_SCENES_STRINGS ( 1 << MAX_CHOREO_SCENES_STRING_BITS ) #define CHOREO_SCENES_INVALID_STRING ( MAX_CHOREO_SCENES_STRINGS - 1 ) -#define MAX_PARTICLESYSTEMS_STRING_BITS 11 +#define MAX_PARTICLESYSTEMS_STRING_BITS 12 #define MAX_PARTICLESYSTEMS_STRINGS ( 1 << MAX_PARTICLESYSTEMS_STRING_BITS ) #define PARTICLESYSTEMS_INVALID_STRING ( MAX_PARTICLESYSTEMS_STRINGS - 1 ) diff --git a/mp/src/game/server/player.cpp b/mp/src/game/server/player.cpp index a44ee551..e0e7ccd2 100644 --- a/mp/src/game/server/player.cpp +++ b/mp/src/game/server/player.cpp @@ -585,7 +585,9 @@ CBasePlayer::CBasePlayer( ) m_bForceOrigin = false; m_hVehicle = NULL; m_pCurrentCommand = NULL; - + m_iLockViewanglesTickNumber = 0; + m_qangLockViewangles.Init(); + // Setup our default FOV m_iDefaultFOV = g_pGameRules->DefaultFOV(); @@ -976,7 +978,7 @@ void CBasePlayer::DamageEffect(float flDamage, int fDamageType) } else if (fDamageType & DMG_DROWN) { - //Red damage indicator + //Blue damage indicator color32 blue = {0,0,128,128}; UTIL_ScreenFade( this, blue, 1.0f, 0.1f, FFADE_IN ); } @@ -2325,6 +2327,7 @@ bool CBasePlayer::SetObserverMode(int mode ) break; case OBS_MODE_CHASE : + case OBS_MODE_POI: // PASSTIME case OBS_MODE_IN_EYE : // udpate FOV and viewmodels SetObserverTarget( m_hObserverTarget ); @@ -2420,8 +2423,7 @@ void CBasePlayer::CheckObserverSettings() } // check if our spectating target is still a valid one - - if ( m_iObserverMode == OBS_MODE_IN_EYE || m_iObserverMode == OBS_MODE_CHASE || m_iObserverMode == OBS_MODE_FIXED ) + if ( m_iObserverMode == OBS_MODE_IN_EYE || m_iObserverMode == OBS_MODE_CHASE || m_iObserverMode == OBS_MODE_FIXED || m_iObserverMode == OBS_MODE_POI ) { ValidateCurrentObserverTarget(); @@ -2633,7 +2635,10 @@ bool CBasePlayer::SetObserverTarget(CBaseEntity *target) Vector dir, end; Vector start = target->EyePosition(); - AngleVectors( target->EyeAngles(), &dir ); + QAngle ang = target->EyeAngles(); + ang.z = 0; // PASSTIME no view roll when spectating ball + + AngleVectors( ang, &dir ); VectorNormalize( dir ); VectorMA( start, -64.0f, dir, end ); @@ -2643,7 +2648,7 @@ bool CBasePlayer::SetObserverTarget(CBaseEntity *target) trace_t tr; UTIL_TraceRay( ray, MASK_PLAYERSOLID, target, COLLISION_GROUP_PLAYER_MOVEMENT, &tr ); - JumptoPosition( tr.endpos, target->EyeAngles() ); + JumptoPosition( tr.endpos, ang ); } return true; @@ -3411,6 +3416,8 @@ void CBasePlayer::ForceSimulation() m_nSimulationTick = -1; } +ConVar sv_usercmd_custom_random_seed( "sv_usercmd_custom_random_seed", "1", FCVAR_CHEAT, "When enabled server will populate an additional random seed independent of the client" ); + //----------------------------------------------------------------------------- // Purpose: // Input : *buf - @@ -3437,6 +3444,16 @@ void CBasePlayer::ProcessUsercmds( CUserCmd *cmds, int numcmds, int totalcmds, pCmd->MakeInert(); } + if ( sv_usercmd_custom_random_seed.GetBool() ) + { + float fltTimeNow = float( Plat_FloatTime() * 1000.0 ); + pCmd->server_random_seed = *reinterpret_cast<int*>( (char*)&fltTimeNow ); + } + else + { + pCmd->server_random_seed = pCmd->random_seed; + } + ctx->cmds.AddToTail( *pCmd ); } ctx->numcmds = numcmds; @@ -7875,7 +7892,7 @@ void CMovementSpeedMod::InputSpeedMod(inputdata_t &data) // Bring the weapon back if ( HasSpawnFlags( SF_SPEED_MOD_SUPPRESS_WEAPONS ) && pPlayer->GetActiveWeapon() == NULL ) { - pPlayer->SetActiveWeapon( pPlayer->Weapon_GetLast() ); + pPlayer->SetActiveWeapon( pPlayer->GetLastWeapon() ); if ( pPlayer->GetActiveWeapon() ) { pPlayer->GetActiveWeapon()->Deploy(); @@ -8857,8 +8874,6 @@ void CBasePlayer::SetPlayerName( const char *name ) Assert( strlen(name) > 0 ); Q_strncpy( m_szNetname, name, sizeof(m_szNetname) ); - // Be extra thorough - Q_RemoveAllEvilCharacters( m_szNetname ); } } @@ -9386,4 +9401,4 @@ uint64 CBasePlayer::GetSteamIDAsUInt64( void ) return steamIDForPlayer.ConvertToUint64(); return 0; } -#endif // NO_STEAM
\ No newline at end of file +#endif // NO_STEAM diff --git a/mp/src/game/server/player.h b/mp/src/game/server/player.h index 8edefff3..e87af06d 100644 --- a/mp/src/game/server/player.h +++ b/mp/src/game/server/player.h @@ -415,7 +415,7 @@ public: virtual bool Weapon_ShouldSetLast( CBaseCombatWeapon *pOldWeapon, CBaseCombatWeapon *pNewWeapon ) { return true; } virtual bool Weapon_ShouldSelectItem( CBaseCombatWeapon *pWeapon ); void Weapon_DropSlot( int weaponSlot ); - CBaseCombatWeapon *Weapon_GetLast( void ) { return m_hLastWeapon.Get(); } + CBaseCombatWeapon *GetLastWeapon( void ) { return m_hLastWeapon.Get(); } virtual void OnMyWeaponFired( CBaseCombatWeapon *weapon ); // call this when this player fires a weapon to allow other systems to react virtual float GetTimeSinceWeaponFired( void ) const; // returns the time, in seconds, since this player fired a weapon @@ -735,6 +735,8 @@ public: bool IsPredictingWeapons( void ) const; int CurrentCommandNumber() const; const CUserCmd *GetCurrentUserCommand() const; + int GetLockViewanglesTickNumber() const { return m_iLockViewanglesTickNumber; } + QAngle GetLockViewanglesData() const { return m_qangLockViewangles; } int GetFOV( void ); // Get the current FOV value int GetDefaultFOV( void ) const; // Default FOV if not specified otherwise @@ -891,7 +893,8 @@ public: #if defined USES_ECON_ITEMS CEconWearable *GetWearable( int i ) { return m_hMyWearables[i]; } - int GetNumWearables( void ) { return m_hMyWearables.Count(); } + const CEconWearable *GetWearable( int i ) const { return m_hMyWearables[i]; } + int GetNumWearables( void ) const { return m_hMyWearables.Count(); } #endif private: @@ -1058,6 +1061,8 @@ protected: // Last received usercmd (in case we drop a lot of packets ) CUserCmd m_LastCmd; CUserCmd *m_pCurrentCommand; + int m_iLockViewanglesTickNumber; + QAngle m_qangLockViewangles; float m_flStepSoundTime; // time to check for next footstep sound diff --git a/mp/src/game/server/player_command.cpp b/mp/src/game/server/player_command.cpp index b607bbab..bf77b5d4 100644 --- a/mp/src/game/server/player_command.cpp +++ b/mp/src/game/server/player_command.cpp @@ -23,6 +23,7 @@ extern CMoveData *g_pMoveData; // This is a global because it is subclassed by e extern ConVar sv_noclipduringpause; ConVar sv_maxusrcmdprocessticks_warning( "sv_maxusrcmdprocessticks_warning", "-1", FCVAR_NONE, "Print a warning when user commands get dropped due to insufficient usrcmd ticks allocated, number of seconds to throttle, negative disabled" ); +static ConVar sv_maxusrcmdprocessticks_holdaim( "sv_maxusrcmdprocessticks_holdaim", "1", FCVAR_CHEAT, "Hold client aim for multiple server sim ticks when client-issued usrcmd contains multiple actions (0: off; 1: hold this server tick; 2+: hold multiple ticks)" ); //----------------------------------------------------------------------------- // Purpose: @@ -442,6 +443,12 @@ void CPlayerMove::RunCommand ( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper // Copy output FinishMove( player, ucmd, g_pMoveData ); + // If we have to restore the view angle then do so right now + if ( !player->IsBot() && ( gpGlobals->tickcount - player->GetLockViewanglesTickNumber() < sv_maxusrcmdprocessticks_holdaim.GetInt() ) ) + { + player->pl.v_angle = player->GetLockViewanglesData(); + } + // Let server invoke any needed impact functions VPROF_SCOPE_BEGIN( "moveHelper->ProcessImpacts" ); moveHelper->ProcessImpacts(); diff --git a/mp/src/game/server/point_spotlight.cpp b/mp/src/game/server/point_spotlight.cpp index 9ff28cf6..030537ed 100644 --- a/mp/src/game/server/point_spotlight.cpp +++ b/mp/src/game/server/point_spotlight.cpp @@ -56,6 +56,7 @@ private: private: bool m_bSpotlightOn; bool m_bEfficientSpotlight; + bool m_bIgnoreSolid; Vector m_vSpotlightTargetPos; Vector m_vSpotlightCurrentPos; Vector m_vSpotlightDir; @@ -88,6 +89,7 @@ BEGIN_DATADESC( CPointSpotlight ) DEFINE_FIELD( m_vSpotlightDir, FIELD_VECTOR ), DEFINE_FIELD( m_nHaloSprite, FIELD_INTEGER ), + DEFINE_KEYFIELD( m_bIgnoreSolid, FIELD_BOOLEAN, "IgnoreSolid" ), DEFINE_KEYFIELD( m_flSpotlightMaxLength,FIELD_FLOAT, "SpotlightLength"), DEFINE_KEYFIELD( m_flSpotlightGoalWidth,FIELD_FLOAT, "SpotlightWidth"), DEFINE_KEYFIELD( m_flHDRColorScale, FIELD_FLOAT, "HDRColorScale" ), @@ -118,6 +120,7 @@ CPointSpotlight::CPointSpotlight() #endif m_flHDRColorScale = 1.0f; m_nMinDXLevel = 0; + m_bIgnoreSolid = false; } @@ -332,12 +335,21 @@ void CPointSpotlight::SpotlightCreate(void) AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); - trace_t tr; - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr); + Vector vTargetPos; + if ( m_bIgnoreSolid ) + { + vTargetPos = GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength; + } + else + { + trace_t tr; + UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + m_vSpotlightDir * m_flSpotlightMaxLength, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + vTargetPos = tr.endpos; + } m_hSpotlightTarget = (CSpotlightEnd*)CreateEntityByName( "spotlight_end" ); m_hSpotlightTarget->Spawn(); - m_hSpotlightTarget->SetAbsOrigin( tr.endpos ); + m_hSpotlightTarget->SetAbsOrigin( vTargetPos ); m_hSpotlightTarget->SetOwnerEntity( this ); m_hSpotlightTarget->m_clrRender = m_clrRender; m_hSpotlightTarget->m_Radius = m_flSpotlightMaxLength; @@ -381,9 +393,17 @@ Vector CPointSpotlight::SpotlightCurrentPos(void) AngleVectors( GetAbsAngles(), &m_vSpotlightDir ); // Get beam end point. Only collide with solid objects, not npcs - trace_t tr; - UTIL_TraceLine( GetAbsOrigin(), GetAbsOrigin() + (m_vSpotlightDir * 2 * m_flSpotlightMaxLength), MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); - return tr.endpos; + Vector vEndPos = GetAbsOrigin() + ( m_vSpotlightDir * 2 * m_flSpotlightMaxLength ); + if ( m_bIgnoreSolid ) + { + return vEndPos; + } + else + { + trace_t tr; + UTIL_TraceLine( GetAbsOrigin(), vEndPos, MASK_SOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); + return tr.endpos; + } } //------------------------------------------------------------------------------ diff --git a/mp/src/game/server/props.cpp b/mp/src/game/server/props.cpp index 648191db..9ba6c11b 100644 --- a/mp/src/game/server/props.cpp +++ b/mp/src/game/server/props.cpp @@ -1811,6 +1811,8 @@ LINK_ENTITY_TO_CLASS( dynamic_prop, CDynamicProp ); LINK_ENTITY_TO_CLASS( prop_dynamic, CDynamicProp ); LINK_ENTITY_TO_CLASS( prop_dynamic_override, CDynamicProp ); +IMPLEMENT_AUTO_LIST( IPhysicsPropAutoList ); + BEGIN_DATADESC( CDynamicProp ) // Fields diff --git a/mp/src/game/server/props.h b/mp/src/game/server/props.h index 8db20f2f..7dc9f48a 100644 --- a/mp/src/game/server/props.h +++ b/mp/src/game/server/props.h @@ -327,7 +327,8 @@ protected: //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -class CPhysicsProp : public CBreakableProp +DECLARE_AUTO_LIST( IPhysicsPropAutoList ); +class CPhysicsProp : public CBreakableProp, public IPhysicsPropAutoList { DECLARE_CLASS( CPhysicsProp, CBreakableProp ); DECLARE_SERVERCLASS(); diff --git a/mp/src/game/server/recipientfilter.cpp b/mp/src/game/server/recipientfilter.cpp index d39419c3..64889b31 100644 --- a/mp/src/game/server/recipientfilter.cpp +++ b/mp/src/game/server/recipientfilter.cpp @@ -341,7 +341,7 @@ CTeamRecipientFilter::CTeamRecipientFilter( int team, bool isReliable ) if ( pPlayer->GetTeamNumber() != team ) { //If we're in the spectator team then we should be getting whatever messages the person I'm spectating gets. - if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR && (pPlayer->GetObserverMode() == OBS_MODE_IN_EYE || pPlayer->GetObserverMode() == OBS_MODE_CHASE) ) + if ( pPlayer->GetTeamNumber() == TEAM_SPECTATOR && (pPlayer->GetObserverMode() == OBS_MODE_IN_EYE || pPlayer->GetObserverMode() == OBS_MODE_CHASE || pPlayer->GetObserverMode() == OBS_MODE_POI) ) { if ( pPlayer->GetObserverTarget() ) { diff --git a/mp/src/game/server/sceneentity.cpp b/mp/src/game/server/sceneentity.cpp index ccdc4cd4..f092624f 100644 --- a/mp/src/game/server/sceneentity.cpp +++ b/mp/src/game/server/sceneentity.cpp @@ -4951,8 +4951,9 @@ void CSceneManager::RemoveScenesInvolvingActor( CBaseFlex *pActor ) if ( !pActor ) return; + // This loop can remove items from m_ActiveScenes array, so loop through backwards. int c = m_ActiveScenes.Count(); - for ( int i = 0; i < c; i++ ) + for ( int i = c - 1 ; i >= 0; --i ) { CSceneEntity *pScene = m_ActiveScenes[ i ].Get(); if ( !pScene ) diff --git a/mp/src/game/server/slideshow_display.cpp b/mp/src/game/server/slideshow_display.cpp index 79e78841..12e19ceb 100644 --- a/mp/src/game/server/slideshow_display.cpp +++ b/mp/src/game/server/slideshow_display.cpp @@ -528,8 +528,8 @@ void CSlideshowDisplay::BuildSlideShowImagesList( void ) if ( bLoaded ) { - char szKeywords[ 256 ]; - Q_strcpy( szKeywords, pMaterialKeys->GetString( "%keywords", "" ) ); + char szKeywords[ 256 ] = {0}; + V_strcpy_safe( szKeywords, pMaterialKeys->GetString( "%keywords", "" ) ); char *pchKeyword = szKeywords; @@ -562,7 +562,7 @@ void CSlideshowDisplay::BuildSlideShowImagesList( void ) { // Couldn't find the list, so create it iList = m_SlideKeywordList.AddToTail( new SlideKeywordList_t ); - Q_strcpy( m_SlideKeywordList[ iList ]->szSlideKeyword, pchKeyword ); + V_strcpy_safe( m_SlideKeywordList[iList]->szSlideKeyword, pchKeyword ); } pchKeyword = pNextKeyword; @@ -581,7 +581,7 @@ void CSlideshowDisplay::BuildSlideShowImagesList( void ) { // Couldn't find the generic list, so create it iList = m_SlideKeywordList.AddToHead( new SlideKeywordList_t ); - Q_strcpy( m_SlideKeywordList[ iList ]->szSlideKeyword, "" ); + V_strcpy_safe( m_SlideKeywordList[iList]->szSlideKeyword, "" ); } if ( IsX360() ) diff --git a/mp/src/game/server/team_control_point.cpp b/mp/src/game/server/team_control_point.cpp index 1bbec70a..a1288ad7 100644 --- a/mp/src/game/server/team_control_point.cpp +++ b/mp/src/game/server/team_control_point.cpp @@ -17,6 +17,7 @@ #ifdef TF_DLL #include "tf_shareddefs.h" +#include "tf_gamerules.h" #endif #define CONTROL_POINT_UNLOCK_THINK "UnlockThink" @@ -269,6 +270,7 @@ void CTeamControlPoint::Precache( void ) #ifdef TF_DLL PrecacheScriptSound( "Announcer.ControlPointContested" ); + PrecacheScriptSound( "Announcer.ControlPointContested_Neutral" ); #endif } @@ -653,7 +655,15 @@ void CTeamControlPoint::InternalSetOwner( int iCapTeam, bool bMakeSound, int iNu Assert( playerIndex > 0 && playerIndex <= gpGlobals->maxClients ); - PlayerCapped( ToBaseMultiplayerPlayer(UTIL_PlayerByIndex( playerIndex )) ); + CBaseMultiplayerPlayer *pPlayer = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( playerIndex ) ); + PlayerCapped( pPlayer ); + +#ifdef TF_DLL + if ( TFGameRules() && TFGameRules()->IsHolidayActive( kHoliday_EOTL ) ) + { + TFGameRules()->DropBonusDuck( pPlayer->GetAbsOrigin(), ToTFPlayer( pPlayer ), NULL, NULL, false, true ); + } +#endif } // Remap team to get first game team = 1 @@ -733,7 +743,7 @@ void CTeamControlPoint::SendCapString( int iCapTeam, int iNumCappingPlayers, int //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -void CTeamControlPoint::CaptureBlocked( CBaseMultiplayerPlayer *pPlayer ) +void CTeamControlPoint::CaptureBlocked( CBaseMultiplayerPlayer *pPlayer, CBaseMultiplayerPlayer *pVictim ) { if( strlen( STRING(m_iszPrintName) ) <= 0 ) return; @@ -746,6 +756,10 @@ void CTeamControlPoint::CaptureBlocked( CBaseMultiplayerPlayer *pPlayer ) event->SetString( "cpname", STRING(m_iszPrintName) ); event->SetInt( "blocker", pPlayer->entindex() ); event->SetInt( "priority", 9 ); + if ( pVictim ) + { + event->SetInt( "victim", pVictim->entindex() ); + } gameeventmanager->FireEvent( event ); } diff --git a/mp/src/game/server/team_control_point.h b/mp/src/game/server/team_control_point.h index 816a3ac1..bfcfe100 100644 --- a/mp/src/game/server/team_control_point.h +++ b/mp/src/game/server/team_control_point.h @@ -82,7 +82,7 @@ public: void SetCappersRequiredForTeam( int iGameTeam, int iCappers ); - void CaptureBlocked( CBaseMultiplayerPlayer *pPlayer ); + void CaptureBlocked( CBaseMultiplayerPlayer *pPlayer, CBaseMultiplayerPlayer *pVictim ); int PointValue( void ); diff --git a/mp/src/game/server/team_control_point_master.cpp b/mp/src/game/server/team_control_point_master.cpp index 956f7ddb..9b007e06 100644 --- a/mp/src/game/server/team_control_point_master.cpp +++ b/mp/src/game/server/team_control_point_master.cpp @@ -1056,65 +1056,44 @@ bool CTeamControlPointMaster::IsBaseControlPoint( int iPointIndex ) int CTeamControlPointMaster::GetBaseControlPoint( int iTeam ) { int iRetVal = -1; - int nLowestValue = 999, nHighestValue = -1; - int iLowestIndex = 0, iHighestIndex = 0; + int nLowestValue = 999; + int nHighestValue = -1; + CTeamControlPoint *pLowestPoint = NULL; + CTeamControlPoint *pHighestPoint = NULL; - for( int i = 0 ; i < (int)m_ControlPoints.Count() ; i++ ) + for( unsigned int i = 0 ; i < m_ControlPoints.Count() ; i++ ) { CTeamControlPoint *pPoint = m_ControlPoints[i]; - int iPointIndex = m_ControlPoints[i]->GetPointIndex(); - - if ( PlayingMiniRounds() && iTeam > LAST_SHARED_TEAM ) + if ( !PlayingMiniRounds() || ( IsInRound( pPoint ) && ( iTeam > LAST_SHARED_TEAM ) ) ) { - if ( IsInRound( pPoint ) ) // is this point in the current round? - { - if ( iPointIndex > nHighestValue ) - { - nHighestValue = iPointIndex; - iHighestIndex = i; - } + int nTempValue = pPoint->GetPointIndex(); - if ( iPointIndex < nLowestValue ) - { - nLowestValue = iPointIndex; - iLowestIndex = i; - } - } - } - else - { - if ( pPoint->GetDefaultOwner() != iTeam ) + if ( nTempValue > nHighestValue ) { - continue; + nHighestValue = nTempValue; + pHighestPoint = pPoint; } - // If it's the first or the last point, it's their base - if ( iPointIndex == 0 || iPointIndex == (((int)m_ControlPoints.Count())-1) ) + if ( nTempValue < nLowestValue ) { - iRetVal = iPointIndex; - break; + nLowestValue = nTempValue; + pLowestPoint = pPoint; } } } - if ( PlayingMiniRounds() && iTeam > LAST_SHARED_TEAM ) + if ( pLowestPoint && pHighestPoint ) { - if ( nLowestValue != 999 && nHighestValue != -1 ) + // which point is owned by this team? + if ( ( pLowestPoint->GetDefaultOwner() == iTeam && pHighestPoint->GetDefaultOwner() == iTeam ) || // if the same team owns both, take the highest value to be the last point + ( pHighestPoint->GetDefaultOwner() == iTeam ) ) { - CTeamControlPoint *pLowestPoint = m_ControlPoints[iLowestIndex]; - CTeamControlPoint *pHighestPoint = m_ControlPoints[iHighestIndex]; - - // which point is owned by this team? - if ( ( pLowestPoint->GetDefaultOwner() == iTeam && pHighestPoint->GetDefaultOwner() == iTeam ) || // if the same team owns both, take the highest value to be the last point - ( pHighestPoint->GetDefaultOwner() == iTeam ) ) - { - iRetVal = nHighestValue; - } - else if ( pLowestPoint->GetDefaultOwner() == iTeam ) - { - iRetVal = nLowestValue; - } + iRetVal = nHighestValue; + } + else if ( pLowestPoint->GetDefaultOwner() == iTeam ) + { + iRetVal = nLowestValue; } } diff --git a/mp/src/game/server/team_control_point_master.h b/mp/src/game/server/team_control_point_master.h index 0c229a05..465a67dd 100644 --- a/mp/src/game/server/team_control_point_master.h +++ b/mp/src/game/server/team_control_point_master.h @@ -132,6 +132,7 @@ public: float GetLastOwnershipChangeTime( void ) { return m_flLastOwnershipChangeTime; } int GetCurrentRoundIndex() { return m_iCurrentRoundIndex; } + bool ShouldSwitchTeamsOnRoundWin( void ) { return m_bSwitchTeamsOnWin; } private: void EXPORT CPMThink( void ); diff --git a/mp/src/game/server/trigger_area_capture.cpp b/mp/src/game/server/trigger_area_capture.cpp index 648a3b86..979ef4bc 100644 --- a/mp/src/game/server/trigger_area_capture.cpp +++ b/mp/src/game/server/trigger_area_capture.cpp @@ -535,7 +535,7 @@ void CTriggerAreaCapture::CaptureThink( void ) if ( !bRepeatBlocker ) { - m_hPoint->CaptureBlocked( pBlockingPlayer ); + m_hPoint->CaptureBlocked( pBlockingPlayer, NULL ); // Add this guy to our blocker list int iNew = m_Blockers.AddToTail(); @@ -882,6 +882,12 @@ void CTriggerAreaCapture::EndCapture( int team ) m_nCapturingTeam = TEAM_UNASSIGNED; SetCapTimeRemaining( 0 ); + // play any special cap sounds. need to do this before we update the owner of the point. + if ( TeamplayRoundBasedRules() ) + { + TeamplayRoundBasedRules()->PlaySpecialCapSounds( m_nOwningTeam, m_hPoint.Get() ); + } + //there may have been more than one capper, but only report this one. //he hasn't 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. @@ -912,12 +918,6 @@ void CTriggerAreaCapture::EndCapture( int team ) } } } - - // play any special cap sounds - if ( TeamplayRoundBasedRules() ) - { - TeamplayRoundBasedRules()->PlaySpecialCapSounds( m_nOwningTeam ); - } } //----------------------------------------------------------------------------- @@ -1140,7 +1140,7 @@ bool CTriggerAreaCapture::CheckIfDeathCausesBlock( CBaseMultiplayerPlayer *pVict if ( bBreakCap ) { - m_hPoint->CaptureBlocked( pKiller ); + m_hPoint->CaptureBlocked( pKiller, pVictim ); //BreakCapture( true ); } diff --git a/mp/src/game/server/triggers.cpp b/mp/src/game/server/triggers.cpp index 742976fb..1e317567 100644 --- a/mp/src/game/server/triggers.cpp +++ b/mp/src/game/server/triggers.cpp @@ -475,6 +475,7 @@ void CBaseTrigger::StartTouch(CBaseEntity *pOther) { // First entity to touch us that passes our filters m_OnStartTouchAll.FireOutput( pOther, this ); + StartTouchAll(); } } } @@ -514,7 +515,10 @@ void CBaseTrigger::EndTouch(CBaseEntity *pOther) else if ( hOther->IsPlayer() && !hOther->IsAlive() ) { #ifdef STAGING_ONLY - AssertMsg( 0, CFmtStr( "Dead player [%s] is still touching this trigger at [%f %f %f]", hOther->GetEntityName().ToCStr(), XYZ( hOther->GetAbsOrigin() ) ) ); + if ( !HushAsserts() ) + { + AssertMsg( false, "Dead player [%s] is still touching this trigger at [%f %f %f]", hOther->GetEntityName().ToCStr(), XYZ( hOther->GetAbsOrigin() ) ); + } Warning( "Dead player [%s] is still touching this trigger at [%f %f %f]", hOther->GetEntityName().ToCStr(), XYZ( hOther->GetAbsOrigin() ) ); #endif m_hTouchingEntities.Remove( i ); @@ -530,6 +534,7 @@ void CBaseTrigger::EndTouch(CBaseEntity *pOther) if ( !bFoundOtherTouchee /*&& !m_bDisabled*/ ) { m_OnEndTouchAll.FireOutput(pOther, this); + EndTouchAll(); } } } @@ -634,8 +639,8 @@ void CTriggerRemove::Touch( CBaseEntity *pOther ) BEGIN_DATADESC( CTriggerHurt ) // Function Pointers - DEFINE_FUNCTION( RadiationThink ), - DEFINE_FUNCTION( HurtThink ), + DEFINE_FUNCTION( CTriggerHurtShim::RadiationThinkShim ), + DEFINE_FUNCTION( CTriggerHurtShim::HurtThinkShim ), // Fields DEFINE_FIELD( m_flOriginalDamage, FIELD_FLOAT ), @@ -661,6 +666,7 @@ END_DATADESC() LINK_ENTITY_TO_CLASS( trigger_hurt, CTriggerHurt ); +IMPLEMENT_AUTO_LIST( ITriggerHurtAutoList ); //----------------------------------------------------------------------------- // Purpose: Called when spawning, after keyvalues have been handled. @@ -677,7 +683,7 @@ void CTriggerHurt::Spawn( void ) SetThink( NULL ); if (m_bitsDamageInflict & DMG_RADIATION) { - SetThink ( &CTriggerHurt::RadiationThink ); + SetThink ( &CTriggerHurtShim::RadiationThinkShim ); SetNextThink( gpGlobals->curtime + random->RandomFloat(0.0, 0.5) ); } } @@ -723,6 +729,15 @@ bool CTriggerHurt::HurtEntity( CBaseEntity *pOther, float damage ) if ( !pOther->m_takedamage || !PassesTriggerFilters(pOther) ) return false; + // If player is disconnected, we're probably in this routine via the + // PhysicsRemoveTouchedList() function to make sure all Untouch()'s are called for the + // player. Calling TakeDamage() in this case can get into the speaking criteria, which + // will then loop through the control points and the touched list again. We shouldn't + // need to hurt players that are disconnected, so skip all of this... + bool bPlayerDisconnected = pOther->IsPlayer() && ( ((CBasePlayer *)pOther)->IsConnected() == false ); + if ( bPlayerDisconnected ) + return false; + if ( damage < 0 ) { pOther->TakeHealth( -damage, m_bitsDamageInflict ); @@ -862,11 +877,29 @@ void CTriggerHurt::Touch( CBaseEntity *pOther ) { if ( m_pfnThink == NULL ) { - SetThink( &CTriggerHurt::HurtThink ); + SetThink( &CTriggerHurtShim::HurtThinkShim ); SetNextThink( gpGlobals->curtime ); } } +//----------------------------------------------------------------------------- +// Purpose: Checks if this point is in any trigger_hurt zones with positive damage +//----------------------------------------------------------------------------- +bool IsTakingTriggerHurtDamageAtPoint( const Vector &vecPoint ) +{ + for ( int i = 0; i < ITriggerHurtAutoList::AutoList().Count(); i++ ) + { + // Some maps use trigger_hurt with negative values as healing triggers; don't consider those + CTriggerHurt *pTrigger = static_cast<CTriggerHurt*>( ITriggerHurtAutoList::AutoList()[i] ); + if ( !pTrigger->m_bDisabled && pTrigger->PointIsWithin( vecPoint ) && pTrigger->m_flDamage > 0.f ) + { + return true; + } + } + + return false; +} + // ################################################################################## // >> TriggerMultiple @@ -2310,8 +2343,8 @@ class CTriggerTeleport : public CBaseTrigger public: DECLARE_CLASS( CTriggerTeleport, CBaseTrigger ); - void Spawn( void ); - void Touch( CBaseEntity *pOther ); + virtual void Spawn( void ) OVERRIDE; + virtual void Touch( CBaseEntity *pOther ) OVERRIDE; string_t m_iLandmark; @@ -2326,14 +2359,11 @@ BEGIN_DATADESC( CTriggerTeleport ) END_DATADESC() - - void CTriggerTeleport::Spawn( void ) { InitTrigger(); } - //----------------------------------------------------------------------------- // Purpose: Teleports the entity that touched us to the location of our target, // setting the toucher's angles to our target's angles if they are a @@ -2416,6 +2446,46 @@ LINK_ENTITY_TO_CLASS( info_teleport_destination, CPointEntity ); //----------------------------------------------------------------------------- +// Teleport Relative trigger +//----------------------------------------------------------------------------- +class CTriggerTeleportRelative : public CBaseTrigger +{ +public: + DECLARE_CLASS(CTriggerTeleportRelative, CBaseTrigger); + + virtual void Spawn( void ) OVERRIDE; + virtual void Touch( CBaseEntity *pOther ) OVERRIDE; + + Vector m_TeleportOffset; + + DECLARE_DATADESC(); +}; + +LINK_ENTITY_TO_CLASS( trigger_teleport_relative, CTriggerTeleportRelative ); +BEGIN_DATADESC( CTriggerTeleportRelative ) + DEFINE_KEYFIELD( m_TeleportOffset, FIELD_VECTOR, "teleportoffset" ) +END_DATADESC() + + +void CTriggerTeleportRelative::Spawn( void ) +{ + InitTrigger(); +} + +void CTriggerTeleportRelative::Touch( CBaseEntity *pOther ) +{ + if ( !PassesTriggerFilters(pOther) ) + { + return; + } + + const Vector finalPos = m_TeleportOffset + WorldSpaceCenter(); + const Vector *momentum = &vec3_origin; + + pOther->Teleport( &finalPos, NULL, momentum ); +} + +//----------------------------------------------------------------------------- // Purpose: Saves the game when the player touches the trigger. Can be enabled or disabled //----------------------------------------------------------------------------- class CTriggerToggleSave : public CBaseTrigger @@ -4822,6 +4892,78 @@ void CServerRagdollTrigger::EndTouch(CBaseEntity *pOther) } } + +//----------------------------------------------------------------------------- +// Purpose: A trigger that adds impulse to touching entities +//----------------------------------------------------------------------------- +class CTriggerApplyImpulse : public CBaseTrigger +{ +public: + DECLARE_CLASS( CTriggerApplyImpulse, CBaseTrigger ); + DECLARE_DATADESC(); + + CTriggerApplyImpulse(); + + void Spawn( void ); + + void InputApplyImpulse( inputdata_t& ); + +private: + Vector m_vecImpulseDir; + float m_flForce; +}; + + +BEGIN_DATADESC( CTriggerApplyImpulse ) + DEFINE_KEYFIELD( m_vecImpulseDir, FIELD_VECTOR, "impulse_dir" ), + DEFINE_KEYFIELD( m_flForce, FIELD_FLOAT, "force" ), + DEFINE_INPUTFUNC( FIELD_VOID, "ApplyImpulse", InputApplyImpulse ), +END_DATADESC() + + +LINK_ENTITY_TO_CLASS( trigger_apply_impulse, CTriggerApplyImpulse ); + + +CTriggerApplyImpulse::CTriggerApplyImpulse() +{ + m_flForce = 300.f; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTriggerApplyImpulse::Spawn() +{ + // Convert pushdir from angles to a vector + Vector vecAbsDir; + QAngle angPushDir = QAngle(m_vecImpulseDir.x, m_vecImpulseDir.y, m_vecImpulseDir.z); + AngleVectors(angPushDir, &vecAbsDir); + + // Transform the vector into entity space + VectorIRotate( vecAbsDir, EntityToWorldTransform(), m_vecImpulseDir ); + + BaseClass::Spawn(); + + InitTrigger(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CTriggerApplyImpulse::InputApplyImpulse( inputdata_t& ) +{ + Vector vecImpulse = m_flForce * m_vecImpulseDir; + FOR_EACH_VEC( m_hTouchingEntities, i ) + { + if ( m_hTouchingEntities[i] ) + { + m_hTouchingEntities[i]->ApplyAbsVelocityImpulse( vecImpulse ); + } + } +} + #ifdef HL1_DLL //---------------------------------------------------------------------------------- // func_friction diff --git a/mp/src/game/server/triggers.h b/mp/src/game/server/triggers.h index 7695d08c..6ae7312f 100644 --- a/mp/src/game/server/triggers.h +++ b/mp/src/game/server/triggers.h @@ -68,6 +68,8 @@ public: virtual bool PassesTriggerFilters(CBaseEntity *pOther); virtual void StartTouch(CBaseEntity *pOther); virtual void EndTouch(CBaseEntity *pOther); + virtual void StartTouchAll() {} + virtual void EndTouchAll() {} bool IsTouching( CBaseEntity *pOther ); CBaseEntity *GetTouchedEntityOfType( const char *sClassName ); @@ -164,7 +166,21 @@ protected: // Purpose: Hurts anything that touches it. If the trigger has a targetname, // firing it will toggle state. //----------------------------------------------------------------------------- -class CTriggerHurt : public CBaseTrigger + +// This class is to get around the fact that DEFINE_FUNCTION doesn't like multiple inheritance +class CTriggerHurtShim : public CBaseTrigger +{ + virtual void RadiationThink( void ) = 0; + virtual void HurtThink( void ) = 0; + +public: + + void RadiationThinkShim( void ){ RadiationThink(); } + void HurtThinkShim( void ){ HurtThink(); } +}; + +DECLARE_AUTO_LIST( ITriggerHurtAutoList ); +class CTriggerHurt : public CTriggerHurtShim, public ITriggerHurtAutoList { public: CTriggerHurt() @@ -173,7 +189,7 @@ public: m_flDamageCap = 20.0f; } - DECLARE_CLASS( CTriggerHurt, CBaseTrigger ); + DECLARE_CLASS( CTriggerHurt, CTriggerHurtShim ); void Spawn( void ); void RadiationThink( void ); @@ -207,4 +223,6 @@ public: CUtlVector<EHANDLE> m_hurtEntities; }; +bool IsTakingTriggerHurtDamageAtPoint( const Vector &vecPoint ); + #endif // TRIGGERS_H diff --git a/mp/src/game/server/util.cpp b/mp/src/game/server/util.cpp index 8cd8f989..4b2008df 100644 --- a/mp/src/game/server/util.cpp +++ b/mp/src/game/server/util.cpp @@ -59,7 +59,7 @@ void DBG_AssertFunction( bool fExpr, const char *szExpr, const char *szFile, int Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n%s", szExpr, szFile, szLine, szMessage); else Q_snprintf(szOut,sizeof(szOut), "ASSERT FAILED:\n %s \n(%s@%d)\n", szExpr, szFile, szLine); - Warning( szOut); + Warning( "%s", szOut); } #endif // DEBUG @@ -161,6 +161,11 @@ IServerNetworkable *CEntityFactoryDictionary::Create( const char *pClassName ) IEntityFactory *pFactory = FindFactory( pClassName ); if ( !pFactory ) { +#ifdef STAGING_ONLY + static ConVarRef tf_bot_use_items( "tf_bot_use_items" ); + if ( tf_bot_use_items.IsValid() && tf_bot_use_items.GetInt() ) + return NULL; +#endif Warning("Attempted to create unknown entity type %s!\n", pClassName ); return NULL; } @@ -568,6 +573,24 @@ CBasePlayer *UTIL_PlayerByIndex( int playerIndex ) return pPlayer; } +CBasePlayer *UTIL_PlayerBySteamID( const CSteamID &steamID ) +{ + CSteamID steamIDPlayer; + for ( int i = 1; i <= gpGlobals->maxClients; i++ ) + { + CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); + if ( !pPlayer ) + continue; + + if ( !pPlayer->GetSteamID( &steamIDPlayer ) ) + continue; + + if ( steamIDPlayer == steamID ) + return pPlayer; + } + return NULL; +} + CBasePlayer* UTIL_PlayerByName( const char *name ) { if ( !name || !name[0] ) diff --git a/mp/src/game/server/util.h b/mp/src/game/server/util.h index 92c341d8..c06cbfe9 100644 --- a/mp/src/game/server/util.h +++ b/mp/src/game/server/util.h @@ -222,6 +222,7 @@ float UTIL_GetSimulationInterval(); // NOTENOTE: Use UTIL_GetLocalPlayer instead of UTIL_PlayerByIndex IF you're in single player // and you want the player. CBasePlayer *UTIL_PlayerByIndex( int playerIndex ); +CBasePlayer *UTIL_PlayerBySteamID( const CSteamID &steamID ); // NOTENOTE: Use this instead of UTIL_PlayerByIndex IF you're in single player // and you want the player. diff --git a/mp/src/game/server/vote_controller.cpp b/mp/src/game/server/vote_controller.cpp index 7dab54bb..a29b449f 100644 --- a/mp/src/game/server/vote_controller.cpp +++ b/mp/src/game/server/vote_controller.cpp @@ -14,6 +14,7 @@ #ifdef TF_DLL #include "tf/tf_gamerules.h" +#include "tf/tf_voteissues.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -38,15 +39,15 @@ LINK_ENTITY_TO_CLASS( vote_controller, CVoteController ); CVoteController *g_voteController = NULL; -ConVar sv_vote_timer_duration("sv_vote_timer_duration", "15", FCVAR_DEVELOPMENTONLY, "How long to allow voting on an issue"); -ConVar sv_vote_command_delay("sv_vote_command_delay", "2", FCVAR_DEVELOPMENTONLY, "How long after a vote passes until the action happens", false, 0, true, 4.5); -ConVar sv_allow_votes("sv_allow_votes", "1", FCVAR_NONE, "Allow voting?"); +ConVar sv_vote_timer_duration( "sv_vote_timer_duration", "15", FCVAR_DEVELOPMENTONLY, "How long to allow voting on an issue" ); +ConVar sv_vote_command_delay( "sv_vote_command_delay", "2", FCVAR_DEVELOPMENTONLY, "How long after a vote passes until the action happens", false, 0.f, true, 4.5f ); +ConVar sv_allow_votes( "sv_allow_votes", "1", FCVAR_NONE, "Allow voting?" ); ConVar sv_vote_failure_timer( "sv_vote_failure_timer", "300", FCVAR_NONE, "A vote that fails cannot be re-submitted for this long" ); #ifdef TF_DLL ConVar sv_vote_failure_timer_mvm( "sv_vote_failure_timer_mvm", "120", FCVAR_NONE, "A vote that fails in MvM cannot be re-submitted for this long" ); #endif // TF_DLL -ConVar sv_vote_creation_timer("sv_vote_creation_timer", "120", FCVAR_DEVELOPMENTONLY, "How often someone can individually call a vote."); -ConVar sv_vote_quorum_ratio( "sv_vote_quorum_ratio", "0.6", FCVAR_HIDDEN, "The minimum ratio of players needed to vote on an issue to resolve it.", true, 0.1, true, 1.0 ); +ConVar sv_vote_creation_timer( "sv_vote_creation_timer", "150", FCVAR_NONE, "How long before a player can attempt to call another vote (in seconds)." ); +ConVar sv_vote_quorum_ratio( "sv_vote_quorum_ratio", "0.6", FCVAR_NOTIFY, "The minimum ratio of eligible players needed to pass a vote. Min 0.5, Max 1.0.", true, 0.1f, true, 1.0f ); ConVar sv_vote_allow_spectators( "sv_vote_allow_spectators", "0", FCVAR_NONE, "Allow spectators to vote?" ); ConVar sv_vote_ui_hide_disabled_issues( "sv_vote_ui_hide_disabled_issues", "1", FCVAR_NONE, "Suppress listing of disabled issues in the vote setup screen." ); @@ -61,7 +62,9 @@ public: CVoteControllerSystem( char const *name ) : CAutoGameSystemPerFrame( name ) { SetDefLessFunc( m_mapKickWatchList ); + SetDefLessFunc( m_mapNameLockedList ); m_flNextKickCheckTime = 0.f; + m_flNextNameLockCheckTime = 0.f; } virtual void LevelInitPreEntity() @@ -93,38 +96,54 @@ public: break; // Constantly called code - resume on next pass } - CBasePlayer *pTarget = NULL; - CSteamID steamIDPlayer; - for ( int j = 1; j <= gpGlobals->maxClients; j++ ) + CBasePlayer *pTarget = UTIL_PlayerBySteamID( m_mapKickWatchList.Key( i ) ); + if ( pTarget ) { - CBasePlayer *pPlayer = UTIL_PlayerByIndex( j ); - if ( !pPlayer ) - continue; + // Welcome back + engine->ServerCommand( CFmtStr( "kickid %d %s;", pTarget->GetUserID(), "Kicked by server." ) ); + } + } - if ( pPlayer->GetSteamID( &steamIDPlayer ) == false ) - continue; + m_flNextKickCheckTime = gpGlobals->curtime + 0.2f; + } - if ( steamIDPlayer == m_mapKickWatchList.Key( i ) ) + // Name lock management + if ( m_flNextNameLockCheckTime < gpGlobals->curtime ) + { + FOR_EACH_MAP( m_mapNameLockedList, i ) + { + CBasePlayer *pPlayer = UTIL_PlayerBySteamID( m_mapNameLockedList.Key( i ) ); + + // Time up? + if ( gpGlobals->curtime > m_mapNameLockedList[i] ) + { + // Disable the lock if they're still here + if ( pPlayer ) { - pTarget = pPlayer; - break; + engine->ServerCommand( UTIL_VarArgs( "namelockid %d %d\n", pPlayer->GetUserID(), 0 ) ); } - } - if ( pTarget ) + // Remove and break - this will re-run in 1 second + m_mapNameLockedList.RemoveAt( i ); + break; + } + // See if they reconnected + else if ( pPlayer && !engine->IsPlayerNameLocked( pPlayer->edict() ) ) { - // Welcome back - engine->ServerCommand( CFmtStr( "kickid %d %s;", pTarget->GetUserID(), "Kicked by server." ) ); + engine->ServerCommand( UTIL_VarArgs( "namelockid %d %d\n", pPlayer->GetUserID(), 1 ) ); } } - m_flNextKickCheckTime = gpGlobals->curtime + 0.2f; + m_flNextNameLockCheckTime = gpGlobals->curtime + 1.f; } } } void AddPlayerToKickWatchList( CSteamID steamID, float flDuration ) { + if ( !steamID.IsValid() || !steamID.BIndividualAccount() ) + return; + flDuration = clamp( flDuration, 1.f, (float)k_nKickWatchListMaxDuration ); if ( m_mapKickWatchList.Find( steamID ) == m_mapKickWatchList.InvalidIndex() ) { @@ -132,10 +151,24 @@ public: } } + void AddPlayerToNameLockedList( CSteamID steamID, float flDuration ) + { + if ( !steamID.IsValid() || !steamID.BIndividualAccount() ) + return; + + flDuration = clamp( flDuration, 1.f, (float)k_nKickWatchListMaxDuration ); + if ( m_mapNameLockedList.Find( steamID ) == m_mapNameLockedList.InvalidIndex() ) + { + m_mapNameLockedList.Insert( steamID, ( gpGlobals->curtime + flDuration ) ); + } + } + private: CUtlMap< CSteamID, float > m_mapKickWatchList; + CUtlMap< CSteamID, float > m_mapNameLockedList; float m_flNextKickCheckTime; + float m_flNextNameLockCheckTime; }; CVoteControllerSystem VoteControllerSystem( "CVoteControllerSystem" ); @@ -185,7 +218,7 @@ CON_COMMAND( callvote, "Start a vote on an issue." ) } CBasePlayer *pVoteCaller = UTIL_GetCommandClient(); - if( !pVoteCaller ) + if ( !pVoteCaller ) return; if ( !sv_vote_allow_spectators.GetBool() ) @@ -197,15 +230,21 @@ CON_COMMAND( callvote, "Start a vote on an issue." ) } } - // Prevent spamming commands -#ifndef _DEBUG + if ( g_voteController->IsVoteActive() ) + { + ClientPrint( pVoteCaller, HUD_PRINTCENTER, "#GameUI_vote_failed_vote_in_progress" ); + return; + } + + // Ask the controller if this is allowed int nCooldown = 0; - if ( !g_voteController->CanEntityCallVote( pVoteCaller, nCooldown ) ) + vote_create_failed_t nError = VOTE_FAILED_GENERIC; + + if ( !g_voteController->CanEntityCallVote( pVoteCaller, nCooldown, nError ) ) { - g_voteController->SendVoteCreationFailedMessage( VOTE_FAILED_RATE_EXCEEDED, pVoteCaller, nCooldown ); + g_voteController->SendVoteCreationFailedMessage( nError, pVoteCaller, nCooldown ); return; } -#endif // Parameters char szEmptyDetails[MAX_VOTE_DETAILS_LENGTH]; @@ -214,7 +253,7 @@ CON_COMMAND( callvote, "Start a vote on an issue." ) const char *arg3 = args.ArgC() >= 3 ? args[2] : szEmptyDetails; // If we don't have any arguments, invoke VoteSetup UI - if( args.ArgC() < 2 ) + if ( args.ArgC() < 2 ) { g_voteController->SetupVote( pVoteCaller->entindex() ); return; @@ -252,7 +291,7 @@ void CVoteController::ResetData( void ) m_acceptingVotesTimer.Invalidate(); m_executeCommandTimer.Invalidate(); m_iEntityHoldingVote = -1; - m_iOnlyTeamToVote = TEAM_INVALID; + m_iOnlyTeamToVote = TEAM_UNASSIGNED; m_bIsYesNoVote = true; for( int voteIndex = 0; voteIndex < ARRAYSIZE( m_nVotesCast ); ++voteIndex ) @@ -290,9 +329,22 @@ int CVoteController::UpdateTransmitState( void ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- +bool CVoteController::IsVoteSystemEnabled( void ) +{ +#ifdef TF_DLL + if ( TFGameRules() && TFGameRules()->IsCompetitiveMode() ) + return false; +#endif // TF_DLL + + return sv_allow_votes.GetBool(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- bool CVoteController::CanTeamCastVote( int iTeam ) const { - if ( m_iOnlyTeamToVote == TEAM_INVALID ) + if ( m_iOnlyTeamToVote == TEAM_UNASSIGNED ) return true; return iTeam == m_iOnlyTeamToVote; @@ -310,7 +362,7 @@ bool CVoteController::SetupVote( int iEntIndex ) int nIssueCount = 0; // Passing an nIssueCount of 0 triggers a "Voting disabled on server" message in the setup UI - if ( sv_allow_votes.GetBool() ) + if ( IsVoteSystemEnabled() ) { for( int iIndex = 0; iIndex < m_potentialIssues.Count(); ++iIndex ) { @@ -330,28 +382,28 @@ bool CVoteController::SetupVote( int iEntIndex ) filter.MakeReliable(); UserMessageBegin( filter, "VoteSetup" ); WRITE_BYTE( nIssueCount ); + int nMsgSize = 0; for( int iIndex = 0; iIndex < m_potentialIssues.Count(); ++iIndex ) { CBaseIssue *pCurrentIssue = m_potentialIssues[iIndex]; if ( pCurrentIssue ) { - if ( pCurrentIssue->IsEnabled() ) - { - WRITE_STRING( pCurrentIssue->GetTypeString() ); - } - else - { - // Don't send/display disabled issues when set - if ( sv_vote_ui_hide_disabled_issues.GetBool() ) - continue; - - char szDisabledIssueStr[MAX_COMMAND_LENGTH + 12]; - V_strcpy( szDisabledIssueStr, pCurrentIssue->GetTypeString() ); - V_strcat( szDisabledIssueStr, " (Disabled on Server)", sizeof(szDisabledIssueStr) ); - - WRITE_STRING( szDisabledIssueStr ); - } + // Don't send/display disabled issues when set + if ( !pCurrentIssue->IsEnabled() && sv_vote_ui_hide_disabled_issues.GetBool() ) + continue; + + // Don't exceed MAX_USER_MSG_DATA (hack) + nMsgSize += ( V_strlen( pCurrentIssue->GetTypeString() ) + 1 ); + nMsgSize += ( V_strlen( pCurrentIssue->GetTypeStringLocalized() ) + 1 ); + ++nMsgSize; + Assert( nMsgSize <= MAX_USER_MSG_DATA ); + if ( nMsgSize > MAX_USER_MSG_DATA ) + continue; + + WRITE_STRING( pCurrentIssue->GetTypeString() ); + WRITE_STRING( pCurrentIssue->GetTypeStringLocalized() ); + WRITE_BYTE( pCurrentIssue->IsEnabled() ); } } @@ -368,29 +420,29 @@ bool CVoteController::CreateVote( int iEntIndex, const char *pszTypeString, cons // Terrible Hack: Dedicated servers pass 99 as the EntIndex bool bDedicatedServer = ( iEntIndex == DEDICATED_SERVER ) ? true : false; - if( !sv_allow_votes.GetBool() ) + if ( !IsVoteSystemEnabled() ) return false; // Already running a vote? - if( IsVoteActive() ) + if ( IsVoteActive() ) return false; CBasePlayer *pVoteCaller = UTIL_PlayerByIndex( iEntIndex ); - if( !pVoteCaller && !bDedicatedServer ) + if ( !pVoteCaller && !bDedicatedServer ) return false; // Find the issue the user is asking for - for( int issueIndex = 0; issueIndex < m_potentialIssues.Count(); ++issueIndex ) + for ( int issueIndex = 0; issueIndex < m_potentialIssues.Count(); ++issueIndex ) { CBaseIssue *pCurrentIssue = m_potentialIssues[issueIndex]; if ( !pCurrentIssue ) return false; - if( FStrEq( pszTypeString, pCurrentIssue->GetTypeString() ) ) + if ( FStrEq( pszTypeString, pCurrentIssue->GetTypeString() ) ) { vote_create_failed_t nErrorCode = VOTE_FAILED_GENERIC; int nTime = 0; - if( pCurrentIssue->CanCallVote( iEntIndex, pszDetailString, nErrorCode, nTime ) ) + if ( pCurrentIssue->CanCallVote( iEntIndex, pszDetailString, nErrorCode, nTime ) ) { // Establish a bunch of data on this particular issue pCurrentIssue->SetIssueDetails( pszDetailString ); @@ -399,14 +451,7 @@ bool CVoteController::CreateVote( int iEntIndex, const char *pszTypeString, cons m_iEntityHoldingVote = iEntIndex; if ( !bDedicatedServer ) { - if( pCurrentIssue->IsAllyRestrictedVote() ) - { - m_iOnlyTeamToVote = GetVoterTeam( pVoteCaller ); - } - else - { - m_iOnlyTeamToVote = TEAM_INVALID; - } + m_iOnlyTeamToVote = ( pCurrentIssue->IsTeamRestrictedVote() ) ? GetVoterTeam( pVoteCaller ) : TEAM_UNASSIGNED; } // Now get our choices @@ -442,10 +487,10 @@ bool CVoteController::CreateVote( int iEntIndex, const char *pszTypeString, cons // Now the vote handling and UI m_nPotentialVotes = pCurrentIssue->CountPotentialVoters(); - m_acceptingVotesTimer.Start( sv_vote_timer_duration.GetFloat() ); + m_acceptingVotesTimer.Start( sv_vote_timer_duration.GetFloat() + random->RandomFloat( -1.f, 1.f ) ); // Force the vote holder to agree with a Yes/No vote - if ( m_bIsYesNoVote && !bDedicatedServer ) + if ( pCurrentIssue->IsYesNoVote() && !bDedicatedServer ) { TryCastVote( iEntIndex, "Option1" ); } @@ -458,7 +503,7 @@ bool CVoteController::CreateVote( int iEntIndex, const char *pszTypeString, cons WRITE_BYTE( m_iEntityHoldingVote ); WRITE_STRING( pCurrentIssue->GetDisplayString() ); WRITE_STRING( pCurrentIssue->GetDetailsString() ); - WRITE_BOOL( m_bIsYesNoVote ); + WRITE_BOOL( pCurrentIssue->IsYesNoVote() ); MessageEnd(); if ( !bDedicatedServer ) @@ -506,11 +551,7 @@ void CVoteController::SendVoteFailedToPassMessage( vote_create_failed_t nReason { Assert( m_potentialIssues[m_iActiveIssueIndex] ); - // See if we have a player target. - CBasePlayer *pVoteTarget = m_potentialIssues[m_iActiveIssueIndex]->m_hPlayerTarget; - bool bFakeClient = ( pVoteTarget && ( pVoteTarget->IsFakeClient() || pVoteTarget->IsHLTV() || pVoteTarget->IsReplay() ) ); - - UTIL_LogPrintf( "Vote failed \"%s %s\" with code %i (proxy: %i) \n", m_potentialIssues[m_iActiveIssueIndex]->GetTypeString(), m_potentialIssues[m_iActiveIssueIndex]->GetDetailsString(), (int)nReason, bFakeClient ); + UTIL_LogPrintf( "Vote failed \"%s %s\" with code %i\n", m_potentialIssues[m_iActiveIssueIndex]->GetTypeString(), m_potentialIssues[m_iActiveIssueIndex]->GetDetailsString(), (int)nReason ); CBroadcastRecipientFilter filter; filter.MakeReliable(); @@ -526,24 +567,24 @@ void CVoteController::SendVoteFailedToPassMessage( vote_create_failed_t nReason //----------------------------------------------------------------------------- CVoteController::TryCastVoteResult CVoteController::TryCastVote( int iEntIndex, const char *pszVoteString ) { - if( !sv_allow_votes.GetBool() ) + if ( !IsVoteSystemEnabled() ) return CAST_FAIL_SERVER_DISABLE; - if( iEntIndex >= ARRAYSIZE( m_nVotesCast ) ) + if ( iEntIndex >= ARRAYSIZE( m_nVotesCast ) ) return CAST_FAIL_SYSTEM_ERROR; - if( !IsVoteActive() ) + if ( !IsVoteActive() ) return CAST_FAIL_NO_ACTIVE_ISSUE; - if( m_executeCommandTimer.HasStarted() ) + if ( m_executeCommandTimer.HasStarted() ) return CAST_FAIL_VOTE_CLOSED; - if( m_potentialIssues[m_iActiveIssueIndex] && m_potentialIssues[m_iActiveIssueIndex]->IsAllyRestrictedVote() ) + if ( m_potentialIssues[m_iActiveIssueIndex] && m_potentialIssues[m_iActiveIssueIndex]->IsTeamRestrictedVote() ) { CBaseEntity *pVoteHolder = UTIL_EntityByIndex( m_iEntityHoldingVote ); CBaseEntity *pVoter = UTIL_EntityByIndex( iEntIndex ); - if( ( pVoteHolder == NULL ) || ( pVoter == NULL ) || ( GetVoterTeam( pVoteHolder ) != GetVoterTeam( pVoter ) ) ) + if ( ( pVoteHolder == NULL ) || ( pVoter == NULL ) || ( GetVoterTeam( pVoteHolder ) != GetVoterTeam( pVoter ) ) ) { return CAST_FAIL_TEAM_RESTRICTED; } @@ -552,7 +593,7 @@ CVoteController::TryCastVoteResult CVoteController::TryCastVote( int iEntIndex, // Look for a previous vote int nOldVote = m_nVotesCast[iEntIndex]; #ifndef DEBUG - if( nOldVote != VOTE_UNCAST ) + if ( nOldVote != VOTE_UNCAST ) { return CAST_FAIL_NO_CHANGES; } @@ -644,67 +685,54 @@ void CVoteController::VoteControllerThink( void ) } // Vote time is up - process the result - if( m_acceptingVotesTimer.HasStarted() && m_acceptingVotesTimer.IsElapsed() ) + if ( m_acceptingVotesTimer.HasStarted() && m_acceptingVotesTimer.IsElapsed() ) { m_acceptingVotesTimer.Invalidate(); - int nVoteTally = 0; - for ( int index = 0; index < MAX_VOTE_OPTIONS; index++ ) - { - nVoteTally += m_nVoteOptionCount.Get( index ); - } - - bool bVotePassed = true; - - // for record-keeping + // For GC record-keeping if ( m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() ) { m_potentialIssues[m_iActiveIssueIndex]->SetYesNoVoteCount( m_nVoteOptionCount[VOTE_OPTION1], m_nVoteOptionCount[VOTE_OPTION2], m_nPotentialVotes ); } - // Have we exceeded the required ratio of Voted-vs-Abstained? - if ( nVoteTally >= m_nPotentialVotes * sv_vote_quorum_ratio.GetFloat() ) - { - int nWinningVoteOption = GetWinningVoteOption(); - Assert( nWinningVoteOption >= 0 && nWinningVoteOption < m_VoteOptions.Count() ); + bool bVotePassed = false; - if ( nWinningVoteOption >= 0 && nWinningVoteOption < MAX_VOTE_OPTIONS ) + if ( GetNumVotesCast() >= ( m_nPotentialVotes * m_potentialIssues[m_iActiveIssueIndex]->GetQuorumRatio() ) ) + { + int nPassingVoteOptionIndex = GetVoteIssueIndexWithHighestCount(); + if ( nPassingVoteOptionIndex >= 0 && nPassingVoteOptionIndex < MAX_VOTE_OPTIONS ) { - // YES/NO VOTES + // YES/NO VOTES - hard-wired to VOTE_OPTION1 (Yes) if ( m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() ) { - // Option1 is Yes - if ( nWinningVoteOption != VOTE_OPTION1 ) + if ( nPassingVoteOptionIndex == VOTE_OPTION1 ) { - SendVoteFailedToPassMessage( VOTE_FAILED_YES_MUST_EXCEED_NO ); - bVotePassed = false; - } + bVotePassed = true; + } } - // GENERAL VOTES: - // We set the details string after the vote, since that's when - // we finally have a parameter to pass along and execute - else if ( nWinningVoteOption < m_VoteOptions.Count() ) + // GENERAL VOTES - as long as there's a quorum, go with the most popular choice + else { - m_potentialIssues[m_iActiveIssueIndex]->SetIssueDetails( m_VoteOptions[nWinningVoteOption] ); + bVotePassed = true; + + // We set the details string after the vote, since that's when + // we finally have a parameter to pass along and execute + m_potentialIssues[m_iActiveIssueIndex]->SetIssueDetails( m_VoteOptions[nPassingVoteOptionIndex] ); } } } - else - { - SendVoteFailedToPassMessage( VOTE_FAILED_QUORUM_FAILURE ); - bVotePassed = false; - } - if ( bVotePassed ) + if ( bVotePassed ) { - m_executeCommandTimer.Start( sv_vote_command_delay.GetFloat() ); - m_resetVoteTimer.Start( 5.0 ); - - // Target is not always a player (changelevel, etc) + // Always NULL check, as some votes don't target players (i.e. ChangeLevel) CBasePlayer *pVoteTarget = m_potentialIssues[m_iActiveIssueIndex]->m_hPlayerTarget; - bool bFakeClient = ( pVoteTarget && ( pVoteTarget->IsFakeClient() || pVoteTarget->IsHLTV() || pVoteTarget->IsReplay() ) ); - UTIL_LogPrintf( "Vote succeeded \"%s %s\" (proxy: %i) \n", m_potentialIssues[m_iActiveIssueIndex]->GetTypeString(), m_potentialIssues[m_iActiveIssueIndex]->GetDetailsString(), bFakeClient ); + // Don't delay successful kick votes + float flDelay = IsPlayerBeingKicked( pVoteTarget ) ? 0.f : sv_vote_command_delay.GetFloat(); + m_executeCommandTimer.Start( flDelay ); + m_resetVoteTimer.Start( 5.f ); + + UTIL_LogPrintf( "Vote succeeded \"%s %s\"\n", m_potentialIssues[m_iActiveIssueIndex]->GetTypeString(), m_potentialIssues[m_iActiveIssueIndex]->GetDetailsString() ); CBroadcastRecipientFilter filter; filter.MakeReliable(); @@ -717,8 +745,10 @@ void CVoteController::VoteControllerThink( void ) } else { + vote_create_failed_t nReason = m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() ? VOTE_FAILED_YES_MUST_EXCEED_NO : VOTE_FAILED_QUORUM_FAILURE; + SendVoteFailedToPassMessage( nReason ); m_potentialIssues[m_iActiveIssueIndex]->OnVoteFailed( m_iEntityHoldingVote ); - m_resetVoteTimer.Start( 5.0 ); + m_resetVoteTimer.Start( 5.f ); } } @@ -768,12 +798,15 @@ void CVoteController::CheckForEarlyVoteClose( void ) //----------------------------------------------------------------------------- bool CVoteController::IsValidVoter( CBasePlayer *pWhom ) { - if ( pWhom == NULL ) + if ( !pWhom ) return false; if ( !pWhom->IsConnected() ) return false; + if ( pWhom->GetTeamNumber() == TEAM_UNASSIGNED ) + return false; + if ( !sv_vote_allow_spectators.GetBool() ) { if ( pWhom->GetTeamNumber() == TEAM_SPECTATOR ) @@ -818,7 +851,7 @@ void CVoteController::RegisterIssue( CBaseIssue *pszNewIssue ) //----------------------------------------------------------------------------- void CVoteController::ListIssues( CBasePlayer *pForWhom ) { - if( !sv_allow_votes.GetBool() ) + if ( !IsVoteSystemEnabled() ) return; ClientPrint( pForWhom, HUD_PRINTCONSOLE, "---Vote commands---\n" ); @@ -832,45 +865,34 @@ void CVoteController::ListIssues( CBasePlayer *pForWhom ) } //----------------------------------------------------------------------------- -// Purpose: +// Purpose: -1 when invalid //----------------------------------------------------------------------------- -int CVoteController::GetWinningVoteOption( void ) +int CVoteController::GetVoteIssueIndexWithHighestCount( void ) { - if ( m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() ) + int nMaxIndex = -1; + + // Legacy Yes/No system + if ( m_iActiveIssueIndex != INVALID_ISSUE && m_potentialIssues[m_iActiveIssueIndex]->IsYesNoVote() ) { return ( m_nVoteOptionCount[VOTE_OPTION1] > m_nVoteOptionCount[VOTE_OPTION2] ) ? VOTE_OPTION1 : VOTE_OPTION2; } + // Which option had the most votes? else { - CUtlVector <int> pVoteCounts; + int nMaxCount = 0; - // Which option had the most votes? - // driller: Need to handle ties - int nHighest = m_nVoteOptionCount[0]; + // TODO: Handle ties for ( int iIndex = 0; iIndex < m_nVoteOptionCount.Count(); iIndex ++ ) { - nHighest = ( ( nHighest < m_nVoteOptionCount[iIndex] ) ? m_nVoteOptionCount[iIndex] : nHighest ); - pVoteCounts.AddToTail( m_nVoteOptionCount[iIndex] ); - } - - m_nHighestCountIndex = -1; - for ( int iIndex = 0; iIndex < m_nVoteOptionCount.Count(); iIndex++ ) - { - if ( m_nVoteOptionCount[iIndex] == nHighest ) + if ( m_nVoteOptionCount[iIndex] && m_nVoteOptionCount[iIndex] > nMaxCount ) { - m_nHighestCountIndex = iIndex; - // henryg: break on first match, not last. this avoids a crash - // if we are all tied at zero and we pick something beyond the - // last vote option. this code really ought to ignore attempts - // to tally votes for options beyond the last valid one! - break; + nMaxCount = m_nVoteOptionCount[iIndex]; + nMaxIndex = iIndex; } } - - return m_nHighestCountIndex; } - return -1; + return nMaxIndex; } //----------------------------------------------------------------------------- @@ -898,11 +920,12 @@ void CVoteController::TrackVoteCaller( CBasePlayer *pPlayer ) //----------------------------------------------------------------------------- // Purpose: Check the history of steamIDs that called votes and test against a timer //----------------------------------------------------------------------------- -bool CVoteController::CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown ) +bool CVoteController::CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown, vote_create_failed_t &nErrorCode ) { if ( !pPlayer ) return false; - + +#ifndef _DEBUG CSteamID steamID; pPlayer->GetSteamID( &steamID ); @@ -913,11 +936,15 @@ bool CVoteController::CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown ) // Timer elapsed? nCooldown = (int)( m_VoteCallers[ iIdx ] - gpGlobals->curtime ); if ( nCooldown > 0 ) + { + nErrorCode = VOTE_FAILED_RATE_EXCEEDED; return false; + } // Expired m_VoteCallers.Remove( iIdx ); } +#endif return true; }; @@ -925,17 +952,61 @@ bool CVoteController::CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown ) //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- +int CVoteController::GetNumVotesCast( void ) +{ + int nVoteTally = 0; + + for ( int index = 0; index < MAX_VOTE_OPTIONS; index++ ) + { + nVoteTally += m_nVoteOptionCount.Get( index ); + } + + return nVoteTally; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- void CVoteController::AddPlayerToKickWatchList( CSteamID steamID, float flDuration ) { VoteControllerSystem.AddPlayerToKickWatchList( steamID, flDuration ); } //----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void CVoteController::AddPlayerToNameLockedList( CSteamID steamID, float flDuration, int nUserID ) +{ + engine->ServerCommand( UTIL_VarArgs( "namelockid %d %d\n", nUserID, 1 ) ); + + VoteControllerSystem.AddPlayerToNameLockedList( steamID, flDuration ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool CVoteController::IsPlayerBeingKicked( CBasePlayer *pPlayer ) +{ +#ifdef TF_DLL + if ( pPlayer && m_iActiveIssueIndex != INVALID_ISSUE ) + { + CKickIssue *pKickIssue = dynamic_cast< CKickIssue* >( m_potentialIssues[m_iActiveIssueIndex] ); + if ( pKickIssue ) + { + return pKickIssue->m_hPlayerTarget == pPlayer; + } + } +#endif // TF_DLL + + return false; +} + +//----------------------------------------------------------------------------- // Purpose: BaseIssue //----------------------------------------------------------------------------- CBaseIssue::CBaseIssue( const char *pszTypeString ) { - Q_strcpy( m_szTypeString, pszTypeString ); + V_strcpy_safe( m_szTypeString, pszTypeString ); m_iNumYesVotes = 0; m_iNumNoVotes = 0; @@ -979,13 +1050,13 @@ const char *CBaseIssue::GetDetailsString( void ) //----------------------------------------------------------------------------- void CBaseIssue::SetIssueDetails( const char *pszDetails ) { - Q_strcpy( m_szDetailsString, pszDetails ); + V_strcpy_safe( m_szDetailsString, pszDetails ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- -bool CBaseIssue::IsAllyRestrictedVote( void ) +bool CBaseIssue::IsTeamRestrictedVote( void ) { return false; } @@ -1030,7 +1101,7 @@ void CBaseIssue::OnVoteFailed( int iEntityHoldingVote ) // Need to create a new one FailedVote *pNewFailedVote = new FailedVote; int iIndex = m_FailedVotes.AddToTail( pNewFailedVote ); - Q_strcpy( m_FailedVotes[iIndex]->szFailedVoteParameter, GetDetailsString() ); + V_strcpy_safe( m_FailedVotes[iIndex]->szFailedVoteParameter, GetDetailsString() ); m_FailedVotes[iIndex]->flLockoutTime = gpGlobals->curtime + sv_vote_failure_timer.GetFloat(); } } @@ -1184,4 +1255,12 @@ bool CBaseIssue::GetVoteOptions( CUtlVector <const char*> &vecNames ) return true; } +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +float CBaseIssue::GetQuorumRatio( void ) +{ + return sv_vote_quorum_ratio.GetFloat(); +} + diff --git a/mp/src/game/server/vote_controller.h b/mp/src/game/server/vote_controller.h index ccc55cd5..a3f82ff6 100644 --- a/mp/src/game/server/vote_controller.h +++ b/mp/src/game/server/vote_controller.h @@ -20,17 +20,18 @@ class CBaseIssue // Base class concept for vote issues (i.e. Kick Player). Created per level-load and destroyed by CVoteController's dtor. { public: - CBaseIssue(const char *typeString); - virtual ~CBaseIssue(); + CBaseIssue( const char *typeString ); + virtual ~CBaseIssue(); const char *GetTypeString( void ); // Connection between console command and specific type of issue - virtual const char *GetDetailsString(); + virtual const char *GetTypeStringLocalized( void ) { return ""; } // When empty, the client uses the classname string and prepends "#Vote_" + virtual const char *GetDetailsString( void ); virtual void SetIssueDetails( const char *pszDetails ); // We need to know the details part of the con command for later virtual void OnVoteFailed( int iEntityHoldingVote ); // The moment the vote fails, also has some time for feedback before the window goes away virtual void OnVoteStarted( void ) {} // Called as soon as the vote starts virtual bool IsEnabled( void ) { return false; } // Query the issue to see if it's enabled virtual bool CanTeamCallVote( int iTeam ) const; // Can someone on the given team call this vote? virtual bool CanCallVote( int nEntIndex, const char *pszDetails, vote_create_failed_t &nFailCode, int &nTime ); // Can this guy hold a vote on this issue? - virtual bool IsAllyRestrictedVote( void ); // Can only members of the same team vote on this? + virtual bool IsTeamRestrictedVote( void ); // Restrict access and visibility of this vote to a specific team? virtual const char *GetDisplayString( void ) = 0; // The string that will be passed to the client for display virtual void ExecuteCommand( void ) = 0; // Where the magic happens. Do your thing. virtual void ListIssueDetails( CBasePlayer *pForWhom ) = 0; // Someone would like to know all your valid details @@ -42,6 +43,7 @@ public: virtual bool GetVoteOptions( CUtlVector <const char*> &vecNames ); // We use this to generate options for voting virtual bool BRecordVoteFailureEventForEntity( int iVoteCallingEntityIndex ) const { return iVoteCallingEntityIndex != DEDICATED_SERVER; } void SetIssueCooldownDuration( float flDuration ) { m_flNextCallTime = gpGlobals->curtime + flDuration; } // The issue can not be raised again for this period of time (in seconds) + virtual float GetQuorumRatio( void ); // Each issue can decide the required ratio of voted-vs-abstained CHandle< CBasePlayer > m_hPlayerTarget; // If the target of the issue is a player, we should store them here @@ -54,11 +56,9 @@ protected: float flLockoutTime; }; - CUtlVector<FailedVote *> m_FailedVotes; - - char m_szTypeString[MAX_COMMAND_LENGTH]; - char m_szDetailsString[MAX_VOTE_DETAILS_LENGTH]; - + CUtlVector< FailedVote* > m_FailedVotes; + char m_szTypeString[MAX_COMMAND_LENGTH]; + char m_szDetailsString[MAX_VOTE_DETAILS_LENGTH]; int m_iNumYesVotes; int m_iNumNoVotes; int m_iNumPotentialVotes; @@ -89,6 +89,7 @@ public: virtual void Spawn( void ); virtual int UpdateTransmitState( void ); + virtual bool IsVoteSystemEnabled( void ); bool SetupVote( int iEntIndex ); // This creates a list of issues for the UI bool CreateVote( int iEntIndex, const char *pszTypeString, const char *pszDetailString ); // This is what the UI passes in @@ -101,12 +102,15 @@ public: void SendVoteFailedToPassMessage( vote_create_failed_t nReason ); void VoteChoice_Increment( int nVoteChoice ); void VoteChoice_Decrement( int nVoteChoice ); - int GetWinningVoteOption( void ); + int GetVoteIssueIndexWithHighestCount( void ); void TrackVoteCaller( CBasePlayer *pPlayer ); - bool CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown ); + bool CanEntityCallVote( CBasePlayer *pPlayer, int &nCooldown, vote_create_failed_t &nErrorCode ); bool IsVoteActive( void ) { return m_iActiveIssueIndex != INVALID_ISSUE; } + int GetNumVotesCast( void ); - void AddPlayerToKickWatchList( CSteamID steamID, float flDuration ); + void AddPlayerToKickWatchList( CSteamID steamID, float flDuration ); // Band-aid until we figure out how player's avoid kick votes + void AddPlayerToNameLockedList( CSteamID steamID, float flDuration, int nUserID ); + bool IsPlayerBeingKicked( CBasePlayer *pPlayer ); protected: void ResetData( void ); @@ -123,7 +127,6 @@ protected: CountdownTimer m_resetVoteTimer; // when the current vote will end int m_nVotesCast[MAX_PLAYERS + 1]; // arrays are zero-based and player indices are one-based int m_iEntityHoldingVote; - int m_nHighestCountIndex; CUtlVector <CBaseIssue *> m_potentialIssues; CUtlVector <const char *> m_VoteOptions; |