static int CC_asw_teleport_autocomplete( char const *partial, char commands[ COMMAND_COMPLETION_MAXITEMS ][ COMMAND_COMPLETION_ITEM_LENGTH ] )
{
	if ( !g_pGameRules )
	{
		return 0;
	}

	char const *cmdname = "asw_teleport";

	char *substring = (char *)partial;
	if ( Q_strstr( partial, cmdname ) )
	{
		substring = (char *)partial + strlen( cmdname ) + 1;
	}

	int checklen = Q_strlen( substring );
	CUtlSymbolTable entries( 0, 0, true );
	CUtlVector< CUtlSymbol > symbols;

	CBaseEntity *pos = NULL;
	while ( ( pos = gEntList.NextEnt( pos ) ) != NULL )
	{
		// Check target name against partial string
		if ( pos->GetEntityName() == NULL_STRING )
			continue;

		if ( Q_strnicmp( STRING( pos->GetEntityName() ), substring, checklen ) )
			continue;

		CUtlSymbol sym = entries.AddString( STRING( pos->GetEntityName() ) );

		int idx = symbols.Find( sym );
		if ( idx == symbols.InvalidIndex() )
		{
			symbols.AddToTail( sym );
		}

		// Too many
		if ( symbols.Count() >= COMMAND_COMPLETION_MAXITEMS )
			break;
	}

	// Now fill in the results
	for ( int i = 0; i < symbols.Count(); i++ )
	{
		char const *name = entries.String( symbols[ i ] );

		char buf[ 512 ];
		Q_strncpy( buf, name, sizeof( buf ) );
		Q_strlower( buf );

		Q_snprintf( commands[ i ], COMMAND_COMPLETION_ITEM_LENGTH, "%s %s",
			cmdname, buf );
	}

	return symbols.Count();
}
Пример #2
0
//-----------------------------------------------------------------------------
// Purpose: Input handler that converts our target to a physics object.
//-----------------------------------------------------------------------------
void CPhysConvert::InputConvertTarget( inputdata_t &inputdata )
{
	bool createAsleep = HasSpawnFlags(SF_CONVERT_ASLEEP);
	// Fire output
	m_OnConvert.FireOutput( inputdata.pActivator, this );

	CBaseEntity *entlist[512];
	CBaseEntity *pSwap = gEntList.FindEntityByName( NULL, m_swapModel, inputdata.pActivator );
	CBaseEntity *pEntity = NULL;
	
	int count = 0;
	while ( (pEntity = gEntList.FindEntityByName( pEntity, m_target, inputdata.pActivator )) != NULL )
	{
		entlist[count++] = pEntity;
		if ( count >= ARRAYSIZE(entlist) )
			break;
	}

	// if we're swapping to model out, don't loop over more than one object
	// multiple objects with the same brush model will render, but the dynamic lights
	// and decals will be shared between the two instances...
	if ( pSwap && count > 0 )
	{
		count = 1;
	}

	for ( int i = 0; i < count; i++ )
	{
		pEntity = entlist[i];

		// don't convert something that is already physics based
		if ( pEntity->GetMoveType() == MOVETYPE_VPHYSICS )
		{
			Msg( "ERROR phys_convert %s ! Already MOVETYPE_VPHYSICS\n", STRING(pEntity->m_iClassname) );
			continue;
		}

		UnlinkFromParent( pEntity );

		if ( pSwap )
		{
			// we can't reuse this physics object, so kill it
			pEntity->VPhysicsDestroyObject();
			pEntity->SetModel( STRING(pSwap->GetModelName()) );
		}

		CBaseEntity *pPhys = CreateSimplePhysicsObject( pEntity, createAsleep );
		
		// created phys object, now move hierarchy over
		if ( pPhys )
		{
			pPhys->SetName( pEntity->GetEntityName() );
			TransferChildren( pEntity, pPhys );
			pEntity->AddSolidFlags( FSOLID_NOT_SOLID );
			pEntity->m_fEffects |= EF_NODRAW;
			UTIL_Relink( pEntity );
			UTIL_Remove( pEntity );
		}
	}
}
Пример #3
0
void CTFOClassnameFinder::FindTarget( inputdata_t &inputdata )
{
	bool bFound = false;
	CBaseEntity *pMyFoundEnt = NULL;

	// Try classname
	pMyFoundEnt = gEntList.FindEntityByClassname( NULL, szTarget.ToCStr() );
	while ( pMyFoundEnt )
	{
		if ( !strcmp( pMyFoundEnt->GetClassname(), szTarget.ToCStr() ) )
		{
			pFoundTarget.FireOutput( this, this );
			bFound = true;
			break;
		}

		pMyFoundEnt = gEntList.FindEntityByClassname( pMyFoundEnt, szTarget.ToCStr() );
	}

	if ( bFound ) 
		return;

	// Try the actual name
	pMyFoundEnt = gEntList.FindEntityByName( NULL, szTarget.ToCStr() );
	while ( pMyFoundEnt )
	{
		if ( !strcmp( pMyFoundEnt->GetEntityName().ToCStr(), szTarget.ToCStr() ) )
		{
			pFoundTarget.FireOutput( this, this );
			break;
		}

		pMyFoundEnt = gEntList.FindEntityByName( pMyFoundEnt, szTarget.ToCStr() );
	}
}
Пример #4
0
// Find a player with a case-insensitive name search.
static CBasePlayer* FindPlayerByName(const char *pTestName)
{
	for(int i=1; i <= gpGlobals->maxClients; i++)
	{
		edict_t *pEdict = engine->PEntityOfEntIndex(i);
		if(pEdict)
		{
			CBaseEntity *pEnt = CBaseEntity::Instance(pEdict);
			if(pEnt && pEnt->IsPlayer())
			{			
				const char *pNetName = STRING(pEnt->GetEntityName());
				if(stricmp(pNetName, pTestName) == 0)
				{
					return (CBasePlayer*)pEnt;
				}
			}
		}
	}

	return NULL;
}
Пример #5
0
	entityIndex = trace.m_pEnt ? (short)trace.m_pEnt->entindex() : 0;
	if ( entityIndex )
	{
		CBaseEntity *ent = trace.m_pEnt;
		if ( ent )
		{
			modelIndex = ent->GetModelIndex();
			VectorITransform( GetAbsOrigin(), ent->EntityToWorldTransform(), position );

			canDraw = ( modelIndex != 0 );
			if ( !canDraw )
			{
				Warning( "Suppressed StaticDecal which would have hit entity %i (class:%s, name:%s) with modelindex = 0\n",
					ent->entindex(),
					ent->GetClassname(),
					STRING( ent->GetEntityName() ) );
			}
		}
	}

	if ( canDraw )
	{
		engine->StaticDecal( position, m_nTexture, entityIndex, modelIndex, m_bLowPriority );
	}

	SUB_Remove();
}


