aboutsummaryrefslogtreecommitdiff
path: root/mp/src/game/server
diff options
context:
space:
mode:
authorJohn Schoenick <[email protected]>2015-09-09 18:35:41 -0700
committerJohn Schoenick <[email protected]>2015-09-09 18:35:41 -0700
commit0d8dceea4310fde5706b3ce1c70609d72a38efdf (patch)
treec831ef32c2c801a5c5a80401736b52c7b5a528ec /mp/src/game/server
parentUpdated the SDK with the latest code from the TF and HL2 branches. (diff)
downloadsource-sdk-2013-master.tar.xz
source-sdk-2013-master.zip
Updated the SDK with the latest code from the TF and HL2 branches.HEADmaster
Diffstat (limited to 'mp/src/game/server')
-rw-r--r--mp/src/game/server/AI_ResponseSystem.h1
-rw-r--r--mp/src/game/server/BaseAnimatingOverlay.cpp19
-rw-r--r--mp/src/game/server/BaseAnimatingOverlay.h1
-rw-r--r--mp/src/game/server/CommentarySystem.cpp2
-rw-r--r--mp/src/game/server/GameStats_BasicStatsFunctions.cpp2
-rw-r--r--mp/src/game/server/ai_activity.cpp47
-rw-r--r--mp/src/game/server/baseanimating.cpp52
-rw-r--r--mp/src/game/server/baseanimating.h11
-rw-r--r--mp/src/game/server/basecombatcharacter.cpp4
-rw-r--r--mp/src/game/server/baseentity.cpp14
-rw-r--r--mp/src/game/server/baseentity.h7
-rw-r--r--mp/src/game/server/baseflex.cpp4
-rw-r--r--mp/src/game/server/client.cpp73
-rw-r--r--mp/src/game/server/doors.h2
-rw-r--r--mp/src/game/server/episodic/ep2_gamestats.h8
-rw-r--r--mp/src/game/server/func_break.cpp4
-rw-r--r--mp/src/game/server/gameinterface.cpp71
-rw-r--r--mp/src/game/server/gameinterface.h14
-rw-r--r--mp/src/game/server/items.h3
-rw-r--r--mp/src/game/server/nav_area.cpp5
-rw-r--r--mp/src/game/server/nav_area.h11
-rw-r--r--mp/src/game/server/nav_entities.cpp3
-rw-r--r--mp/src/game/server/nav_file.cpp14
-rw-r--r--mp/src/game/server/nav_generate.cpp11
-rw-r--r--mp/src/game/server/nav_mesh.cpp12
-rw-r--r--mp/src/game/server/networkstringtable_gamedll.h2
-rw-r--r--mp/src/game/server/player.cpp35
-rw-r--r--mp/src/game/server/player.h9
-rw-r--r--mp/src/game/server/player_command.cpp7
-rw-r--r--mp/src/game/server/point_spotlight.cpp32
-rw-r--r--mp/src/game/server/props.cpp2
-rw-r--r--mp/src/game/server/props.h3
-rw-r--r--mp/src/game/server/recipientfilter.cpp2
-rw-r--r--mp/src/game/server/sceneentity.cpp3
-rw-r--r--mp/src/game/server/slideshow_display.cpp8
-rw-r--r--mp/src/game/server/team_control_point.cpp18
-rw-r--r--mp/src/game/server/team_control_point.h2
-rw-r--r--mp/src/game/server/team_control_point_master.cpp65
-rw-r--r--mp/src/game/server/team_control_point_master.h1
-rw-r--r--mp/src/game/server/trigger_area_capture.cpp16
-rw-r--r--mp/src/game/server/triggers.cpp162
-rw-r--r--mp/src/game/server/triggers.h22
-rw-r--r--mp/src/game/server/util.cpp25
-rw-r--r--mp/src/game/server/util.h1
-rw-r--r--mp/src/game/server/vote_controller.cpp375
-rw-r--r--mp/src/game/server/vote_controller.h29
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;