void CObjectTeleporter::ShowDirectionArrow( bool bShow ) { if ( bShow != m_bShowDirectionArrow ) { if ( m_iDirectionBodygroup >= 0 ) { SetBodygroup( m_iDirectionBodygroup, bShow ? 1 : 0 ); } m_bShowDirectionArrow = bShow; if ( bShow ) { CObjectTeleporter *pMatch = GetMatchingTeleporter(); Assert( pMatch ); Vector vecToOwner = pMatch->GetAbsOrigin() - GetAbsOrigin(); QAngle angleToExit; VectorAngles( vecToOwner, Vector(0,0,1), angleToExit ); angleToExit -= GetAbsAngles(); // pose param is flipped and backwards, adjust. //m_flYawToExit = anglemod( -angleToExit.y + 180.0 ); m_flYawToExit = AngleNormalize( -angleToExit.y + 180.0 ); // For whatever reason the original code normalizes angle 0 to 360 while pose param // takes angle from -180 to 180. I have no idea how did this work properly // in official TF2 all this time. (Nicknine) } } }
bool CObjectTeleporter::CheckUpgradeOnHit( CTFPlayer *pPlayer ) { bool bUpgradeSuccesful = false; if ( BaseClass::CheckUpgradeOnHit( pPlayer ) ) { CObjectTeleporter *pMatch = GetMatchingTeleporter(); if ( pMatch ) { //pMatch->m_iUpgradeMetal = m_iUpgradeMetal; if ( pMatch && pMatch->CanBeUpgraded( pPlayer ) && GetUpgradeLevel() > pMatch->GetUpgradeLevel() ) { // This end just got upgraded so make another end play upgrade anim if possible. pMatch->StartUpgrading(); } // Other end still needs to keep up even while hauled etc. CopyUpgradeStateToMatch( pMatch, false ); } bUpgradeSuccesful = true; } return bUpgradeSuccesful; }
void CObjectTeleporter::ShowDirectionArrow( bool bShow ) { if ( bShow != m_bShowDirectionArrow ) { if ( m_iDirectionBodygroup >= 0 ) { SetBodygroup( m_iDirectionBodygroup, bShow ? 1 : 0 ); } m_bShowDirectionArrow = bShow; if ( bShow ) { CObjectTeleporter *pMatch = GetMatchingTeleporter(); Assert( pMatch ); Vector vecToOwner = pMatch->GetAbsOrigin() - GetAbsOrigin(); QAngle angleToExit; VectorAngles( vecToOwner, Vector(0,0,1), angleToExit ); angleToExit -= GetAbsAngles(); // pose param is flipped and backwards, adjust. m_flYawToExit = anglemod( -angleToExit.y + 180 ); } } }
//----------------------------------------------------------------------------- // Receive a teleporting player //----------------------------------------------------------------------------- bool CObjectTeleporter::IsMatchingTeleporterReady( void ) { if ( m_hMatchingTeleporter.Get() == NULL ) { m_hMatchingTeleporter = FindMatch(); } CObjectTeleporter *pMatch = GetMatchingTeleporter(); if ( pMatch && pMatch->GetState() != TELEPORTER_STATE_BUILDING && !pMatch->IsDisabled() && !pMatch->IsUpgrading() && !pMatch->IsRedeploying() ) return true; return false; }
void CObjectTeleporter::InitializeMapPlacedObject( void ) { BaseClass::InitializeMapPlacedObject(); CObjectTeleporter *pMatch = dynamic_cast<CObjectTeleporter*> ( gEntList.FindEntityByName( NULL, m_szMatchingTeleporterName ) ) ; if ( pMatch ) { // Copy upgrade state from higher level end. bool bCopyFrom = pMatch->GetUpgradeLevel() > GetUpgradeLevel(); if ( pMatch->GetUpgradeLevel() == GetUpgradeLevel() ) { // If same level use it if it has more metal. bCopyFrom = pMatch->m_iUpgradeMetal > m_iUpgradeMetal; } CopyUpgradeStateToMatch( pMatch, bCopyFrom ); m_hMatchingTeleporter = pMatch; } }
bool CObjectTeleporter::InputWrenchHit( CTFPlayer *pPlayer, CTFWrench *pWrench, Vector vecHitPos ) { if ( HasSapper() && GetMatchingTeleporter() ) { CObjectTeleporter *pMatch = GetMatchingTeleporter(); // do damage to any attached buildings CTakeDamageInfo info( pPlayer, pPlayer, 65, DMG_CLUB, TF_DMG_WRENCH_FIX ); IHasBuildPoints *pBPInterface = dynamic_cast< IHasBuildPoints * >( pMatch ); int iNumObjects = pBPInterface->GetNumObjectsOnMe(); for ( int iPoint=0; iPoint < iNumObjects; iPoint++ ) { CBaseObject *pObject = pMatch->GetBuildPointObject( iPoint ); if ( pObject && pObject->IsHostileUpgrade() ) pObject->TakeDamage( info ); } } return BaseClass::InputWrenchHit( pPlayer, pWrench, vecHitPos ); }
CObjectTeleporter* CObjectTeleporter::FindMatch( void ) { int iObjMode = GetObjectMode(); int iOppositeMode = ( iObjMode == TELEPORTER_TYPE_ENTRANCE ) ? TELEPORTER_TYPE_EXIT : TELEPORTER_TYPE_ENTRANCE; CObjectTeleporter *pMatch = NULL; CTFPlayer *pBuilder = GetBuilder(); if ( !pBuilder ) { return NULL; } int i; int iNumObjects = pBuilder->GetObjectCount(); for ( i=0;i<iNumObjects;i++ ) { CBaseObject *pObj = pBuilder->GetObject(i); if ( pObj && pObj->GetType() == GetType() && pObj->GetObjectMode() == iOppositeMode && !pObj->IsDisabled() ) { pMatch = ( CObjectTeleporter * )pObj; // Copy upgrade state from higher level end. bool bCopyFrom = pMatch->GetUpgradeLevel() > GetUpgradeLevel(); if ( pMatch->GetUpgradeLevel() == GetUpgradeLevel() ) { // If same level use it if it has more metal. bCopyFrom = pMatch->m_iUpgradeMetal > m_iUpgradeMetal; } CopyUpgradeStateToMatch( pMatch, bCopyFrom ); break; } } return pMatch; }
bool CObjectTeleporter::Command_Repair( CTFPlayer *pActivator ) { bool bRepaired = false; int iAmountToHeal = 0; int iRepairCost = 0; // There's got to be a better way a shorter way to mirror repairs and such. if ( GetHealth() < GetMaxHealth() ) { iAmountToHeal = min( 100, GetMaxHealth() - GetHealth() ); // repair the building iRepairCost = ceil( (float)( iAmountToHeal ) * 0.2f ); TRACE_OBJECT( UTIL_VarArgs( "%0.2f CObjectDispenser::Command_Repair ( %d / %d ) - cost = %d\n", gpGlobals->curtime, GetHealth(), GetMaxHealth(), iRepairCost ) ); if ( iRepairCost > 0 ) { if ( iRepairCost > pActivator->GetBuildResources() ) { iRepairCost = pActivator->GetBuildResources(); } pActivator->RemoveBuildResources( iRepairCost ); float flNewHealth = min( GetMaxHealth(), GetHealth() + ( iRepairCost * 5 ) ); SetHealth( flNewHealth ); bRepaired = (iRepairCost > 0); CObjectTeleporter *pMatch = GetMatchingTeleporter(); if ( pMatch && pMatch->GetState() != TELEPORTER_STATE_BUILDING && !pMatch->IsUpgrading() ) { float flNewHealth = min( pMatch->GetMaxHealth(), pMatch->GetHealth() + ( iRepairCost * 5 ) ); pMatch->SetHealth( flNewHealth ); } } } else if ( GetMatchingTeleporter() ) // See if the other teleporter needs repairing { CObjectTeleporter *pMatch = GetMatchingTeleporter(); if ( pMatch->GetHealth() < pMatch->GetMaxHealth() && pMatch->GetState() != TELEPORTER_STATE_BUILDING && !pMatch->IsUpgrading() ) { iAmountToHeal = min( 100, pMatch->GetMaxHealth() - pMatch->GetHealth() ); // repair the building iRepairCost = ceil( (float)(iAmountToHeal)* 0.2f ); TRACE_OBJECT( UTIL_VarArgs( "%0.2f CObjectDispenser::Command_Repair ( %d / %d ) - cost = %d\n", gpGlobals->curtime, pMatch->GetHealth(), pMatch->GetMaxHealth(), iRepairCost ) ); if ( iRepairCost > 0 ) { if ( iRepairCost > pActivator->GetBuildResources() ) { iRepairCost = pActivator->GetBuildResources(); } pActivator->RemoveBuildResources( iRepairCost ); float flNewHealth = min( pMatch->GetMaxHealth(), pMatch->GetHealth() + ( iRepairCost * 5 ) ); pMatch->SetHealth( flNewHealth ); bRepaired = (iRepairCost > 0); } } } return bRepaired; }
//----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- 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; } }
//----------------------------------------------------------------------------- // Purpose: Start placing or building the currently selected object //----------------------------------------------------------------------------- void CTFWeaponBuilder::PrimaryAttack( void ) { CTFPlayer *pOwner = ToTFPlayer( GetOwner() ); if ( !pOwner ) return; if ( !CanAttack() ) return; // Necessary so that we get the latest building position for the test, otherwise // we are one frame behind. UpdatePlacementState(); // What state should we move to? switch( m_iBuildState ) { case BS_IDLE: { // Idle state starts selection SetCurrentState( BS_SELECTING ); } break; case BS_SELECTING: { // Do nothing, client handles selection return; } break; case BS_PLACING: { if ( m_hObjectBeingBuilt ) { int iFlags = m_hObjectBeingBuilt->GetObjectFlags(); // Tricky, because this can re-calc the object position and change whether its a valid // pos or not. Best not to do this only in debug, but we can be pretty sure that this // will give the same result as was calculated in UpdatePlacementState() above. Assert( IsValidPlacement() ); // If we're placing an attachment, like a sapper, play a placement animation on the owner if ( m_hObjectBeingBuilt->MustBeBuiltOnAttachmentPoint() ) { pOwner->DoAnimationEvent( PLAYERANIMEVENT_ATTACK_GRENADE ); } // Need to save this for later since StartBuilding will clear m_hObjectBeingBuilt. CBaseObject *pParentObject = m_hObjectBeingBuilt->GetParentObject(); StartBuilding(); // Attaching a sapper to a teleporter automatically saps another end. if ( GetType() == OBJ_ATTACHMENT_SAPPER ) { CObjectTeleporter *pTeleporter = dynamic_cast<CObjectTeleporter *>( pParentObject ); if ( pTeleporter ) { CObjectTeleporter *pMatch = pTeleporter->GetMatchingTeleporter(); // If the other end is not already sapped then place a sapper on it. if ( pMatch && !pMatch->IsPlacing() && !pMatch->HasSapper() ) { SetCurrentState( BS_PLACING ); StartPlacement(); if ( m_hObjectBeingBuilt.Get() ) { m_hObjectBeingBuilt->UpdateAttachmentPlacement( pMatch ); StartBuilding(); } } } } // Should we switch away? if ( iFlags & OF_ALLOW_REPEAT_PLACEMENT ) { // Start placing another SetCurrentState( BS_PLACING ); StartPlacement(); } else { SwitchOwnersWeaponToLast(); } } } break; case BS_PLACING_INVALID: { if ( m_flNextDenySound < gpGlobals->curtime ) { CSingleUserRecipientFilter filter( pOwner ); EmitSound( filter, entindex(), "Player.DenyWeaponSelection" ); m_flNextDenySound = gpGlobals->curtime + 0.5; } } break; } m_flNextPrimaryAttack = gpGlobals->curtime + 0.2f; }
//----------------------------------------------------------------------------- // //----------------------------------------------------------------------------- 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; } }