//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFPlayerResource::UpdatePlayerData( void ) { int i; BaseClass::UpdatePlayerData(); for ( i = 1 ; i <= gpGlobals->maxClients; i++ ) { CTFPlayer *pPlayer = (CTFPlayer*)UTIL_PlayerByIndex( i ); if ( pPlayer && pPlayer->IsConnected() ) { PlayerStats_t *pPlayerStats = CTF_GameStats.FindPlayerStats( pPlayer ); if ( pPlayerStats ) { m_iMaxHealth.Set( i, pPlayer->GetPlayerClass()->GetMaxHealth() ); m_iPlayerClass.Set( i, pPlayer->GetPlayerClass()->GetClassIndex() ); int iTotalScore = CTFGameRules::CalcPlayerScore( &pPlayerStats->statsAccumulated ); m_iTotalScore.Set( i, iTotalScore ); Vector vecColor = pPlayer->m_vecPlayerColor; m_iColors.Set(i, Vector(vecColor.x, vecColor.y, vecColor.z)); //Msg("Server %f %f %f\n", m_iColors.Get(i).x, m_iColors.Get(i).y, m_iColors.Get(i).z); m_iKillstreak.Set(i, pPlayer->m_Shared.GetKillstreak()); } } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFForceRespawn::ForceRespawn( bool bSwitchTeams ) { int i = 0; // respawn the players for ( i = 1 ; i <= gpGlobals->maxClients ; i++ ) { CTFPlayer *pPlayer = ToTFPlayer( UTIL_PlayerByIndex( i ) ); if ( pPlayer ) { pPlayer->RemoveAllOwnedEntitiesFromWorld(); // Ignore players who aren't on an active team if ( pPlayer->GetTeamNumber() != TF_TEAM_RED && pPlayer->GetTeamNumber() != TF_TEAM_BLUE ) { // Let the player spawn immediately when they do pick a class pPlayer->AllowInstantSpawn(); continue; } if ( bSwitchTeams ) { if ( pPlayer->GetTeamNumber() == TF_TEAM_RED ) { pPlayer->ForceChangeTeam( TF_TEAM_BLUE ); } else if ( pPlayer->GetTeamNumber() == TF_TEAM_BLUE ) { pPlayer->ForceChangeTeam( TF_TEAM_RED ); } } // Ignore players who haven't picked a class yet if ( !pPlayer->GetPlayerClass() || pPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_UNDEFINED ) { // Allow them to spawn instantly when they do choose pPlayer->AllowInstantSpawn(); continue; } pPlayer->ForceRespawn(); } } // remove any dropped weapons/ammo packs CBaseEntity *pEnt = NULL; while ( (pEnt = gEntList.FindEntityByClassname( pEnt, "tf_ammo_pack" )) != NULL ) { UTIL_Remove( pEnt ); } // Output. m_outputOnForceRespawn.FireOutput( this, this ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItem_DynamicResupply::ComputeAmmoRatios( CItem_DynamicResupply* pMaster, CBasePlayer *pPlayer, int iDebug, SpawnInfo_t *pSpawnInfo ) { #ifdef TF_CLASSIC // TF2 players don't use HL2 ammo and TF2 ammo packs are based on ratios so we act differently here. // We get player's lowest ammo ratio and compare it to the desired ratio. CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer ); float flLowestRatio = 1.0f; for ( int i = 1; i < TF_AMMO_GRENADES1; i++ ) { float flMaxAmmo = pTFPlayer->GetPlayerClass()->GetData()->m_aAmmoMax[i]; float flCurrentAmmo = pTFPlayer->GetAmmoCount( i ); flLowestRatio = min( flLowestRatio, ( flCurrentAmmo / flMaxAmmo ) ); } #endif for ( int i = 0; i < NUM_AMMO_ITEMS; i++ ) { #ifdef TF_CLASSIC flLowestRatio += (pSpawnInfo[i].m_iPotentialItems * g_DynamicResupplyAmmoItems[i].flAmmoRatio); pSpawnInfo[i].m_flCurrentRatio = clamp( flLowestRatio, 0, 1 );; #else // Get the ammodef's int iAmmoType = GetAmmoDef()->Index( g_DynamicResupplyAmmoItems[i].sAmmoDef ); Assert( iAmmoType != -1 ); // Ignore ammo types if we don't have a weapon that uses it (except for the grenade) if ( (i != DS_GRENADE_INDEX) && !pPlayer->Weapon_GetWpnForAmmo( iAmmoType ) ) { pSpawnInfo[i].m_flCurrentRatio = 1.0; } else { float flMax = GetAmmoDef()->MaxCarry( iAmmoType ); float flCurrentAmmo = pPlayer->GetAmmoCount( iAmmoType ); flCurrentAmmo += (pSpawnInfo[i].m_iPotentialItems * g_DynamicResupplyAmmoItems[i].iAmmoCount); pSpawnInfo[i].m_flCurrentRatio = (flCurrentAmmo / flMax); } #endif // Use the master if we're supposed to pSpawnInfo[i].m_flDesiredRatio = pMaster->m_flDesiredAmmo[i] * sk_dynamic_resupply_modifier.GetFloat(); pSpawnInfo[i].m_flDelta = pSpawnInfo[i].m_flDesiredRatio - pSpawnInfo[i].m_flCurrentRatio; pSpawnInfo[i].m_flDelta = clamp( pSpawnInfo[i].m_flDelta, 0, 1 ); } if ( iDebug ) { Msg("Calculating desired ammo ratios & deltas:\n"); for ( int i = 0; i < NUM_AMMO_ITEMS; i++ ) { Msg(" %s Desired Ratio: %.2f, Current Ratio: %.2f = Delta of %.2f\n", g_DynamicResupplyAmmoItems[i].sEntityName, pSpawnInfo[i].m_flDesiredRatio, pSpawnInfo[i].m_flCurrentRatio, pSpawnInfo[i].m_flDelta ); } } }
//----------------------------------------------------------------------------- // Purpose: MyTouch function for the ammopack //----------------------------------------------------------------------------- bool CAmmoPack::MyTouch( CBasePlayer *pPlayer ) { bool bSuccess = false; if ( ValidTouch( pPlayer ) ) { CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer ); if ( !pTFPlayer ) return false; int iMaxPrimary = pTFPlayer->GetPlayerClass()->GetData()->m_aAmmoMax[TF_AMMO_PRIMARY]; if ( pPlayer->GiveAmmo( ceil(iMaxPrimary * PackRatios[GetPowerupSize()]), TF_AMMO_PRIMARY, true ) ) { bSuccess = true; } int iMaxSecondary = pTFPlayer->GetPlayerClass()->GetData()->m_aAmmoMax[TF_AMMO_SECONDARY]; if ( pPlayer->GiveAmmo( ceil(iMaxSecondary * PackRatios[GetPowerupSize()]), TF_AMMO_SECONDARY, true ) ) { bSuccess = true; } int iMaxMetal = pTFPlayer->GetPlayerClass()->GetData()->m_aAmmoMax[TF_AMMO_METAL]; if ( pPlayer->GiveAmmo( ceil(iMaxMetal * PackRatios[GetPowerupSize()]), TF_AMMO_METAL, true ) ) { bSuccess = true; } // did we give them anything? if ( bSuccess ) { CSingleUserRecipientFilter filter( pPlayer ); EmitSound( filter, entindex(), TF_AMMOPACK_PICKUP_SOUND ); } } return bSuccess; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFGameStats::Event_PlayerDisconnected( CBasePlayer *pPlayer ) { CTFPlayer *pTFPlayer = ToTFPlayer( pPlayer ); if ( !pTFPlayer ) return; ResetPlayerStats( pTFPlayer ); if ( pPlayer->IsAlive() ) { int iClass = pTFPlayer->GetPlayerClass()->GetClassIndex(); if ( m_reportedStats.m_pCurrentGame != NULL ) { m_reportedStats.m_pCurrentGame->m_aClassStats[iClass].iTotalTime += (int) ( gpGlobals->curtime - pTFPlayer->GetSpawnTime() ); } } }
//----------------------------------------------------------------------------- // Purpose: This is called pre player movement and copies all the data necessary // from the player for movement. (Server-side, the client-side version // of this code can be found in prediction.cpp.) //----------------------------------------------------------------------------- void CTFPlayerMove::SetupMove( CBasePlayer *player, CUserCmd *ucmd, IMoveHelper *pHelper, CMoveData *move ) { CTFPlayer *pTFPlayer = ToTFPlayer( player ); if ( pTFPlayer ) { // Check to see if we are a crouched, heavy, firing his weapons and zero out movement. if ( pTFPlayer->GetPlayerClass()->IsClass( TF_CLASS_HEAVYWEAPONS ) ) { if ( ( pTFPlayer->GetFlags() & FL_DUCKING ) && ( pTFPlayer->m_Shared.InCond( TF_COND_AIMING ) ) ) { ucmd->forwardmove = 0.0f; ucmd->sidemove = 0.0f; } } } BaseClass::SetupMove( player, ucmd, pHelper, move ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CTFGameStats::Event_PlayerKilledOther( CBasePlayer *pAttacker, CBaseEntity *pVictim, const CTakeDamageInfo &info ) { // This also gets called when the victim is a building. That gets tracked separately as building destruction, don't count it here if ( !pVictim->IsPlayer() ) return; CTFPlayer *pPlayerAttacker = static_cast< CTFPlayer * >( pAttacker ); IncrementStat( pPlayerAttacker, TFSTAT_KILLS, 1 ); // keep track of how many times every player kills every other player CTFPlayer *pPlayerVictim = ToTFPlayer( pVictim ); TrackKillStats( pAttacker, pPlayerVictim ); int iClass = pPlayerAttacker->GetPlayerClass()->GetClassIndex(); if ( m_reportedStats.m_pCurrentGame != NULL ) { m_reportedStats.m_pCurrentGame->m_aClassStats[iClass].iKills++; } }
//----------------------------------------------------------------------------- // 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 ); } }
//----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CObjectTeleporter::TeleporterThink( void ) { SetContextThink( &CObjectTeleporter::TeleporterThink, gpGlobals->curtime + BUILD_TELEPORTER_NEXT_THINK, TELEPORTER_THINK_CONTEXT ); // At any point, if our match is not ready, revert to IDLE if ( IsDisabled() || IsRedeploying() || IsMatchingTeleporterReady() == false ) { ShowDirectionArrow( false ); if ( GetState() != TELEPORTER_STATE_IDLE && !IsUpgrading() ) { SetState( TELEPORTER_STATE_IDLE ); CObjectTeleporter *pMatch = GetMatchingTeleporter(); if ( !pMatch ) { // The other end has been destroyed. Revert back to L1. m_iUpgradeLevel = 1; // We need to adjust for any damage received if we downgraded float flHealthPercentage = GetHealth() / GetMaxHealthForCurrentLevel(); SetMaxHealth( GetMaxHealthForCurrentLevel() ); SetHealth( (int)( GetMaxHealthForCurrentLevel() * flHealthPercentage ) ); m_iUpgradeMetal = 0; } } return; } if ( m_flMyNextThink && m_flMyNextThink > gpGlobals->curtime ) return; // pMatch is not NULL and is not building CObjectTeleporter *pMatch = GetMatchingTeleporter(); Assert( pMatch ); Assert( pMatch->m_iState != TELEPORTER_STATE_BUILDING ); switch ( m_iState ) { // Teleporter is not yet active, do nothing case TELEPORTER_STATE_BUILDING: case TELEPORTER_STATE_UPGRADING: ShowDirectionArrow( false ); break; default: case TELEPORTER_STATE_IDLE: // Do we have a match that is active? // Make sure both ends wait through full recharge time in case they get upgraded while recharging. if ( IsMatchingTeleporterReady() && !IsUpgrading() && gpGlobals->curtime > m_flRechargeTime ) { SetState( TELEPORTER_STATE_READY ); EmitSound( "Building_Teleporter.Ready" ); if ( GetObjectMode() == TELEPORTER_TYPE_ENTRANCE ) { ShowDirectionArrow( true ); } } break; case TELEPORTER_STATE_READY: break; case TELEPORTER_STATE_SENDING: { pMatch->TeleporterReceive( m_hTeleportingPlayer, 1.0 ); m_flRechargeTime = gpGlobals->curtime + ( BUILD_TELEPORTER_FADEOUT_TIME + BUILD_TELEPORTER_FADEIN_TIME + g_flTeleporterRechargeTimes[ GetUpgradeLevel() - 1] ); // change state to recharging... SetState( TELEPORTER_STATE_RECHARGING ); } break; case TELEPORTER_STATE_RECEIVING: { // get the position we'll move the player to Vector newPosition = GetAbsOrigin(); newPosition.z += TELEPORTER_MAXS.z + 1; // Telefrag anyone in the way CBaseEntity *pEnts[256]; Vector mins, maxs; Vector expand( 4, 4, 4 ); mins = newPosition + VEC_HULL_MIN - expand; maxs = newPosition + VEC_HULL_MAX + expand; CTFPlayer *pTeleportingPlayer = m_hTeleportingPlayer.Get(); // move the player if ( pTeleportingPlayer ) { CUtlVector<CBaseEntity*> hPlayersToKill; bool bClear = true; // Telefrag any players in the way int numEnts = UTIL_EntitiesInBox( pEnts, 256, mins, maxs, 0 ); if ( numEnts ) { //Iterate through the list and check the results for ( int i = 0; i < numEnts && bClear; i++ ) { if ( pEnts[i] == NULL ) continue; if ( pEnts[i] == this ) continue; // kill players if ( pEnts[i]->IsPlayer() ) { if ( !pTeleportingPlayer->InSameTeam(pEnts[i]) ) { hPlayersToKill.AddToTail( pEnts[i] ); } continue; } if ( pEnts[i]->IsBaseObject() ) continue; // Solid entities will prevent a teleport if ( pEnts[i]->IsSolid() && pEnts[i]->ShouldCollide( pTeleportingPlayer->GetCollisionGroup(), MASK_ALL ) && g_pGameRules->ShouldCollide( pTeleportingPlayer->GetCollisionGroup(), pEnts[i]->GetCollisionGroup() ) ) { // We're going to teleport into something solid. Abort & destroy this exit. bClear = false; } } } if ( bClear ) { // Telefrag all enemy players we've found for ( int player = 0; player < hPlayersToKill.Count(); player++ ) { CTakeDamageInfo info( this, pTeleportingPlayer, 1000, DMG_CRUSH, TF_DMG_TELEFRAG ); hPlayersToKill[player]->TakeDamage( info ); } pTeleportingPlayer->Teleport( &newPosition, &(GetAbsAngles()), &vec3_origin ); // Unzoom if we are a sniper zoomed! if ( ( pTeleportingPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SNIPER ) && pTeleportingPlayer->m_Shared.InCond( TF_COND_AIMING ) ) { CTFWeaponBase *pWpn = pTeleportingPlayer->GetActiveTFWeapon(); if ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_SNIPERRIFLE ) { CTFSniperRifle *pRifle = static_cast<CTFSniperRifle*>( pWpn ); pRifle->ToggleZoom(); } } pTeleportingPlayer->SetFOV( pTeleportingPlayer, 0, tf_teleporter_fov_time.GetFloat(), tf_teleporter_fov_start.GetInt() ); color32 fadeColor = {255,255,255,100}; UTIL_ScreenFade( pTeleportingPlayer, fadeColor, 0.25, 0.4, FFADE_IN ); } else { DetonateObject(); } } SetState( TELEPORTER_STATE_RECEIVING_RELEASE ); m_flMyNextThink = gpGlobals->curtime + ( BUILD_TELEPORTER_FADEIN_TIME ); } break; case TELEPORTER_STATE_RECEIVING_RELEASE: { CTFPlayer *pTeleportingPlayer = m_hTeleportingPlayer.Get(); if ( pTeleportingPlayer ) { int iTeam = GetBuilder() ? GetBuilder()->GetTeamNumber() : GetTeamNumber(); pTeleportingPlayer->m_Shared.SetTeleporterEffectColor( iTeam ); pTeleportingPlayer->TeleportEffect(); pTeleportingPlayer->m_Shared.RemoveCond( TF_COND_SELECTED_TO_TELEPORT ); if ( !m_bWasMapPlaced && GetBuilder() ) CTF_GameStats.Event_PlayerUsedTeleport( GetBuilder(), pTeleportingPlayer ); IGameEvent * event = gameeventmanager->CreateEvent( "player_teleported" ); if ( event ) { event->SetInt( "userid", pTeleportingPlayer->GetUserID() ); if ( GetBuilder() ) event->SetInt( "builderid", GetBuilder()->GetUserID() ); Vector vecOrigin = GetAbsOrigin(); Vector vecDestinationOrigin = GetMatchingTeleporter()->GetAbsOrigin(); Vector vecDifference = Vector( vecOrigin.x - vecDestinationOrigin.x, vecOrigin.y - vecDestinationOrigin.y, vecOrigin.z - vecDestinationOrigin.z ); float flDist = sqrtf( pow( vecDifference.x, 2 ) + pow( vecDifference.y, 2 ) + pow( vecDifference.z, 2 ) ); event->SetFloat( "dist", flDist ); gameeventmanager->FireEvent( event, true ); } // Don't thank ourselves. if ( pTeleportingPlayer != GetBuilder() ) pTeleportingPlayer->SpeakConceptIfAllowed( MP_CONCEPT_TELEPORTED ); } // reset the pointers to the player now that we're done teleporting SetTeleportingPlayer( NULL ); pMatch->SetTeleportingPlayer( NULL ); SetState( TELEPORTER_STATE_RECHARGING ); m_flMyNextThink = gpGlobals->curtime + ( g_flTeleporterRechargeTimes[ GetUpgradeLevel() - 1 ] ); } break; case TELEPORTER_STATE_RECHARGING: // If we are finished recharging, go active if ( gpGlobals->curtime > m_flRechargeTime ) { SetState( TELEPORTER_STATE_READY ); EmitSound( "Building_Teleporter.Ready" ); } break; } }
bool CTFGameMovement::CheckJumpButton() { // Are we dead? Then we cannot jump. if ( player->pl.deadflag ) return false; // Check to see if we are in water. if ( !CheckWaterJumpButton() ) return false; // Cannot jump while taunting if ( m_pTFPlayer->m_Shared.InCond( TF_COND_TAUNTING ) ) return false; // Check to see if the player is a scout. bool bScout = m_pTFPlayer->GetPlayerClass()->IsClass( TF_CLASS_SCOUT ); bool bAirDash = false; bool bOnGround = ( player->GetGroundEntity() != NULL ); // Cannot jump will ducked. if ( player->GetFlags() & FL_DUCKING ) { // Let a scout do it. bool bAllow = (bScout && !bOnGround) || tf2c_duckjump.GetBool() || TFGameRules()->IsDeathmatch(); if ( !bAllow ) return false; } // Cannot jump while in the unduck transition. if ( ( player->m_Local.m_bDucking && ( player->GetFlags() & FL_DUCKING ) ) || ( player->m_Local.m_flDuckJumpTime > 0.0f ) && !tf2c_duckjump.GetBool() ) return false; // Cannot jump again until the jump button has been released. if ( mv->m_nOldButtons & IN_JUMP && !(tf2c_autojump.GetBool() || TFGameRules()->IsDeathmatch()) ) return false; // In air, so ignore jumps (unless you are a scout). if ( !bOnGround ) { if ( bScout && !m_pTFPlayer->m_Shared.IsAirDashing() ) { bAirDash = true; } else { mv->m_nOldButtons |= IN_JUMP; return false; } } // Check for an air dash. if ( bAirDash ) { AirDash(); return true; } PreventBunnyJumping(); // Start jump animation and player sound (specific TF animation and flags). m_pTFPlayer->DoAnimationEvent( PLAYERANIMEVENT_JUMP ); player->PlayStepSound( (Vector &)mv->GetAbsOrigin(), player->m_pSurfaceData, 1.0, true ); m_pTFPlayer->m_Shared.SetJumping( true ); // Set the player as in the air. SetGroundEntity( NULL ); // Check the surface the player is standing on to see if it impacts jumping. float flGroundFactor = 1.0f; if ( player->m_pSurfaceData ) { flGroundFactor = player->m_pSurfaceData->game.jumpFactor; } // fMul = sqrt( 2.0 * gravity * jump_height (21.0units) ) * GroundFactor Assert( sv_gravity.GetFloat() == 800.0f ); float flMul = 268.3281572999747f * flGroundFactor; // Save the current z velocity. float flStartZ = mv->m_vecVelocity[2]; // Acclerate upward if ( ( player->m_Local.m_bDucking ) || ( player->GetFlags() & FL_DUCKING ) ) { // If we are ducking... // d = 0.5 * g * t^2 - distance traveled with linear accel // t = sqrt(2.0 * 45 / g) - how long to fall 45 units // v = g * t - velocity at the end (just invert it to jump up that high) // v = g * sqrt(2.0 * 45 / g ) // v^2 = g * g * 2.0 * 45 / g // v = sqrt( g * 2.0 * 45 ) mv->m_vecVelocity[2] = flMul; // 2 * gravity * jump_height * ground_factor } else { mv->m_vecVelocity[2] += flMul; // 2 * gravity * jump_height * ground_factor } // Apply gravity. FinishGravity(); // Save the output data for the physics system to react to if need be. mv->m_outJumpVel.z += mv->m_vecVelocity[2] - flStartZ; mv->m_outStepHeight += 0.15f; // Flag that we jumped and don't jump again until it is released. mv->m_nOldButtons |= IN_JUMP; return true; }
//----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- void CObjectTeleporter::TeleporterThink( void ) { SetContextThink( &CObjectTeleporter::TeleporterThink, gpGlobals->curtime + BUILD_TELEPORTER_NEXT_THINK, TELEPORTER_THINK_CONTEXT ); // At any point, if our match is not ready, revert to IDLE if ( IsDisabled() || IsMatchingTeleporterReady() == false ) { if ( GetState() != TELEPORTER_STATE_IDLE ) { SetState( TELEPORTER_STATE_IDLE ); ShowDirectionArrow( false ); } return; } if ( m_flMyNextThink && m_flMyNextThink > gpGlobals->curtime ) return; // pMatch is not NULL and is not building CObjectTeleporter *pMatch = GetMatchingTeleporter(); Assert( pMatch ); Assert( pMatch->m_iState != TELEPORTER_STATE_BUILDING ); switch ( m_iState ) { // Teleporter is not yet active, do nothing case TELEPORTER_STATE_BUILDING: break; default: case TELEPORTER_STATE_IDLE: // Do we have a match that is active? if ( IsMatchingTeleporterReady() ) { SetState( TELEPORTER_STATE_READY ); EmitSound( "Building_Teleporter.Ready" ); if ( GetType() == OBJ_TELEPORTER_ENTRANCE ) { ShowDirectionArrow( true ); } } break; case TELEPORTER_STATE_READY: break; case TELEPORTER_STATE_SENDING: { pMatch->TeleporterReceive( m_hTeleportingPlayer, 1.0 ); m_flRechargeTime = gpGlobals->curtime + ( BUILD_TELEPORTER_FADEOUT_TIME + BUILD_TELEPORTER_FADEIN_TIME + TELEPORTER_RECHARGE_TIME ); // change state to recharging... SetState( TELEPORTER_STATE_RECHARGING ); } break; case TELEPORTER_STATE_RECEIVING: { // get the position we'll move the player to Vector newPosition = GetAbsOrigin(); newPosition.z += TELEPORTER_MAXS.z + 1; // Telefrag anyone in the way CBaseEntity *pEnts[256]; Vector mins, maxs; Vector expand( 4, 4, 4 ); mins = newPosition + VEC_HULL_MIN - expand; maxs = newPosition + VEC_HULL_MAX + expand; CTFPlayer *pTeleportingPlayer = m_hTeleportingPlayer.Get(); // move the player if ( pTeleportingPlayer ) { CUtlVector<CBaseEntity*> hPlayersToKill; bool bClear = true; // Telefrag any players in the way int numEnts = UTIL_EntitiesInBox( pEnts, 256, mins, maxs, 0 ); if ( numEnts ) { //Iterate through the list and check the results for ( int i = 0; i < numEnts && bClear; i++ ) { if ( pEnts[i] == NULL ) continue; if ( pEnts[i] == this ) continue; // kill players and NPCs if ( pEnts[i]->IsPlayer() || pEnts[i]->IsNPC() ) { if ( !pTeleportingPlayer->InSameTeam(pEnts[i]) ) { hPlayersToKill.AddToTail( pEnts[i] ); } continue; } if ( pEnts[i]->IsBaseObject() ) continue; // Solid entities will prevent a teleport if ( pEnts[i]->IsSolid() && pEnts[i]->ShouldCollide( pTeleportingPlayer->GetCollisionGroup(), MASK_ALL ) && g_pGameRules->ShouldCollide( pTeleportingPlayer->GetCollisionGroup(), pEnts[i]->GetCollisionGroup() ) ) { // We're going to teleport into something solid. Abort & destroy this exit. bClear = false; } } } if ( bClear ) { // Telefrag all enemy players we've found for ( int player = 0; player < hPlayersToKill.Count(); player++ ) { hPlayersToKill[player]->TakeDamage( CTakeDamageInfo( pTeleportingPlayer, this, 1000, DMG_CRUSH ) ); } pTeleportingPlayer->Teleport( &newPosition, &(GetAbsAngles()), &vec3_origin ); // Unzoom if we are a sniper zoomed! if ( ( pTeleportingPlayer->GetPlayerClass()->GetClassIndex() == TF_CLASS_SNIPER ) && pTeleportingPlayer->m_Shared.InCond( TF_COND_AIMING ) ) { CTFWeaponBase *pWpn = pTeleportingPlayer->GetActiveTFWeapon(); if ( pWpn && pWpn->GetWeaponID() == TF_WEAPON_SNIPERRIFLE ) { CTFSniperRifle *pRifle = static_cast<CTFSniperRifle*>( pWpn ); pRifle->ToggleZoom(); } } pTeleportingPlayer->SetFOV( pTeleportingPlayer, 0, tf_teleporter_fov_time.GetFloat(), tf_teleporter_fov_start.GetInt() ); color32 fadeColor = {255,255,255,100}; UTIL_ScreenFade( pTeleportingPlayer, fadeColor, 0.25, 0.4, FFADE_IN ); } else { DetonateObject(); } } SetState( TELEPORTER_STATE_RECEIVING_RELEASE ); m_flMyNextThink = gpGlobals->curtime + ( BUILD_TELEPORTER_FADEIN_TIME ); } break; case TELEPORTER_STATE_RECEIVING_RELEASE: { CTFPlayer *pTeleportingPlayer = m_hTeleportingPlayer.Get(); if ( pTeleportingPlayer ) { pTeleportingPlayer->TeleportEffect(); pTeleportingPlayer->m_Shared.RemoveCond( TF_COND_SELECTED_TO_TELEPORT ); CTF_GameStats.Event_PlayerUsedTeleport( GetBuilder(), pTeleportingPlayer ); pTeleportingPlayer->SpeakConceptIfAllowed( MP_CONCEPT_TELEPORTED ); } // reset the pointers to the player now that we're done teleporting SetTeleportingPlayer( NULL ); pMatch->SetTeleportingPlayer( NULL ); SetState( TELEPORTER_STATE_RECHARGING ); m_flMyNextThink = gpGlobals->curtime + ( TELEPORTER_RECHARGE_TIME ); } break; case TELEPORTER_STATE_RECHARGING: // If we are finished recharging, go active if ( gpGlobals->curtime > m_flRechargeTime ) { SetState( TELEPORTER_STATE_READY ); EmitSound( "Building_Teleporter.Ready" ); } break; } }