bool CDecal::KeyValue( const char *szKeyName, const char *szValue )
{
Пример #6
0
//-----------------------------------------------------------------------------
// Purpose: We've hit a waypoint. Handle it, and return true if this is the
//			end of the path.
//-----------------------------------------------------------------------------
bool CNPC_VehicleDriver::WaypointReached( void )
{
	// We reached our current waypoint.
	m_vecPrevPrevPoint = m_vecPrevPoint;
	m_vecPrevPoint = GetAbsOrigin();

	// If we've got to our goal, we're done here.
	if ( GetNavigator()->CurWaypointIsGoal() )
	{
		// Necessary for InPass outputs to be fired, is a no-op otherwise
		GetNavigator()->AdvancePath();
	
		// Stop pathing
		ClearWaypoints();
		TaskComplete();
		SetGoalEnt( NULL );
		return true;
	}

	AI_Waypoint_t *pCurWaypoint = GetNavigator()->GetPath()->GetCurWaypoint();
	if ( !pCurWaypoint )
		return false;

	// Check to see if the waypoint wants us to change speed
	if ( pCurWaypoint->Flags() & bits_WP_TO_PATHCORNER )
	{
		CBaseEntity *pEntity = pCurWaypoint->hPathCorner;
		if ( pEntity )
		{
			if ( pEntity->m_flSpeed > 0 )
			{
				if ( pEntity->m_flSpeed <= 1.0 )
				{
					m_flDriversMaxSpeed = pEntity->m_flSpeed;
					RecalculateSpeeds();
				}
				else
				{
					Warning("path_track %s tried to tell the npc_vehicledriver to set speed to %.3f. npc_vehicledriver only accepts values between 0 and 1.\n", STRING(pEntity->GetEntityName()), pEntity->m_flSpeed );
				}
			}
		}
	}

	// Get the waypoints for the next part of the path
	GetNavigator()->AdvancePath();
	if ( !GetNavigator()->GetPath()->GetCurWaypoint() )
	{
		ClearWaypoints();
		TaskComplete();
		SetGoalEnt( NULL );
		return true;
	}

	m_vecDesiredPosition = GetNavigator()->GetCurWaypointPos();	
	CalculatePostPoints();

	// Move to the next waypoint
	delete m_pCurrentWaypoint;
	m_pCurrentWaypoint = m_pNextWaypoint;
	m_Waypoints[1] = new CVehicleWaypoint( m_vecPrevPoint, m_vecDesiredPosition, m_vecPostPoint, m_vecPostPostPoint );
	m_pNextWaypoint = m_Waypoints[1];

	// Drop the spline marker back
	m_flDistanceAlongSpline = MAX( 0, m_flDistanceAlongSpline - 1.0 );

	CheckForTeleport();

	return false;
}
Пример #7
0
void CFlexExpresser::InputSpeakResponseConcept( inputdata_t &inputdata )
{
	const char *pInputString = STRING(inputdata.value.StringID());
	// if no params, early out
	if (!pInputString || *pInputString == 0)
	{
		Warning( "empty SpeakResponse input from %s to %s\n", inputdata.pCaller->GetDebugName(), GetDebugName() );
		return;
	}

	char buf[512]; // temporary for tokenizing
	char outputmodifiers[512]; // eventual output to speak
	int outWritten = 0;
	V_strncpy(buf, pInputString, 510);
	buf[511] = 0; // just in case the last character is a comma -- enforce that the 
	// last character in the buffer is always a terminator.
	// special syntax allowing designers to submit inputs with contexts like
	// "concept,context1:value1,context2:value2,context3:value3"
	// except that entity i/o seems to eat commas these days (didn't used to be the case)
	// so instead of commas we have to use spaces in the entity IO, 
	// and turn them into commas here. AWESOME.
	char *pModifiers = const_cast<char *>(V_strnchr(buf, ' ', 510));
	if ( pModifiers )
	{
		*pModifiers = 0;
		++pModifiers;

		// tokenize on spaces
		char *token = strtok(pModifiers, " ");
		while (token)
		{
			// find the start characters for the key and value
			// (seperated by a : which we replace with null)
			char * RESTRICT key = token;
			char * RESTRICT colon = const_cast<char *>(V_strnchr(key, ':', 510)); 
			char * RESTRICT value;
			if (!colon)
			{
				Warning( "faulty context k:v pair in entity io %s\n", pInputString );
				break;
			}

			// write the key and colon to the output string
			int toWrite = colon - key + 1;
			if ( outWritten + toWrite >= 512 )
			{
				Warning( "Speak input to %s had overlong parameter %s", GetDebugName(), pInputString );
				return;
			}
			memcpy(outputmodifiers + outWritten, key, toWrite);
			outWritten += toWrite;

			*colon = 0;
			value = colon + 1;

			// determine if the value is actually a procedural name
			CBaseEntity *pProcedural = gEntList.FindEntityProcedural( value, this, inputdata.pActivator, inputdata.pCaller );

			// write the value to the output -- if it's a procedural name, replace appropriately; 
			// if not, just copy over.
			const char *valString; 
			if (pProcedural)
			{
					valString = STRING(pProcedural->GetEntityName());
				}
			else
			{
				valString = value;
			}
			toWrite = strlen(valString);
			toWrite = MIN( 511-outWritten, toWrite );
			V_strncpy( outputmodifiers + outWritten, valString, toWrite+1 );
			outWritten += toWrite;

			// get the next token
			token = strtok(NULL, " ");
			if (token)
			{
				// if there is a next token, write in a comma
				if (outWritten < 511)
				{
					outputmodifiers[outWritten++]=',';
				}
			}
		}
	}

	// null terminate just in case
	outputmodifiers[outWritten] = 0;

	Speak( buf, outWritten > 0 ? outputmodifiers : NULL );
}
Пример #8
0
	void DrawOverlays(CTFBotMissionSuicideBomber *action, CTFBot *actor)
	{
		constexpr float dt = 0.1f;
		
		constexpr float target_cross_size = 5.0f;
		constexpr int   target_cross_r    = 0x00;
		constexpr int   target_cross_g    = 0xff;
		constexpr int   target_cross_b    = 0x00;
		
		constexpr float det_cross_size = 5.0f;
		constexpr int   det_cross_r    = 0xff;
		constexpr int   det_cross_g    = 0x00;
		constexpr int   det_cross_b    = 0x00;
		
		
		int line;
		char buf[1024];
		
		
		/* OVERLAYS: BOT */
		line = 0;
		
		auto pos = actor->GetAbsOrigin();
		snprintf(buf, sizeof(buf), "POS: (%4.0f, %4.0f, %4.0f)", pos.x, pos.y, pos.z);
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		snprintf(buf, sizeof(buf), "HEALTH: %d", actor->GetHealth());
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		CBaseEntity *target = action->m_hTarget;
		if (target == nullptr) {
			snprintf(buf, sizeof(buf), "TARGET: null");
		} else {
			const char *name;
			if (target->IsPlayer()) {
				name = static_cast<CBasePlayer *>(target)->GetPlayerName();
			} else {
				name = STRING(target->GetEntityName());
			}
			snprintf(buf, sizeof(buf), "TARGET: #%d '%s' '%s'",
				ENTINDEX(target), target->GetClassname(), name);
		}
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		snprintf(buf, sizeof(buf), "PATH FAILS: %d", action->m_nConsecutivePathFailures);
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		if (action->m_ctRecomputePath.HasStarted()) {
			snprintf(buf, sizeof(buf), "TIMER(path): %.1f / %.1f",
				action->m_ctRecomputePath.GetElapsedTime(),
				action->m_ctRecomputePath.GetCountdownDuration());
		} else {
			snprintf(buf, sizeof(buf), "TIMER(path): NOT STARTED");
		}
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		if (action->m_ctPlaySound.HasStarted()) {
			snprintf(buf, sizeof(buf), "TIMER(sound): %.1f / %.1f",
				action->m_ctPlaySound.GetElapsedTime(),
				action->m_ctPlaySound.GetCountdownDuration());
		} else {
			snprintf(buf, sizeof(buf), "TIMER(sound): NOT STARTED");
		}
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		if (action->m_ctDetonation.HasStarted()) {
			snprintf(buf, sizeof(buf), "TIMER(det): %.1f / %.1f",
				action->m_ctDetonation.GetElapsedTime(),
				action->m_ctDetonation.GetCountdownDuration());
		} else {
			snprintf(buf, sizeof(buf), "TIMER(det): NOT STARTED");
		}
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		snprintf(buf, sizeof(buf), "DETONATING: %s",
			(action->m_bDetonating ? "yes" : "no"));
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		snprintf(buf, sizeof(buf), "DET(reached_goal): %s",
			(action->m_bDetReachedGoal ? "yes" : "no"));
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		snprintf(buf, sizeof(buf), "DET(lost_all_health): %s",
			(action->m_bDetLostAllHealth ? "yes" : "no"));
		NDebugOverlay::EntityText(ENTINDEX(actor), line++, buf, dt, 255, 255, 255, 255);
		
		ConVarRef tf_bot_suicide_bomb_range("tf_bot_suicide_bomb_range");
		float det_range = tf_bot_suicide_bomb_range.GetFloat() / 3.0f;
		
		int det_sphere_r = 0xff;
		int det_sphere_g = 0x00;
		int det_sphere_b = 0x00;
		int det_sphere_a = 0x00;
		
		if (action->m_vecTargetPos.DistToSqr(actor->GetAbsOrigin()) < det_range * det_range) {
			Vector vec_clear = action->m_vecTargetPos;
			vec_clear.z += 18.0f;
			
			if (actor->IsLineOfFireClear(vec_clear)) {
				det_sphere_r = 0x00;
				det_sphere_g = 0xff;
				det_sphere_b = 0x00;
			} else {
				det_sphere_r = 0xff;
				det_sphere_g = 0xff;
				det_sphere_b = 0x00;
			}
		}
		
		NDebugOverlay::Sphere(actor->GetAbsOrigin(), vec3_angle, det_range,
			det_sphere_r, det_sphere_g, det_sphere_b, det_sphere_a, true, dt);
		
		
		/* OVERLAYS: TARGET POS */
		line = 0;
		
		snprintf(buf, sizeof(buf), "TARGET POS: (%4.0f, %4.0f, %4.0f)",
			action->m_vecTargetPos.x, action->m_vecTargetPos.y, action->m_vecTargetPos.z);
		NDebugOverlay::EntityTextAtPosition(action->m_vecTargetPos, line++, buf, dt, 255, 255, 255, 255);
		
		snprintf(buf, sizeof(buf), "DIST TO: %.1f", action->m_vecTargetPos.DistToSqr(actor->GetAbsOrigin()));
		
		NDebugOverlay::Cross3D(action->m_vecTargetPos,
			target_cross_size, target_cross_r, target_cross_g, target_cross_b, true, dt);
		
		
		/* OVERLAYS: DETONATE POS */
		line = 0;
		
		snprintf(buf, sizeof(buf), "DET POS: (%4.0f, %4.0f, %4.0f)",
			action->m_vecDetonatePos.x, action->m_vecDetonatePos.y, action->m_vecDetonatePos.z);
		NDebugOverlay::EntityTextAtPosition(action->m_vecDetonatePos, line++, buf, dt, 255, 255, 255, 255);
		
		NDebugOverlay::Cross3D(action->m_vecDetonatePos,
			det_cross_size, det_cross_r, det_cross_g, det_cross_b, true, dt);
	}
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void CAI_SpeechFilter::PopulateSubjectList( bool purge )
{
	// Populate the subject list. Try targetname first.
	CBaseEntity *pSearch = NULL;
	int iNumSubjects = 0;
	do
	{
		pSearch = gEntList.FindEntityByName( pSearch, m_iszSubject );
		if ( pSearch )
		{
#ifndef CSTRIKE_DLL
			CAI_PlayerAlly *pAlly = dynamic_cast<CAI_PlayerAlly *>(pSearch);
			if ( pAlly )
			{
				if( purge )
				{
					pAlly->SetSpeechFilter( NULL );
				}
				else
				{
					if( pAlly->GetSpeechFilter() != NULL )
					{
						DevWarning("ai_speechfilter %s is slamming NPC %s's current speech filter.\n", STRING(GetEntityName()), STRING(pSearch->GetEntityName()) );
					}

					pAlly->SetSpeechFilter( this );
				}
			}
			else if ( pSearch->IsNPC() )
			{
				DevWarning("ai_speechfilter %s tries to use %s as a subject, but it's not a talking NPC.\n", STRING(GetEntityName()), STRING(pSearch->GetEntityName()) );
			}
#endif
			iNumSubjects++;
		}
	} while( pSearch );

	if ( !iNumSubjects )
	{
		// No subjects found by targetname! Assume classname.
		do
		{
			pSearch = gEntList.FindEntityByClassname( pSearch, STRING(m_iszSubject) );
			if( pSearch )
			{
#ifndef CSTRIKE_DLL
				CAI_PlayerAlly *pAlly = dynamic_cast<CAI_PlayerAlly *>(pSearch);
				if ( pAlly )
				{
					if( purge )
					{
						pAlly->SetSpeechFilter( NULL );
					}
					else
					{
						if( pAlly->GetSpeechFilter() != NULL )
						{
							DevWarning("ai_speechfilter %s is slamming NPC %s's current speech filter.\n", STRING(GetEntityName()), STRING(pSearch->GetEntityName()) );
						}

						pAlly->SetSpeechFilter( this );
					}
				}
				else if ( pSearch->IsNPC() )
				{
					DevWarning("ai_speechfilter %s tries to use %s as a subject, but it's not a talking NPC.\n", STRING(GetEntityName()), STRING(pSearch->GetEntityName()) );
				}
#endif
				iNumSubjects++;
			}
		} while( pSearch );
	}

	// If the subject list is still empty, we have an error!
	if ( !iNumSubjects )
	{
		DevMsg( 2, "ai_speechfilter finds no subject(s) called: %s\n", STRING( m_iszSubject ) );
	}
}
Пример #10
0
void CASW_Arena::SpawnArenaWave()
{
	if ( !ASWSpawnManager() )
		return;

	if ( ASWGameRules() )
	{
		ASWGameRules()->BroadcastSound( "Spawner.Horde" );
	}

	// find the 4 corridor spawn points
	CUtlVector<CBaseEntity*> arenaSpawns[4];
	int arenaSpawnsUsed[4];
	memset( arenaSpawnsUsed, 0, sizeof( arenaSpawnsUsed ) );

	CBaseEntity* pEntity = NULL;
	while ((pEntity = gEntList.FindEntityByClassname( pEntity, "info_target" )) != NULL)
	{
		if ( !stricmp( STRING( pEntity->GetEntityName() ), "Spawn_Front" ) )
		{
			arenaSpawns[0].AddToTail( pEntity );
		}
		else if ( !stricmp( STRING( pEntity->GetEntityName() ), "Spawn_Right" ) )
		{
			arenaSpawns[1].AddToTail( pEntity );
		}
		else if ( !stricmp( STRING( pEntity->GetEntityName() ), "Spawn_Below" ) )
		{
			arenaSpawns[2].AddToTail( pEntity );
		}
		else if ( !stricmp( STRING( pEntity->GetEntityName() ), "Spawn_Left" ) )
		{
			arenaSpawns[3].AddToTail( pEntity );
		}
	}
	Msg( "Found arena spawns: N:%d E:%d S:%d W:%d\n", arenaSpawns[0].Count(), arenaSpawns[1].Count(), arenaSpawns[2].Count(), arenaSpawns[3].Count() );

	// decide how many alien types we're going to spawn
	int iAlienTypes = 2;
	float fRandom = RandomFloat();
	if ( fRandom < 0.1f )
	{
		iAlienTypes = 4;
	}
	else if ( fRandom < 0.30f )
	{
		iAlienTypes = 3;
	}

	for ( int i=0 ; i<iAlienTypes; i++ )
	{
		// decide on a direction
		int iDirection = RandomInt( 0, 3 );
		// decide on an alien type
		int iAlienType = RandomInt( 0, m_ArenaAliens.Count() - 1 );
		int iQuantity = asw_arena_quantity_scale.GetFloat() * RandomInt( m_ArenaAliens[iAlienType]->m_iQuantityMin, m_ArenaAliens[iAlienType]->m_iQuantityMax );
		int iArenaLevel = m_iArenaWave / asw_arena_waves_per_difficulty.GetInt();
		iQuantity += iArenaLevel;

		for ( int k=0 ; k < iQuantity ; k++ )
		{
			if ( arenaSpawnsUsed[iDirection] < arenaSpawns[iDirection].Count() )
			{
				CBaseEntity *pSpawnPoint = arenaSpawns[iDirection][arenaSpawnsUsed[iDirection]];
				if ( !pSpawnPoint )
					continue;

				if ( ASWSpawnManager()->SpawnAlienAt( m_ArenaAliens[iAlienType]->m_szAlienClass, pSpawnPoint->GetAbsOrigin(), pSpawnPoint->GetAbsAngles() ) )
				{
					arenaSpawnsUsed[iDirection]++;
				}
			}
		}
	}

	m_iArenaWave++;
}
Пример #11
0
//---------------------------------------------------------
// Purpose: See if it is time to poll and do so. 
//
// SOON: We need to load-balance this. 
//---------------------------------------------------------
void CVisibilityMonitor::FrameUpdatePostEntityThink()
{
	if( gpGlobals->curtime < m_flTimeNextPoll )
		return;

	m_flTimeNextPoll = gpGlobals->curtime + vismon_poll_frequency.GetFloat();

	int iDebugging = debug_visibility_monitor.GetInt();

	if( m_Entities.Count() > m_iMaxEntitiesPerThink )
		m_iMaxEntitiesPerThink = m_Entities.Count();

	if( iDebugging > 1 )
	{
		Msg("\nVisMon: Polling now. (Frequency: %f)\n", m_flPollFrequency );
		Msg("VisMon: Time: %f - Tracking %d Entities. (Max:%d)\n", gpGlobals->curtime, m_Entities.Count(), m_iMaxEntitiesPerThink );
	}

	// Cleanup, dump entities that have been removed since we last polled.
	for ( int i = 0 ; i < m_Entities.Count() ; i++ )
	{
		if ( m_Entities[i].entity == NULL )
		{
			m_Entities.FastRemove(i);
			if ( i >= m_Entities.Count() )
			{
				break;
			}
		}
	}

	int numTraces = 0;
	bool bHitTraceLimit = false;

	if( m_iStartElement >= m_Entities.Count() )
	{
		if( iDebugging > 1 )
		{
			Msg("VisMon: RESET\n");
		}

		m_iStartElement = 0;
	}

	if( iDebugging > 1 )
	{
		Msg("VisMon: Starting at element: %d\n", m_iStartElement );
	}

	for( int i = m_iStartElement ; i < m_Entities.Count() ; i++ )
	{
		for( int j = 1 ; j <= gpGlobals->maxClients ; j++ )
		{
			CBasePlayer *pPlayer =UTIL_PlayerByIndex( j );

			if( pPlayer != NULL && pPlayer->IsAlive() && !pPlayer->IsBot() )
			{
				int memoryBit = 1 << j; // The bit that is used to remember whether a given entity has been seen by a given player.

				CBaseEntity *pSeeEntity = m_Entities[ i ].entity.Get();

				if( pSeeEntity == NULL )
				{
					continue;
				}

				if( !(m_Entities[i].memory & memoryBit) )
				{
					// If this player hasn't seen this entity yet, check it.
					if( EntityIsVisibleToPlayer( m_Entities[i], pPlayer, &numTraces ) )
					{
						bool bIgnore = false;

						if( m_Entities[i].pfnEvaluator != NULL && !m_Entities[i].pfnEvaluator( m_Entities[i].entity, pPlayer ) )
						{
							bIgnore = true;
						}

						// See it! Generate our event.
						if( iDebugging > 0 )
						{
							if( bIgnore )
							{
								Msg("VisMon: Player %s IGNORING VISIBILE Entity: %s\n", pPlayer->GetDebugName(), pSeeEntity->GetDebugName() );
								NDebugOverlay::Cross3D( pSeeEntity->WorldSpaceCenter(), 16, 255, 0, 0, false, 10.0f );
							}
							else
							{
								Msg("VisMon: Player %s sees Entity: %s\n", pPlayer->GetDebugName(), pSeeEntity->GetDebugName() );
								NDebugOverlay::Cross3D( pSeeEntity->WorldSpaceCenter(), 16, 0, 255, 0, false, 10.0f );
							}
						}

						if( !bIgnore )
						{
							bool bGenerateEvent = true;

							if( m_Entities[i].pfnCallback != NULL )
							{
								// Make the callback, and let it determine whether to generate the simple event.
								bGenerateEvent = m_Entities[i].pfnCallback( m_Entities[i].entity, pPlayer );
							}

							if( bGenerateEvent )
							{
								// No callback, generate the generic game event.
								IGameEvent * event = gameeventmanager->CreateEvent( "entity_visible" );
								if ( event )
								{
									event->SetInt( "userid", pPlayer->GetUserID() );
									event->SetInt( "subject", pSeeEntity->entindex() );
									event->SetString( "classname", pSeeEntity->GetClassname() );
									event->SetString( "entityname", STRING( pSeeEntity->GetEntityName() ) );
									gameeventmanager->FireEvent( event );
								}
							}

							// Remember that this entity was visible to the player
							m_Entities[i].memory |= memoryBit;
						}
					}
				}
			}
		}

		if( numTraces >= vismon_trace_limit.GetInt() )
		{
			if( iDebugging > 1 )
			{
				Msg("VisMon: MAX Traces. Stopping after element %d\n", i );
			}

			m_iStartElement = i + 1; // Pick up here next think.
			bHitTraceLimit = true;
			break;
		}
	}

	if( !bHitTraceLimit )
	{
		m_iStartElement = 0;
	}

	if( numTraces > m_iMaxTracesPerThink )
		m_iMaxTracesPerThink = numTraces;

	if( iDebugging > 1 )
	{
		Msg("VisMon: %d traces performed during this polling cycle (Max: %d)\n\n", numTraces, m_iMaxTracesPerThink );
	}
}