//---------------------------------------------------------------------------------------- // Purpose: moves the player //---------------------------------------------------------------------------------------- void CTFGameMovement::PlayerMove() { // call base class to do movement BaseClass::PlayerMove(); // handle player's interaction with water int nNewWaterLevel = m_pTFPlayer->GetWaterLevel(); if ( m_nOldWaterLevel != nNewWaterLevel ) { if ( WL_NotInWater == m_nOldWaterLevel ) { // The player has just entered the water. Determine if we should play a splash sound. bool bPlaySplash = false; Vector vecVelocity = m_pTFPlayer->GetAbsVelocity(); if ( vecVelocity.z <= -200.0f ) { // If the player has significant downward velocity, play a splash regardless of water depth. (e.g. Jumping hard into a puddle) bPlaySplash = true; } else { // Look at the water depth below the player. If it's significantly deep, play a splash to accompany the sinking that's about to happen. Vector vecStart = m_pTFPlayer->GetAbsOrigin(); Vector vecEnd = vecStart; vecEnd.z -= 20; // roughly thigh deep trace_t tr; // see if we hit anything solid a little bit below the player UTIL_TraceLine( vecStart, vecEnd, MASK_SOLID,m_pTFPlayer, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction >= 1.0f ) { // some amount of water below the player, play a splash bPlaySplash = true; } } if ( bPlaySplash ) { m_pTFPlayer->EmitSound( "Physics.WaterSplash" ); } } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFGameStats::Event_PlayerKilled( CBasePlayer *pPlayer, const CTakeDamageInfo &info ) { Assert( pPlayer ); CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer ); IncrementStat( pTFPlayer, TFSTAT_DEATHS, 1 ); SendStatsToPlayer( pTFPlayer, STATMSG_PLAYERDEATH ); AccumulateAndResetPerLifeStats( pTFPlayer ); TF_Gamestats_LevelStats_t::PlayerDeathsLump_t death; Vector killerOrg; // set the location where the target died const Vector &org = pPlayer->GetAbsOrigin(); death.nPosition[ 0 ] = static_cast<int>( org.x ); death.nPosition[ 1 ] = static_cast<int>( org.y ); death.nPosition[ 2 ] = static_cast<int>( org.z ); // set the class of the attacker CBaseEntity *pInflictor = info.GetInflictor(); CBaseEntity *pKiller = info.GetAttacker(); CTFPlayer *pScorer = ToTFPlayer( TFGameRules()->GetDeathScorer( pKiller, pInflictor, pPlayer ) ); if ( dynamic_cast< CObjectSentrygun * >( pInflictor ) != NULL ) { killerOrg = pInflictor->GetAbsOrigin(); } else { if ( pScorer ) { CTFPlayerClass *pAttackerClass = pScorer->GetPlayerClass(); death.iAttackClass = ( !pAttackerClass ) ? TF_CLASS_UNDEFINED : pAttackerClass->GetClassIndex(); killerOrg = pScorer->GetAbsOrigin(); } else { death.iAttackClass = TF_CLASS_UNDEFINED; killerOrg = org; } } // set the class of the target CTFPlayerClass *pTargetClass = ( pTFPlayer ) ? pTFPlayer->GetPlayerClass() : NULL; death.iTargetClass = ( !pTargetClass ) ? TF_CLASS_UNDEFINED : pTargetClass->GetClassIndex(); // find the weapon the killer used death.iWeapon = GetWeaponFromDamage( info ); // calculate the distance to the killer death.iDistance = static_cast<unsigned short>( ( killerOrg - org ).Length() ); // add it to the list of deaths TF_Gamestats_LevelStats_t *map = m_reportedStats.m_pCurrentGame; if ( map ) { map->m_aPlayerDeaths.AddToTail( death ); int iClass = ToTFPlayer( pPlayer )->GetPlayerClass()->GetClassIndex(); if ( m_reportedStats.m_pCurrentGame != NULL ) { m_reportedStats.m_pCurrentGame->m_aClassStats[iClass].iDeaths++; m_reportedStats.m_pCurrentGame->m_aClassStats[iClass].iTotalTime += (int) ( gpGlobals->curtime - pTFPlayer->GetSpawnTime() ); } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFGameStats::Event_PlayerDamage( CBasePlayer *pBasePlayer, const CTakeDamageInfo &info, int iDamageTaken ) { CObjectSentrygun *pSentry = NULL; CTFPlayer *pTarget = ToTFPlayer( pBasePlayer ); CTFPlayer *pAttacker = ToTFPlayer( info.GetAttacker() ); if ( !pAttacker ) { pSentry = dynamic_cast< CObjectSentrygun * >( info.GetAttacker() ); if ( !pSentry ) return; pAttacker = pSentry->GetOwner(); if ( !pAttacker ) { // Sentry with no builder? Must be a pre-placed sentry. // It's easier to just cut off here and don't count damage. return; } } // don't count damage to yourself if ( pTarget == pAttacker ) return; IncrementStat( pAttacker, TFSTAT_DAMAGE, iDamageTaken ); TF_Gamestats_LevelStats_t::PlayerDamageLump_t damage; Vector killerOrg; // set the location where the target was hit const Vector &org = pTarget->GetAbsOrigin(); damage.nTargetPosition[ 0 ] = static_cast<int>( org.x ); damage.nTargetPosition[ 1 ] = static_cast<int>( org.y ); damage.nTargetPosition[ 2 ] = static_cast<int>( org.z ); // set the class of the attacker CBaseEntity *pInflictor = info.GetInflictor(); CBasePlayer *pScorer = TFGameRules()->GetDeathScorer( pAttacker, pInflictor, pTarget ); if ( !pSentry ) { pSentry = dynamic_cast< CObjectSentrygun * >( pInflictor ); } if ( pSentry != NULL ) { killerOrg = pSentry->GetAbsOrigin(); damage.iAttackClass = TF_CLASS_ENGINEER; damage.iWeapon = ( info.GetDamageType() & DMG_BLAST ) ? TF_WEAPON_SENTRY_ROCKET : TF_WEAPON_SENTRY_BULLET; } else if ( dynamic_cast<CObjectDispenser *>( pInflictor ) ) { damage.iAttackClass = TF_CLASS_ENGINEER; damage.iWeapon = TF_WEAPON_DISPENSER; } else { CTFPlayer *pTFAttacker = ToTFPlayer( pScorer ); if ( pTFAttacker ) { CTFPlayerClass *pAttackerClass = pTFAttacker->GetPlayerClass(); damage.iAttackClass = ( !pAttackerClass ) ? TF_CLASS_UNDEFINED : pAttackerClass->GetClassIndex(); killerOrg = pTFAttacker->GetAbsOrigin(); } else { damage.iAttackClass = TF_CLASS_UNDEFINED; killerOrg = org; } // find the weapon the killer used damage.iWeapon = GetWeaponFromDamage( info ); } // If normal gameplay state, track weapon stats. if ( ( TFGameRules()->State_Get() == GR_STATE_RND_RUNNING ) && ( damage.iWeapon != TF_WEAPON_NONE ) ) { // record hits & damage in reported per-weapon stats if ( m_reportedStats.m_pCurrentGame != NULL ) { TF_Gamestats_WeaponStats_t *pWeaponStats = &m_reportedStats.m_pCurrentGame->m_aWeaponStats[damage.iWeapon]; pWeaponStats->iHits++; pWeaponStats->iTotalDamage += iDamageTaken; // Try and figure out where the damage is coming from Vector vecDamageOrigin = info.GetReportedPosition(); // If we didn't get an origin to use, try using the attacker's origin if ( vecDamageOrigin == vec3_origin ) { if ( pSentry ) { vecDamageOrigin = pSentry->GetAbsOrigin(); } else { vecDamageOrigin = killerOrg; } } if ( vecDamageOrigin != vec3_origin ) { pWeaponStats->iHitsWithKnownDistance++; int iDistance = (int) vecDamageOrigin.DistTo( pBasePlayer->GetAbsOrigin() ); // Msg( "Damage distance: %d\n", iDistance ); pWeaponStats->iTotalDistance += iDistance; } } } Assert( damage.iAttackClass != TF_CLASS_UNDEFINED ); // record the time the damage occurred damage.fTime = gpGlobals->curtime; // store the attacker's position damage.nAttackerPosition[ 0 ] = static_cast<int>( killerOrg.x ); damage.nAttackerPosition[ 1 ] = static_cast<int>( killerOrg.y ); damage.nAttackerPosition[ 2 ] = static_cast<int>( killerOrg.z ); // set the class of the target CTFPlayer *pTFPlayer = ToTFPlayer( pTarget ); CTFPlayerClass *pTargetClass = ( pTFPlayer ) ? pTFPlayer->GetPlayerClass() : NULL; damage.iTargetClass = ( !pTargetClass ) ? TF_CLASS_UNDEFINED : pTargetClass->GetClassIndex(); Assert( damage.iTargetClass != TF_CLASS_UNDEFINED ); // record the damage done damage.iDamage = info.GetDamage(); // record if it was a crit damage.iCrit = ( ( info.GetDamageType() & DMG_CRITICAL ) != 0 ); // record if it was a kill damage.iKill = ( pTarget->GetHealth() <= 0 ); // add it to the list of damages if ( m_reportedStats.m_pCurrentGame != NULL ) { m_reportedStats.m_pCurrentGame->m_aPlayerDamage.AddToTail( damage ); } }
//----------------------------------------------------------------------------- // Look for a target //----------------------------------------------------------------------------- bool CObjectSentrygun::FindTarget() { // Disable the sentry guns for ifm. if ( tf_sentrygun_notarget.GetBool() ) return false; if ( IsInCommentaryMode() ) return false; // Sapper, etc. if ( IsDisabled() ) return false; // Loop through players within 1100 units (sentry range). Vector vecSentryOrigin = EyePosition(); // Find the opposing team list. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() ); CUtlVector<CTFTeam *> pTeamList; CTFTeam *pTeam = NULL; //CTFTeam *pTeam = pPlayer->GetOpposingTFTeam(); //if ( !pTeam ) // return false; if ( pPlayer ) { // Try builder's team. pTeam = pPlayer->GetTFTeam(); } else { // If we have no builder use our own team number instead. pTeam = GetTFTeam(); } if ( pTeam ) pTeam->GetOpposingTFTeamList( &pTeamList ); else return false; // If we have an enemy get his minimum distance to check against. Vector vecSegment; Vector vecTargetCenter; float flMinDist2 = 1100.0f * 1100.0f; CBaseEntity *pTargetCurrent = NULL; CBaseEntity *pTargetOld = m_hEnemy.Get(); float flOldTargetDist2 = FLT_MAX; // Sentries will try to target players first, then objects. However, if the enemy held was an object it will continue // to try and attack it first. for (int i = 0; i < pTeamList.Size(); i++) { int nTeamCount = pTeamList[i]->GetNumPlayers(); for (int iPlayer = 0; iPlayer < nTeamCount; ++iPlayer) { CTFPlayer *pTargetPlayer = static_cast<CTFPlayer*>(pTeamList[i]->GetPlayer(iPlayer)); if (pTargetPlayer == NULL) continue; // Make sure the player is alive. if (!pTargetPlayer->IsAlive()) continue; if (pTargetPlayer->GetFlags() & FL_NOTARGET) continue; vecTargetCenter = pTargetPlayer->GetAbsOrigin(); vecTargetCenter += pTargetPlayer->GetViewOffset(); VectorSubtract(vecTargetCenter, vecSentryOrigin, vecSegment); float flDist2 = vecSegment.LengthSqr(); // Store the current target distance if we come across it if (pTargetPlayer == pTargetOld) { flOldTargetDist2 = flDist2; } // Check to see if the target is closer than the already validated target. if (flDist2 > flMinDist2) continue; // It is closer, check to see if the target is valid. if (ValidTargetPlayer(pTargetPlayer, vecSentryOrigin, vecTargetCenter)) { flMinDist2 = flDist2; pTargetCurrent = pTargetPlayer; } } // If we already have a target, don't check objects. if (pTargetCurrent == NULL) { int nTeamObjectCount = pTeamList[i]->GetNumObjects(); for (int iObject = 0; iObject < nTeamObjectCount; ++iObject) { CBaseObject *pTargetObject = pTeamList[i]->GetObject(iObject); if (!pTargetObject) continue; vecTargetCenter = pTargetObject->GetAbsOrigin(); vecTargetCenter += pTargetObject->GetViewOffset(); VectorSubtract(vecTargetCenter, vecSentryOrigin, vecSegment); float flDist2 = vecSegment.LengthSqr(); // Store the current target distance if we come across it if (pTargetObject == pTargetOld) { flOldTargetDist2 = flDist2; } // Check to see if the target is closer than the already validated target. if (flDist2 > flMinDist2) continue; // It is closer, check to see if the target is valid. if (ValidTargetObject(pTargetObject, vecSentryOrigin, vecTargetCenter)) { flMinDist2 = flDist2; pTargetCurrent = pTargetObject; } } } // We have a target. if (pTargetCurrent) { if (pTargetCurrent != pTargetOld) { // flMinDist2 is the new target's distance // flOldTargetDist2 is the old target's distance // Don't switch unless the new target is closer by some percentage if (flMinDist2 < (flOldTargetDist2 * 0.75f)) { FoundTarget(pTargetCurrent, vecSentryOrigin); } } return true; } } return false; }