//----------------------------------------------------------------------------- // Purpose: Finds an entity given a procedural name. // Input : szName - The procedural name to search for, should start with '!'. // pSearchingEntity - // pActivator - The activator entity if this was called from an input // or Use handler. //----------------------------------------------------------------------------- CBaseEntity *CGlobalEntityList::FindEntityProcedural( const char *szName, CBaseEntity *pSearchingEntity, CBaseEntity *pActivator, CBaseEntity *pCaller ) { // // Check for the name escape character. // if ( szName[0] == '!' ) { const char *pName = szName + 1; // // It is a procedural name, look for the ones we understand. // if ( FStrEq( pName, "player" ) ) { return (CBaseEntity *)UTIL_PlayerByIndex( 1 ); } else if ( FStrEq( pName, "pvsplayer" ) ) { if ( pSearchingEntity ) { return CBaseEntity::Instance( UTIL_FindClientInPVS( pSearchingEntity->edict() ) ); } else if ( pActivator ) { // FIXME: error condition? return CBaseEntity::Instance( UTIL_FindClientInPVS( pActivator->edict() ) ); } else { // FIXME: error condition? return (CBaseEntity *)UTIL_PlayerByIndex( 1 ); } } else if ( FStrEq( pName, "activator" ) ) { return pActivator; } else if ( FStrEq( pName, "caller" ) ) { return pCaller; } else if ( FStrEq( pName, "picker" ) ) { return FindPickerEntity( UTIL_PlayerByIndex(1) ); } else if ( FStrEq( pName, "self" ) ) { return pSearchingEntity; } else { Warning( "Invalid entity search name %s\n", szName ); Assert(0); } } return NULL; }
bool CAI_ScriptConditions::ActorInPlayersPVS( CBaseEntity *pActor, bool bNot ) { if ( pActor == NULL ) return true; bool bInPVS = !!UTIL_FindClientInPVS( pActor->edict()); if ( bInPVS ) { if( bNot ) { return false; } else { return true; } } else { if( bNot ) { return true; } else { return false; } } }
bool ShouldRemoveThisRagdoll( CBaseAnimating *pRagdoll ) { if ( g_RagdollLVManager.IsLowViolence() ) { return true; } #ifdef CLIENT_DLL /* we no longer ignore enemies just because they are on fire -- a ragdoll in front of me is always a higher priority for retention than a flaming zombie behind me. At the time I put this in, the ragdolls do clean up their own effects if culled via SUB_Remove(). If you're encountering trouble with ragdolls leaving effects behind, try renabling the code below. ///////////////////// //Just ignore it until we're done burning/dissolving. if ( pRagdoll->GetEffectEntity() ) return false; */ Vector vMins, vMaxs; Vector origin = pRagdoll->m_pRagdoll->GetRagdollOrigin(); pRagdoll->m_pRagdoll->GetRagdollBounds( vMins, vMaxs ); if( engine->IsBoxInViewCluster( vMins + origin, vMaxs + origin) == false ) { if ( g_debug_ragdoll_removal.GetBool() ) { debugoverlay->AddBoxOverlay( origin, vMins, vMaxs, QAngle( 0, 0, 0 ), 0, 255, 0, 16, 5 ); debugoverlay->AddLineOverlay( origin, origin + Vector( 0, 0, 64 ), 0, 255, 0, true, 5 ); } return true; } else if( engine->CullBox( vMins + origin, vMaxs + origin ) == true ) { if ( g_debug_ragdoll_removal.GetBool() ) { debugoverlay->AddBoxOverlay( origin, vMins, vMaxs, QAngle( 0, 0, 0 ), 0, 0, 255, 16, 5 ); debugoverlay->AddLineOverlay( origin, origin + Vector( 0, 0, 64 ), 0, 0, 255, true, 5 ); } return true; } #else //CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); if( !UTIL_FindClientInPVS( pRagdoll->edict() ) ) { if ( g_debug_ragdoll_removal.GetBool() ) NDebugOverlay::Line( pRagdoll->GetAbsOrigin(), pRagdoll->GetAbsOrigin() + Vector( 0, 0, 64 ), 0, 255, 0, true, 5 ); return true; } #endif return false; }
bool CTalkMonster::FOkToSpeak() const { // if in the grip of a barnacle, don't speak if ( m_MonsterState == MONSTERSTATE_PRONE || m_IdealMonsterState == MONSTERSTATE_PRONE ) { return false; } // if not alive, certainly don't speak if ( GetDeadFlag() != DEAD_NO ) { return false; } // if someone else is talking, don't speak if (gpGlobals->time <= CTalkMonster::g_talkWaitTime) return false; if ( GetSpawnFlags().Any( SF_MONSTER_GAG ) ) return false; // if player is not in pvs, don't speak if( !IsAlive() || !UTIL_FindClientInPVS( this ) ) return false; // don't talk if you're in combat if (m_hEnemy != NULL && FVisible( m_hEnemy )) return false; return true; }
//--------------------------------------------------------- //--------------------------------------------------------- void CNPC_GroundTurret::GatherConditions() { if( !IsEnabled() ) { return; } if( !IsOpen() && !UTIL_FindClientInPVS( edict() ) ) { return; } // Throw away old enemies so the turret can retire AIEnemiesIter_t iter; for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst(&iter); pEMemory != NULL; pEMemory = GetEnemies()->GetNext(&iter) ) { if( pEMemory->timeLastSeen < gpGlobals->curtime - GROUNDTURRET_RETIRE_TIME ) { pEMemory->hEnemy = NULL; } } BaseClass::GatherConditions(); if( GetEnemy() && HasCondition(COND_SEE_ENEMY) ) { m_flTimeLastSawEnemy = gpGlobals->curtime; } else { if( gpGlobals->curtime - m_flTimeLastSawEnemy >= GROUNDTURRET_RETIRE_TIME ) { m_OnAreaClear.FireOutput(this, this); m_flTimeLastSawEnemy = FLT_MAX; return; } } if( HasCondition( COND_SEE_ENEMY ) ) { m_bSeeEnemy = true; } else { m_bSeeEnemy = false; } if( GetEnemy() && m_bSeeEnemy && IsEnabled() ) { if( m_flTimeNextShoot < gpGlobals->curtime ) { Shoot(); } } }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_GroundTurret::PrescheduleThink() { if( UTIL_FindClientInPVS(edict()) ) { SetNextThink( gpGlobals->curtime + 0.03f ); } else { SetNextThink( gpGlobals->curtime + 0.1f ); } }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_AllyManager::CountAllies( int *pTotal, int *pMedics ) { (*pTotal) = (*pMedics) = 0; if ( !AI_IsSinglePlayer() ) { // @TODO (toml 10-22-04): no MP support right now return; } const Vector & vPlayerPos = UTIL_GetLocalPlayer()->GetAbsOrigin(); CAI_BaseNPC ** ppAIs = g_AI_Manager.AccessAIs(); int nAIs = g_AI_Manager.NumAIs(); for ( int i = 0; i < nAIs; i++ ) { if ( ppAIs[i]->IsAlive() && ppAIs[i]->IsPlayerAlly() ) { // Vital allies do not count. if( ppAIs[i]->Classify() == CLASS_PLAYER_ALLY_VITAL ) continue; // They only count if I can use them. if( ppAIs[i]->HasSpawnFlags(SF_CITIZEN_NOT_COMMANDABLE) ) continue; // They only count if I can use them. if( ppAIs[i]->IRelationType( UTIL_GetLocalPlayer() ) != D_LI ) continue; // Skip distant NPCs if ( !ppAIs[i]->IsInPlayerSquad() && !UTIL_FindClientInPVS( ppAIs[i]->edict() ) && ( ( ppAIs[i]->GetAbsOrigin() - vPlayerPos ).LengthSqr() > 150*12 || fabsf( ppAIs[i]->GetAbsOrigin().z - vPlayerPos.z ) > 192 ) ) continue; if( FClassnameIs( ppAIs[i], "npc_citizen" ) ) { CNPC_Citizen *pCitizen = assert_cast<CNPC_Citizen *>(ppAIs[i]); if ( !pCitizen->CanJoinPlayerSquad() ) continue; if ( pCitizen->WasInPlayerSquad() && !pCitizen->IsInPlayerSquad() ) continue; if ( ppAIs[i]->HasSpawnFlags( SF_CITIZEN_MEDIC ) ) (*pMedics)++; } (*pTotal)++; } } }
void CGib::SpawnHeadGib( CBaseEntity *pVictim ) { CGib *pGib = CREATE_ENTITY( CGib, "gib" ); //City17: Germany Violence Fix. /*if ( g_Language.GetInt() == LANGUAGE_GERMAN ) { pGib->Spawn( "models/germangibs.mdl" );// throw one head pGib->m_nBody = 0; } else {*/ pGib->Spawn( "models/gibs/hgibs.mdl" );// throw one head pGib->m_nBody = 0; //} if ( pVictim ) { Vector vecNewVelocity = pGib->GetAbsVelocity(); pGib->SetLocalOrigin( pVictim->EyePosition() ); edict_t *pentPlayer = UTIL_FindClientInPVS( pGib->edict() ); if ( random->RandomInt ( 0, 100 ) <= 5 && pentPlayer ) { // 5% chance head will be thrown at player's face. CBasePlayer *player = (CBasePlayer *)CBaseEntity::Instance( pentPlayer ); if ( player ) { vecNewVelocity = ( player->EyePosition() ) - pGib->GetAbsOrigin(); VectorNormalize(vecNewVelocity); vecNewVelocity *= 300; vecNewVelocity.z += 100; } } else { vecNewVelocity = Vector (random->RandomFloat(-100,100), random->RandomFloat(-100,100), random->RandomFloat(200,300)); } QAngle vecNewAngularVelocity = pGib->GetLocalAngularVelocity(); vecNewAngularVelocity.x = random->RandomFloat ( 100, 200 ); vecNewAngularVelocity.y = random->RandomFloat ( 100, 300 ); pGib->SetLocalAngularVelocity( vecNewAngularVelocity ); // copy owner's blood color pGib->SetBloodColor( pVictim->BloodColor() ); pGib->AdjustVelocityBasedOnHealth( pVictim->m_iHealth, vecNewVelocity ); pGib->SetAbsVelocity( vecNewVelocity ); } pGib->LimitVelocity(); }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_GroundTurret::Scan() { if( m_bSeeEnemy ) { // Using a bool for this check because the condition gets wiped out by changing schedules. return; } if( IsOpeningOrClosing() ) { // Moving. return; } if( !IsOpen() ) { // Closed return; } if( !UTIL_FindClientInPVS(edict()) ) { return; } if( gpGlobals->curtime >= m_flTimeNextPing ) { EmitSound( "NPC_FloorTurret.Ping" ); m_flTimeNextPing = gpGlobals->curtime + 1.0f; } QAngle scanAngle; Vector forward; Vector vecEye = GetAbsOrigin() + m_vecLightOffset; // Draw the outer extents scanAngle = GetAbsAngles(); scanAngle.y += (GROUNDTURRET_VIEWCONE / 2.0f); AngleVectors( scanAngle, &forward, NULL, NULL ); ProjectBeam( vecEye, forward, 1, 30, 0.1 ); scanAngle = GetAbsAngles(); scanAngle.y -= (GROUNDTURRET_VIEWCONE / 2.0f); AngleVectors( scanAngle, &forward, NULL, NULL ); ProjectBeam( vecEye, forward, 1, 30, 0.1 ); // Draw a sweeping beam scanAngle = GetAbsAngles(); scanAngle.y += (GROUNDTURRET_VIEWCONE / 2.0f) * sin( gpGlobals->curtime * 3.0f ); AngleVectors( scanAngle, &forward, NULL, NULL ); ProjectBeam( vecEye, forward, 1, 30, 0.3 ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItem_DynamicResupply::CheckPVSThink( void ) { edict_t *pentPlayer = UTIL_FindClientInPVS( edict() ); if ( pentPlayer ) { CBasePlayer *pPlayer = (CBasePlayer *)CBaseEntity::Instance( pentPlayer ); if ( pPlayer ) { SpawnDynamicItem( pPlayer ); return; } } SetNextThink( gpGlobals->curtime + DYNAMIC_ITEM_THINK ); }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ bool CAI_PlayerAlly::IsOkToCombatSpeak( void ) { // if not alive, certainly don't speak if ( !IsAlive() ) return false; // if someone else is talking, don't speak if ( !GetExpresser()->SemaphoreIsAvailable() ) return false; if ( m_spawnflags & SF_NPC_GAG ) return false; // Don't speak if playing a script. if ( m_NPCState == NPC_STATE_SCRIPT ) return false; // if player is not in pvs, don't speak if ( !UTIL_FindClientInPVS(edict()) ) return false; return true; }
void CFuncTank::TrackTarget( void ) { trace_t tr; bool updateTime = FALSE, lineOfSight; QAngle angles; Vector barrelEnd; CBaseEntity *pTarget = NULL; barrelEnd.Init(); // Get a position to aim for if (m_pController) { // Tanks attempt to mirror the player's angles angles = m_pController->EyeAngles(); SetNextThink( gpGlobals->curtime + 0.05 ); } else { if ( IsActive() ) { SetNextThink( gpGlobals->curtime + 0.1f ); } else { return; } // ----------------------------------- // Get world target position // ----------------------------------- barrelEnd = WorldBarrelPosition(); Vector worldTargetPosition; if (m_spawnflags & SF_TANK_AIM_AT_POS) { worldTargetPosition = m_vTargetPosition; } else { CBaseEntity *pEntity = (CBaseEntity *)m_hTarget; if ( !pEntity || ( pEntity->GetFlags() & FL_NOTARGET ) ) { if ( m_targetEntityName != NULL_STRING ) // New HL2 behavior { m_hTarget = FindTarget( m_targetEntityName, NULL ); } else // HL1 style { m_hTarget = ToBasePlayer( GetContainingEntity( UTIL_FindClientInPVS( edict() ) ) ); } if ( m_hTarget != NULL ) { SetNextThink( gpGlobals->curtime ); // Think again immediately } else { if ( IsActive() ) { SetNextThink( gpGlobals->curtime + 2 ); // Wait 2 secs } if ( m_fireLast !=0 ) { m_OnLoseTarget.FireOutput(this, this); m_fireLast = 0; } } return; } pTarget = pEntity; // Calculate angle needed to aim at target worldTargetPosition = pEntity->EyePosition(); } float range = (worldTargetPosition - barrelEnd).Length(); if ( !InRange( range ) ) { m_fireLast = 0; return; } UTIL_TraceLine( barrelEnd, worldTargetPosition, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if (m_spawnflags & SF_TANK_AIM_AT_POS) { updateTime = TRUE; m_sightOrigin = m_vTargetPosition; } else { lineOfSight = FALSE; // No line of sight, don't track if ( tr.fraction == 1.0 || tr.m_pEnt == pTarget ) { lineOfSight = TRUE; CBaseEntity *pInstance = pTarget; if ( InRange( range ) && pInstance && pInstance->IsAlive() ) { updateTime = TRUE; // Sight position is BodyTarget with no noise (so gun doesn't bob up and down) m_sightOrigin = pInstance->BodyTarget( GetLocalOrigin(), false ); } } } // Convert targetPosition to parent angles = AimBarrelAt( m_parentMatrix.WorldToLocal( m_sightOrigin ) ); } // Force the angles to be relative to the center position float offsetY = UTIL_AngleDistance( angles.y, m_yawCenter ); float offsetX = UTIL_AngleDistance( angles.x, m_pitchCenter ); angles.y = m_yawCenter + offsetY; angles.x = m_pitchCenter + offsetX; // Limit against range in y // MDB - don't check pitch! If two func_tanks are meant to align, // and one can pitch and the other cannot, this can lead to them getting // different values for angles.y. Nothing is lost by not updating yaw // because the target is not in pitch range. bool bOutsideYawRange = ( fabs( offsetY ) > m_yawRange + m_yawTolerance ); bool bOutsidePitchRange = ( fabs( offsetX ) > m_pitchRange + m_pitchTolerance ); Vector vecToTarget = m_sightOrigin - GetLocalOrigin(); // if target is outside yaw range if ( bOutsideYawRange ) { if ( angles.y > m_yawCenter + m_yawRange ) { angles.y = m_yawCenter + m_yawRange; } else if ( angles.y < (m_yawCenter - m_yawRange) ) { angles.y = (m_yawCenter - m_yawRange); } } if ( bOutsidePitchRange || bOutsideYawRange || ( vecToTarget.Length() < ( barrelEnd - GetAbsOrigin() ).Length() ) ) { // Don't update if you saw the player, but out of range updateTime = false; } if ( updateTime ) { m_lastSightTime = gpGlobals->curtime; m_persist2burst = 0; } // Move toward target at rate or less float distY = UTIL_AngleDistance( angles.y, GetLocalAngles().y ); QAngle vecAngVel = GetLocalAngularVelocity(); vecAngVel.y = distY * 10; vecAngVel.y = clamp( vecAngVel.y, -m_yawRate, m_yawRate ); // Limit against range in x angles.x = clamp( angles.x, m_pitchCenter - m_pitchRange, m_pitchCenter + m_pitchRange ); // Move toward target at rate or less float distX = UTIL_AngleDistance( angles.x, GetLocalAngles().x ); vecAngVel.x = distX * 10; vecAngVel.x = clamp( vecAngVel.x, -m_pitchRate, m_pitchRate ); SetLocalAngularVelocity( vecAngVel ); SetMoveDoneTime( 0.1 ); if ( m_pController ) return; if ( CanFire() && ( (fabs(distX) < m_pitchTolerance && fabs(distY) < m_yawTolerance) || (m_spawnflags & SF_TANK_LINEOFSIGHT) ) ) { bool fire = FALSE; Vector forward; AngleVectors( GetLocalAngles(), &forward ); forward = m_parentMatrix.ApplyRotation( forward ); if ( m_spawnflags & SF_TANK_LINEOFSIGHT ) { float length = (m_maxRange > 0) ? m_maxRange : MAX_TRACE_LENGTH; UTIL_TraceLine( barrelEnd, barrelEnd + forward * length, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if ( tr.m_pEnt == pTarget ) fire = TRUE; } else fire = TRUE; if ( fire ) { if (m_fireLast == 0) { m_OnAquireTarget.FireOutput(this, this); } FiringSequence( barrelEnd, forward, this ); } else { if (m_fireLast !=0) { m_OnLoseTarget.FireOutput(this, this); } m_fireLast = 0; } } else { if (m_fireLast !=0) { m_OnLoseTarget.FireOutput(this, this); } m_fireLast = 0; } }
void CEnvSound::Think( void ) { // get pointer to client if visible; UTIL_FindClientInPVS will // cycle through visible clients on consecutive calls. CBaseEntity* pentPlayer = UTIL_FindClientInPVS( this ); CBasePlayer *pPlayer = NULL; if( FNullEnt( pentPlayer ) ) goto env_sound_Think_slow; // no player in pvs of sound entity, slow it down pPlayer = static_cast<CBasePlayer*>( pentPlayer ); float flRange; // check to see if this is the sound entity that is // currently affecting this player if( !FNullEnt( pPlayer->m_SndLast ) && ( pPlayer->m_SndLast == this ) ) { // this is the entity currently affecting player, check // for validity if( pPlayer->m_flSndRoomtype != 0 && pPlayer->m_flSndRange != 0 ) { // we're looking at a valid sound entity affecting // player, make sure it's still valid, update range if( FEnvSoundInRange( this, pPlayer, &flRange ) ) { pPlayer->m_flSndRange = flRange; goto env_sound_Think_fast; } else { // current sound entity affecting player is no longer valid, // flag this state by clearing room_type and range. // NOTE: we do not actually change the player's room_type // NOTE: until we have a new valid room_type to change it to. pPlayer->m_flSndRange = 0; pPlayer->m_flSndRoomtype = 0; goto env_sound_Think_slow; } } else { // entity is affecting player but is out of range, // wait passively for another entity to usurp it... goto env_sound_Think_slow; } } // if we got this far, we're looking at an entity that is contending // for current player sound. the closest entity to player wins. if( FEnvSoundInRange( this, pPlayer, &flRange ) ) { if( flRange < pPlayer->m_flSndRange || pPlayer->m_flSndRange == 0 ) { // new entity is closer to player, so it wins. pPlayer->m_SndLast = this; pPlayer->m_flSndRoomtype = m_flRoomtype; pPlayer->m_flSndRange = flRange; // send room_type command to player's server. // this should be a rare event - once per change of room_type // only! //CLIENT_COMMAND(pentPlayer, "room_type %f", m_flRoomtype); MESSAGE_BEGIN( MSG_ONE, SVC_ROOMTYPE, NULL, pentPlayer ); // use the magic #1 for "one client" WRITE_SHORT( ( short ) m_flRoomtype ); // sequence number MESSAGE_END(); // crank up nextthink rate for new active sound entity // by falling through to think_fast... } // player is not closer to the contending sound entity, // just fall through to think_fast. this effectively // cranks up the think_rate of entities near the player. } // player is in pvs of sound entity, but either not visible or // not in range. do nothing, fall through to think_fast... env_sound_Think_fast: SetNextThink( gpGlobals->time + 0.25 ); return; env_sound_Think_slow: SetNextThink( gpGlobals->time + 0.75 ); return; }
//----------------------------------------------------------------------------- // Purpose: Whether or not we're in the PVS //----------------------------------------------------------------------------- inline bool CAntlionGrub::InPVS( void ) { return ( UTIL_FindClientInPVS( edict() ) != NULL ) || (UTIL_ClientPVSIsExpanded() && UTIL_FindClientInVisibilityPVS( edict() )); }
//--------------------------------------------------------- //--------------------------------------------------------- void CBounceBomb::SearchThink() { if( !UTIL_FindClientInPVS(edict()) ) { // Sleep! SetNextThink( gpGlobals->curtime + 0.5 ); return; } if( (CAI_BaseNPC::m_nDebugBits & bits_debugDisableAI) ) { if( IsAwake() ) { Wake(false); } SetNextThink( gpGlobals->curtime + 0.5 ); return; } SetNextThink( gpGlobals->curtime + 0.1 ); StudioFrameAdvance(); if( m_pConstraint && gpGlobals->curtime - m_flTimeGrabbed >= 1.0f ) { m_OnPulledUp.FireOutput( this, this ); SetMineState( MINE_STATE_CAPTIVE ); return; } float flNearestNPCDist = FindNearestNPC(); if( flNearestNPCDist <= BOUNCEBOMB_WARN_RADIUS ) { if( !IsAwake() ) { Wake( true ); } } else { if( IsAwake() ) { Wake( false ); } return; } if( flNearestNPCDist <= BOUNCEBOMB_DETONATE_RADIUS && !IsFriend( m_hNearestNPC ) ) { if( m_bBounce ) { SetMineState( MINE_STATE_TRIGGERED ); } else { // Don't pop up in the air, just explode if the NPC gets closer than explode radius. SetThink( &CBounceBomb::ExplodeThink ); SetNextThink( gpGlobals->curtime + m_flExplosionDelay ); } } }
//--------------------------------------------------------- // Count of all the weapons in the world of my type and // see if we have a surplus. If there is a surplus, try // to find suitable candidates for removal. // // Right now we just remove the first weapons we find that // are behind the player, or are out of the player's PVS. // Later, we may want to score the results so that we // removed the farthest gun that's not in the player's // viewcone, etc. // // Some notes and thoughts: // // This code is designed NOT to remove weapons that are // hand-placed by level designers. It should only clean // up weapons dropped by dead NPCs, which is useful in // situations where enemies are spawned in for a sustained // period of time. // // Right now we PREFER to remove weapons that are not in the // player's PVS, but this could be opposite of what we // really want. We may only want to conduct the cleanup on // weapons that are IN the player's PVS. //--------------------------------------------------------- void CGameWeaponManager::Think() { int i; // Don't have to think all that often. SetNextThink( gpGlobals->curtime + 2.0 ); const char *pszWeaponName = STRING( m_iszWeaponName ); CUtlVector<CBaseEntity *> candidates( 0, 64 ); if ( m_bExpectingWeapon ) { CBaseCombatWeapon *pWeapon = NULL; // Firstly, count the total number of weapons of this type in the world. // Also count how many of those can potentially be removed. pWeapon = assert_cast<CBaseCombatWeapon *>(gEntList.FindEntityByClassname( pWeapon, pszWeaponName )); while( pWeapon ) { if( !pWeapon->IsEffectActive( EF_NODRAW ) && pWeapon->IsRemoveable() ) { candidates.AddToTail( pWeapon ); } pWeapon = assert_cast<CBaseCombatWeapon *>(gEntList.FindEntityByClassname( pWeapon, pszWeaponName )); } } else { for ( i = 0; i < m_ManagedNonWeapons.Count(); i++) { CBaseEntity *pEntity = m_ManagedNonWeapons[i]; if ( pEntity ) { Assert( pEntity->m_iClassname == m_iszWeaponName ); if ( !pEntity->IsEffectActive( EF_NODRAW ) ) { candidates.AddToTail( pEntity ); } } else { m_ManagedNonWeapons.FastRemove( i-- ); } } } // Calculate the surplus. int surplus = candidates.Count() - m_iMaxPieces; // Based on what the player can see, try to clean up the world by removing weapons that // the player cannot see right at the moment. CBaseEntity *pCandidate; for ( i = 0; i < candidates.Count() && surplus > 0; i++ ) { bool fRemovedOne = false; pCandidate = candidates[i]; Assert( !pCandidate->IsEffectActive( EF_NODRAW ) ); // if ( gpGlobals->maxClients == 1 ) { CBasePlayer *pPlayer = UTIL_GetNearestVisiblePlayer(pCandidate); // Nodraw serves as a flag that this weapon is already being removed since // all we're really doing inside this loop is marking them for removal by // the entity system. We don't want to count the same weapon as removed // more than once. if( !UTIL_FindClientInPVS( pCandidate->edict() ) ) { fRemovedOne = true; } else if( !pPlayer->FInViewCone( pCandidate ) ) { fRemovedOne = true; } else if ( UTIL_DistApprox( pPlayer->GetAbsOrigin(), pCandidate->GetAbsOrigin() ) > (30*12) ) { fRemovedOne = true; } } // else // { // fRemovedOne = true; // } if( fRemovedOne ) { pCandidate->AddEffects( EF_NODRAW ); UTIL_Remove( pCandidate ); DevMsg( 2, "Surplus %s removed\n", pszWeaponName); surplus--; } } }
void CLeech::SwimThink( void ) { TraceResult tr; float flLeftSide; float flRightSide; float targetSpeed; float targetYaw = 0; CBaseEntity *pTarget; if ( !UTIL_FindClientInPVS( this ) ) { pev->nextthink = gpGlobals->time + RANDOM_FLOAT(1,1.5); pev->velocity = g_vecZero; return; } else pev->nextthink = gpGlobals->time + 0.1; targetSpeed = LEECH_SWIM_SPEED; if ( m_waterTime < gpGlobals->time ) RecalculateWaterlevel(); if ( m_stateTime < gpGlobals->time ) SwitchLeechState(); ClearConditions( bits_COND_CAN_MELEE_ATTACK1 ); switch( m_MonsterState ) { case MONSTERSTATE_COMBAT: pTarget = m_hEnemy; if ( !pTarget ) SwitchLeechState(); else { // Chase the enemy's eyes m_height = pTarget->GetAbsOrigin().z + pTarget->pev->view_ofs.z - 5; // Clip to viable water area if ( m_height < m_bottom ) m_height = m_bottom; else if ( m_height > m_top ) m_height = m_top; Vector location = pTarget->GetAbsOrigin() - GetAbsOrigin(); location.z += (pTarget->pev->view_ofs.z); if ( location.Length() < 40 ) SetConditions( bits_COND_CAN_MELEE_ATTACK1 ); // Turn towards target ent targetYaw = UTIL_VecToYaw( location ); targetYaw = UTIL_AngleDiff( targetYaw, UTIL_AngleMod( pev->angles.y ) ); if ( targetYaw < (-LEECH_TURN_RATE*0.75) ) targetYaw = (-LEECH_TURN_RATE*0.75); else if ( targetYaw > (LEECH_TURN_RATE*0.75) ) targetYaw = (LEECH_TURN_RATE*0.75); else targetSpeed *= 2; } break; default: if ( m_zTime < gpGlobals->time ) { float newHeight = RANDOM_FLOAT( m_bottom, m_top ); m_height = 0.5 * m_height + 0.5 * newHeight; m_zTime = gpGlobals->time + RANDOM_FLOAT( 1, 4 ); } if ( RANDOM_LONG( 0, 100 ) < 10 ) targetYaw = RANDOM_LONG( -30, 30 ); pTarget = NULL; // oldorigin test if ( (GetAbsOrigin() - GetOldOrigin() ).Length() < 1 ) { // If leech didn't move, there must be something blocking it, so try to turn m_sideTime = 0; } break; } m_obstacle = ObstacleDistance( pTarget ); SetOldOrigin( GetAbsOrigin() ); if ( m_obstacle < 0.1 ) m_obstacle = 0.1; // is the way ahead clear? if ( m_obstacle == 1.0 ) { // if the leech is turning, stop the trend. if ( m_flTurning != 0 ) { m_flTurning = 0; } m_fPathBlocked = false; pev->speed = UTIL_Approach( targetSpeed, pev->speed, LEECH_SWIM_ACCEL * LEECH_FRAMETIME ); pev->velocity = gpGlobals->v_forward * pev->speed; } else { m_obstacle = 1.0 / m_obstacle; // IF we get this far in the function, the leader's path is blocked! m_fPathBlocked = true; if ( m_flTurning == 0 )// something in the way and leech is not already turning to avoid { Vector vecTest; // measure clearance on left and right to pick the best dir to turn vecTest = GetAbsOrigin() + (gpGlobals->v_right * LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST); UTIL_TraceLine(GetAbsOrigin(), vecTest, missile, edict(), &tr); flRightSide = tr.flFraction; vecTest = GetAbsOrigin() + (gpGlobals->v_right * -LEECH_SIZEX) + (gpGlobals->v_forward * LEECH_CHECK_DIST); UTIL_TraceLine(GetAbsOrigin(), vecTest, missile, edict(), &tr); flLeftSide = tr.flFraction; // turn left, right or random depending on clearance ratio float delta = (flRightSide - flLeftSide); if ( delta > 0.1 || (delta > -0.1 && RANDOM_LONG(0,100)<50) ) m_flTurning = -LEECH_TURN_RATE; else m_flTurning = LEECH_TURN_RATE; } pev->speed = UTIL_Approach( -(LEECH_SWIM_SPEED*0.5), pev->speed, LEECH_SWIM_DECEL * LEECH_FRAMETIME * m_obstacle ); pev->velocity = gpGlobals->v_forward * pev->speed; } pev->ideal_yaw = m_flTurning + targetYaw; UpdateMotion(); }