//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- 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; } */ 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_GetNearestPlayer(ppAIs[i]->GetAbsOrigin()) ) != D_LI ) continue; // Skip distant NPCs Vector vNearestPlayerPos = UTIL_GetNearestPlayer(ppAIs[i]->GetAbsOrigin())->GetAbsOrigin(); if ( !ppAIs[i]->IsInPlayerSquad() && !UTIL_FindClientInPVS( ppAIs[i]->edict() ) && ( ( ppAIs[i]->GetAbsOrigin() - vNearestPlayerPos ).LengthSqr() > 150*12 || fabsf( ppAIs[i]->GetAbsOrigin().z - vNearestPlayerPos.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)++; } } }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CPropVehicleViewController::InputForcePlayerIn( inputdata_t &inputdata ) { CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); // AI Patch Addition. if ( !pPlayer ) return; ResetUseKey( pPlayer ); // Get the entry animation from the input int iEntryAnim = ACTIVITY_NOT_AVAILABLE; if ( inputdata.value.StringID() != NULL_STRING ) { iEntryAnim = LookupSequence( inputdata.value.String() ); if ( iEntryAnim == ACTIVITY_NOT_AVAILABLE ) { Warning("vehicle_viewcontroller %s could not find specified entry animation %s\n", STRING(GetEntityName()), inputdata.value.String() ); return; } } // Make sure we successfully got in the vehicle if ( pPlayer->GetInVehicle( GetServerVehicle(), VEHICLE_ROLE_DRIVER ) == false ) { // The player was unable to enter the vehicle and the output has failed Assert( 0 ); return; } // Setup the "enter" vehicle sequence SetCycle( 0 ); m_flAnimTime = gpGlobals->curtime; ResetSequence( iEntryAnim ); ResetClientsideFrame(); m_bEnterAnimOn = true; }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CEnvHudHint::InputHideHudHint( inputdata_t &inputdata ) { CBaseEntity *pPlayer = NULL; if ( inputdata.pActivator && inputdata.pActivator->IsPlayer() ) { pPlayer = inputdata.pActivator; } else { #ifdef HL2SB pPlayer = UTIL_GetNearestPlayer( GetAbsOrigin() ); #else pPlayer = UTIL_GetLocalPlayer(); #endif } if ( pPlayer ) { if ( !pPlayer || !pPlayer->IsNetClient() ) return; CSingleUserRecipientFilter user( (CBasePlayer *)pPlayer ); user.MakeReliable(); UserMessageBegin( user, "KeyHintText" ); WRITE_BYTE( 1 ); // one message WRITE_STRING( STRING(NULL_STRING) ); MessageEnd(); } }
//----------------------------------------------------------------------------- // Purpose: Input handler for showing the message and/or playing the sound. //----------------------------------------------------------------------------- void CEnvHudHint::InputShowHudHint( inputdata_t &inputdata ) { if ( AllPlayers() ) { CReliableBroadcastRecipientFilter user; UserMessageBegin( user, "KeyHintText" ); WRITE_BYTE( 1 ); // one message WRITE_STRING( STRING(m_iszMessage) ); MessageEnd(); } else { CBaseEntity *pPlayer = NULL; if ( inputdata.pActivator && inputdata.pActivator->IsPlayer() ) { pPlayer = inputdata.pActivator; } else { pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); } if ( !pPlayer || !pPlayer->IsNetClient() ) return; CSingleUserRecipientFilter user( (CBasePlayer *)pPlayer ); user.MakeReliable(); UserMessageBegin( user, "KeyHintText" ); WRITE_BYTE( 1 ); // one message WRITE_STRING( STRING(m_iszMessage) ); MessageEnd(); } }
//----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - //----------------------------------------------------------------------------- void CPropVehiclePrisonerPod::InputEnterVehicleImmediate( inputdata_t &inputdata ) { if ( m_bEnterAnimOn ) return; // Try the activator first & use them if they are a player. CBaseCombatCharacter *pPassenger = ToBaseCombatCharacter( inputdata.pActivator ); if ( pPassenger == NULL ) { // Activator was not a player, just grab the nearest player. // AI Patch Addition. pPassenger = UTIL_GetNearestPlayer(GetAbsOrigin()); // AI Patch Addition. if ( pPassenger == NULL ) return; } CBasePlayer *pPlayer = ToBasePlayer( pPassenger ); if ( pPlayer != NULL ) { if ( pPlayer->IsInAVehicle() ) { // Force the player out of whatever vehicle they are in. pPlayer->LeaveVehicle(); } pPlayer->GetInVehicle( GetServerVehicle(), VEHICLE_ROLE_DRIVER ); } else { // NPCs are not currently supported - jdw Assert( 0 ); } }
//----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - //----------------------------------------------------------------------------- void CEnvZoom::InputZoom( inputdata_t &inputdata ) { CBasePlayer *pPlayer; if (inputdata.pActivator && inputdata.pActivator->IsPlayer()) pPlayer = ToBasePlayer(inputdata.pActivator); else pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( pPlayer ) { #ifdef HL2_DLL if ( pPlayer == pPlayer->GetFOVOwner() ) { CHL2_Player *pHLPlayer = static_cast<CHL2_Player*>( pPlayer ); pHLPlayer->StopZooming(); } #endif // If the player's already holding a fov from another env_zoom, we're allowed to overwrite it if ( pPlayer->GetFOVOwner() && FClassnameIs( pPlayer->GetFOVOwner(), "env_zoom" ) ) { pPlayer->ClearZoomOwner(); } //Stuff the values pPlayer->SetFOV( this, m_nFOV, m_flSpeed ); } }
//----------------------------------------------------------------------------- // Purpose: // Input : &data - //----------------------------------------------------------------------------- void CItem_DynamicResupply::InputCalculateType( inputdata_t &data ) { // spawn gear for the nearest player CBasePlayer *pNearest = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( pNearest != NULL ) SpawnDynamicItem( pNearest ); }
//----------------------------------------------------------------------------- // Creates the explosion effect //----------------------------------------------------------------------------- void CEnvHeadcrabCanister::Detonate( ) { // Send the impact output m_OnImpacted.FireOutput( this, this, 0 ); if ( !HasSpawnFlags( SF_NO_IMPACT_SOUND ) ) { StopSound( "HeadcrabCanister.IncomingSound" ); EmitSound( "HeadcrabCanister.Explosion" ); } // If we're supposed to be removed, do that now if ( HasSpawnFlags( SF_REMOVE_ON_IMPACT ) ) { SetAbsOrigin( m_vecImpactPosition ); SetModel( ENV_HEADCRABCANISTER_BROKEN_MODEL ); SetMoveType( MOVETYPE_NONE ); IncrementInterpolationFrame(); m_bLanded = true; // Become invisible so our trail can finish up AddEffects( EF_NODRAW ); SetSolidFlags( FSOLID_NOT_SOLID ); SetThink( &CEnvHeadcrabCanister::SUB_Remove ); SetNextThink( gpGlobals->curtime + ENV_HEADCRABCANISTER_TRAIL_TIME ); return; } // Test for damaging things TestForCollisionsAgainstWorld( m_vecImpactPosition ); // Shake the screen unless flagged otherwise if ( !HasSpawnFlags( SF_NO_SHAKE ) ) { CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); // If the player is on foot, then do a more limited shake float shakeRadius = ( pPlayer && pPlayer->IsInAVehicle() ) ? sk_env_headcrabcanister_shake_radius_vehicle.GetFloat() : sk_env_headcrabcanister_shake_radius.GetFloat(); UTIL_ScreenShake( m_vecImpactPosition, sk_env_headcrabcanister_shake_amplitude.GetFloat(), 150.0, 1.0, shakeRadius, SHAKE_START ); } // Do explosion effects if ( !HasSpawnFlags( SF_NO_IMPACT_EFFECTS ) ) { // Normal explosion ExplosionCreate( m_vecImpactPosition, GetAbsAngles(), this, 50.0f, 500.0f, SF_ENVEXPLOSION_NODLIGHTS | SF_ENVEXPLOSION_NOSPARKS | SF_ENVEXPLOSION_NODAMAGE | SF_ENVEXPLOSION_NOSOUND, 1300.0f ); // Dust explosion AR2Explosion *pExplosion = AR2Explosion::CreateAR2Explosion( m_vecImpactPosition ); if( pExplosion ) { pExplosion->SetLifetime(10); } } }
//----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - //----------------------------------------------------------------------------- void CPropCrane::InputForcePlayerIn( inputdata_t &inputdata ) { CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( pPlayer && !m_hPlayer ) { GetServerVehicle()->HandlePassengerEntry( pPlayer, 0 ); } }
bool CNPC_Zombine::AllowedToSprint( void ) { if ( IsOnFire() ) return false; //If you're sprinting then there's no reason to sprint again. if ( IsSprinting() ) return false; int iChance = SPRINT_CHANCE_VALUE; //Secobmod FixMe ?? also changed to HL2MPRules CHL2_Player *pPlayer = dynamic_cast <CHL2_Player*> ( UTIL_GetNearestPlayer(GetAbsOrigin() )); //CHL2MP_Player *pPlayer = dynamic_cast<CHL2MP_Player *>( UTIL_GetNearestPlayer(GetAbsOrigin() ); if ( pPlayer ) { #ifdef MFS if ( HL2MPRules()->IsAlyxInDarknessMode() && pPlayer->FlashlightIsOn() == false ) #else if (IsAlyxInDarknessMode() && pPlayer->FlashlightIsOn() == false) #endif { iChance = SPRINT_CHANCE_VALUE_DARKNESS; } //Bigger chance of this happening if the player is not looking at the zombie if ( pPlayer->FInViewCone( this ) == false ) { iChance *= 2; } } if ( HasGrenade() ) { iChance *= 4; } //Below 25% health they'll always sprint if ( ( GetHealth() > GetMaxHealth() * 0.5f ) ) { if ( IsStrategySlotRangeOccupied( SQUAD_SLOT_ZOMBINE_SPRINT1, SQUAD_SLOT_ZOMBINE_SPRINT2 ) == true ) return false; if ( random->RandomInt( 0, 100 ) > iChance ) return false; if ( m_flSprintRestTime > gpGlobals->curtime ) return false; } float flLength = ( GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter() ).Length(); if ( flLength > MAX_SPRINT_DISTANCE ) return false; return true; }
void CAI_PlaneSolver::GenerateObstacleNpcs( const AILocalMoveGoal_t &goal, float probeDist ) { if ( !ProbeForNpcs() ) { CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); Vector minsSelf, maxsSelf; m_pNpc->CollisionProp()->WorldSpaceSurroundingBounds( &minsSelf, &maxsSelf ); float radiusSelf = (minsSelf.AsVector2D() - maxsSelf.AsVector2D()).Length() * 0.5; for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) { CAI_BaseNPC *pAI = ppAIs[i]; if ( pAI != m_pNpc && pAI->IsAlive() && ( !goal.pPath || pAI != goal.pPath->GetTarget() ) ) { Vector mins, maxs; pAI->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs ); if ( mins.z < maxsSelf.z + 12.0 && maxs.z > minsSelf.z - 12.0 ) { float radius = (mins.AsVector2D() - maxs.AsVector2D()).Length() * 0.5; float distance = ( pAI->GetAbsOrigin().AsVector2D() - m_pNpc->GetAbsOrigin().AsVector2D() ).Length(); if ( distance - radius < radiusSelf + probeDist ) { AddObstacle( pAI->WorldSpaceCenter(), radius, pAI, AIMST_AVOID_NPC ); } } } } #ifdef SecobMod__Enable_Fixed_Multiplayer_AI CBaseEntity *pPlayer = UTIL_GetNearestPlayer(m_pNpc->GetAbsOrigin()); #else CBaseEntity *pPlayer = UTIL_PlayerByIndex( 1 ); #endif //SecobMod__Enable_Fixed_Multiplayer_AI if ( pPlayer ) { Vector mins, maxs; pPlayer->CollisionProp()->WorldSpaceSurroundingBounds( &mins, &maxs ); if ( mins.z < maxsSelf.z + 12.0 && maxs.z > minsSelf.z - 12.0 ) { float radius = (mins.AsVector2D() - maxs.AsVector2D()).Length(); float distance = ( pPlayer->GetAbsOrigin().AsVector2D() - m_pNpc->GetAbsOrigin().AsVector2D() ).Length(); if ( distance - radius < radiusSelf + probeDist ) { AddObstacle( pPlayer->WorldSpaceCenter(), radius, pPlayer, AIMST_AVOID_NPC ); } } } } }
//----------------------------------------------------------------------------- // Purpose: Burn targets around us //----------------------------------------------------------------------------- void CEntityDissolve::DissolveThink( void ) { CBaseAnimating *pTarget = ( GetMoveParent() ) ? GetMoveParent()->GetBaseAnimating() : NULL; if ( GetModelName() == NULL_STRING && pTarget == NULL ) return; if ( pTarget == NULL ) { UTIL_Remove( this ); return; } // Turn them into debris pTarget->SetCollisionGroup( COLLISION_GROUP_DISSOLVING ); if ( pTarget && pTarget->GetFlags() & FL_TRANSRAGDOLL ) { SetRenderColorA( 0 ); } float dt = gpGlobals->curtime - m_flStartTime; if ( dt < m_flFadeInStart ) { SetNextThink( m_flStartTime + m_flFadeInStart ); return; } // If we're done fading, then kill our target entity and us if ( dt >= m_flFadeOutStart + m_flFadeOutLength ) { // Necessary to cause it to do the appropriate death cleanup // Yeah, the player may have nothing to do with it, but // passing NULL to TakeDamage causes bad things to happen CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); int iNoPhysicsDamage = g_pGameRules->Damage_GetNoPhysicsForce(); CTakeDamageInfo info( pPlayer, pPlayer, 10000.0, DMG_GENERIC | DMG_REMOVENORAGDOLL | iNoPhysicsDamage ); pTarget->TakeDamage( info ); if ( pTarget != pPlayer ) { UTIL_Remove( pTarget ); } UTIL_Remove( this ); return; } SetNextThink( gpGlobals->curtime + TICK_INTERVAL ); }
//----------------------------------------------------------------------------- // Purpose: // Input : &inputdata - //----------------------------------------------------------------------------- void CEnvZoom::InputUnZoom( inputdata_t &inputdata ) { CBasePlayer *pPlayer; if (inputdata.pActivator && inputdata.pActivator->IsPlayer()) pPlayer = ToBasePlayer(inputdata.pActivator); else pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( pPlayer ) { // Stuff the values pPlayer->SetFOV( this, 0 ); } }
// player pickup utility routine void Pickup_ForcePlayerToDropThisObject( CBaseEntity *pTarget ) { if ( pTarget == NULL ) return; IPhysicsObject *pPhysics = pTarget->VPhysicsGetObject(); if ( pPhysics == NULL ) return; if ( pPhysics->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { CBasePlayer *pPlayer = UTIL_GetNearestPlayer(pTarget->GetAbsOrigin()); pPlayer->ForceDropOfCarriedPhysObjects( pTarget ); } }
CBaseEntity* MoveToRandomSpot( CBaseEntity *pEnt ) { if ( pEnt ) { #ifdef HL2SB CBasePlayer *pLocalPlayer = UTIL_GetNearestPlayer( pEnt->GetAbsOrigin() ); #else CBasePlayer *pLocalPlayer = UTIL_GetLocalPlayer(); #endif if ( pLocalPlayer ) { Vector vForward; pLocalPlayer->EyeVectors(&vForward ); UTIL_SetOrigin( pEnt, GetRandomSpot() ); } } return pEnt; }
//----------------------------------------------------------------------------- // Purpose: Force the player to enter the vehicle. //----------------------------------------------------------------------------- void CPropVehiclePrisonerPod::InputEnterVehicle( inputdata_t &inputdata ) { if ( m_bEnterAnimOn ) return; // Try the activator first & use them if they are a player. CBaseCombatCharacter *pPassenger = ToBaseCombatCharacter( inputdata.pActivator ); if ( pPassenger == NULL ) { // Activator was not a player, just grab the nearest player. // AI Patch Addition. pPassenger = UTIL_GetNearestPlayer(GetAbsOrigin()); // AI Patch Addition. if ( pPassenger == NULL ) return; } // FIXME: I hate code like this. I should really add a parameter to HandlePassengerEntry // to allow entry into locked vehicles bool bWasLocked = m_bLocked; m_bLocked = false; GetServerVehicle()->HandlePassengerEntry( pPassenger, true ); m_bLocked = bWasLocked; }
//------------------------------------------------------------------------------ bool CNPC_EnemyFinder::ShouldAlwaysThink() { if ( BaseClass::ShouldAlwaysThink() ) return true; CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( pPlayer && IRelationType( pPlayer ) == D_HT ) { float playerDistSqr = GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ); if ( !m_flMaxSearchDist || playerDistSqr <= Square(m_flMaxSearchDist) ) { if ( !FBitSet( m_spawnflags, SF_ENEMY_FINDER_CHECK_VIS) ) return true; if ( playerDistSqr <= Square( 50 * 12 ) ) return true; } } return false; }
//----------------------------------------- // Think //----------------------------------------- void CMessageEntity::Think( void ) { SetNextThink( gpGlobals->curtime + 0.1f ); // check for player distance CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( !pPlayer || ( pPlayer->GetFlags() & FL_NOTARGET ) ) return; Vector worldTargetPosition = pPlayer->EyePosition(); // bail if player is too far away if ( (worldTargetPosition - GetAbsOrigin()).Length() > m_radius ) { m_drawText = false; return; } // turn on text m_drawText = true; }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CNPC_EnemyFinder::StartNPC ( void ) { AddSpawnFlags(SF_NPC_FALL_TO_GROUND); // this prevents CAI_BaseNPC from slamming the finder to // the ground just because it's not MOVETYPE_FLY BaseClass::StartNPC(); if ( m_PlayerFreePass.GetParams().duration > 0.1 ) { m_PlayerFreePass.SetPassTarget( UTIL_GetNearestPlayer(GetAbsOrigin()) ); AI_FreePassParams_t freePassParams = m_PlayerFreePass.GetParams(); freePassParams.coverDist = 120; freePassParams.peekEyeDist = 1.75; freePassParams.peekEyeDistZ = 4; m_PlayerFreePass.SetParams( freePassParams ); } if (!m_nStartOn) { SetThink(NULL); } }
//----------------------------------------------------------------------------- // Purpose: // Input : &data - //----------------------------------------------------------------------------- void CItem_DynamicResupply::InputCalculateType( inputdata_t &data ) {{ #ifdef SecobMod__Enable_Fixed_Multiplayer_AI // spawn gear for the nearest player CBasePlayer *pNearest = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( pNearest != NULL ) SpawnDynamicItem( pNearest ); #else CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); SpawnDynamicItem( pPlayer ); #endif //SecobMod__Enable_Fixed_Multiplayer_AI } //----------------------------------------------------------------------------- // Purpose: // Input : &data - //----------------------------------------------------------------------------- void CItem_DynamicResupply::InputBecomeMaster( inputdata_t &data ) { if ( g_MasterResupply ) g_MasterResupply->m_bIsMaster = false; g_MasterResupply = this; m_bIsMaster = true; // Stop thinking now that I am the master. SetThink( NULL ); } //----------------------------------------------------------------------------- // Chooses an item when the player is full //----------------------------------------------------------------------------- void CItem_DynamicResupply::SpawnFullItem( CItem_DynamicResupply *pMaster, CBasePlayer *pPlayer, int iDebug ) { // Can we not actually spawn the item? if ( !HasSpawnFlags(SF_DYNAMICRESUPPLY_ALWAYS_SPAWN) ) return; float flRatio[NUM_AMMO_ITEMS]; int i; float flTotalProb = 0.0f; for ( i = 0; i < NUM_AMMO_ITEMS; ++i ) { int iAmmoType = GetAmmoDef()->Index( g_DynamicResupplyAmmoItems[i].sAmmoDef ); bool bCanSpawn = pPlayer->Weapon_GetWpnForAmmo( iAmmoType ) != NULL; if ( bCanSpawn && ( g_DynamicResupplyAmmoItems[i].flFullProbability != 0 ) && ( pMaster->m_flDesiredAmmo[i] != 0.0f ) ) { flTotalProb += g_DynamicResupplyAmmoItems[i].flFullProbability; flRatio[i] = flTotalProb; } else { flRatio[i] = -1.0f; } } if ( flTotalProb == 0.0f ) { // If we're supposed to fallback to just a health vial, do that and finish. if ( pMaster->HasSpawnFlags(SF_DYNAMICRESUPPLY_FALLBACK_TO_VIAL) ) { CBaseEntity::Create( "item_healthvial", GetAbsOrigin(), GetAbsAngles(), this ); if ( iDebug ) { Msg("Player is full, spawning item_healthvial due to spawnflag.\n"); } return; } // Otherwise, spawn the first ammo item in the list flRatio[0] = 1.0f; flTotalProb = 1.0f; } float flChoice = random->RandomFloat( 0.0f, flTotalProb ); for ( i = 0; i < NUM_AMMO_ITEMS; ++i ) { if ( flChoice <= flRatio[i] ) { CBaseEntity::Create( g_DynamicResupplyAmmoItems[i].sEntityName, GetAbsOrigin(), GetAbsAngles(), this ); if ( iDebug ) { Msg("Player is full, spawning %s \n", g_DynamicResupplyAmmoItems[i].sEntityName ); } return; } } if ( iDebug ) { Msg("Player is full on all health + ammo, is not spawning.\n" ); } } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CItem_DynamicResupply::FindPotentialItems( int nCount, DynamicResupplyItems_t *pItems, int iDebug, SpawnInfo_t *pSpawnInfo ) { int i; for ( i = 0; i < nCount; ++i ) { pSpawnInfo[i].m_iPotentialItems = 0; } // Count the potential addition of items in the PVS CBaseEntity *pEntity = NULL; while ( (pEntity = UTIL_EntitiesInPVS( this, pEntity )) != NULL ) { if ( pEntity->WorldSpaceCenter().DistToSqr( WorldSpaceCenter() ) > (POTENTIAL_ITEM_RADIUS * POTENTIAL_ITEM_RADIUS) ) continue; for ( i = 0; i < nCount; ++i ) { if ( !FClassnameIs( pEntity, pItems[i].sEntityName ) ) continue; if ( iDebug == 2 ) { NDebugOverlay::Line( WorldSpaceCenter(), pEntity->WorldSpaceCenter(), 0,255,0, true, 20.0 ); } ++pSpawnInfo[i].m_iPotentialItems; break; } } if ( iDebug ) { Msg("Searching the PVS:\n"); for ( int i = 0; i < nCount; i++ ) { Msg(" Found %d '%s' in the PVS.\n", pSpawnInfo[i].m_iPotentialItems, pItems[i].sEntityName ); } } }
CBaseEntity* CHL2MP_Player::EntSelectSpawnPoint( void ) { CBaseEntity *pSpot = NULL; CBaseEntity *pLastSpawnPoint = g_pLastSpawn; edict_t *player = edict(); const char *pSpawnpointName = "info_player_deathmatch"; pSpot = pLastSpawnPoint; // Randomize the start spot for ( int i = random->RandomInt(1,5); i > 0; i-- ) pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); if ( !pSpot ) // skip over the null point pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); CBaseEntity *pFirstSpot = pSpot; do { if ( pSpot ) { // check if pSpot is valid if ( g_pGameRules->IsSpawnPointValid( pSpot, this ) ) { if ( pSpot->GetLocalOrigin() == vec3_origin ) { pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); continue; } // if so, go to pSpot goto ReturnSpot; } } // increment pSpot pSpot = gEntList.FindEntityByClassname( pSpot, pSpawnpointName ); } while ( pSpot != pFirstSpot ); // loop if we're not back to the start // we haven't found a place to spawn yet, so kill any guy at the first spawn point and spawn there if ( pSpot ) { CBaseEntity *ent = NULL; for ( CEntitySphereQuery sphere( pSpot->GetAbsOrigin(), 128 ); (ent = sphere.GetCurrentEntity()) != NULL; sphere.NextEntity() ) { // if ent is a client, kill em (unless they are ourselves) if ( ent->IsPlayer() && !(ent->edict() == player) ) ent->TakeDamage( CTakeDamageInfo( GetContainingEntity(INDEXENT(0)), GetContainingEntity(INDEXENT(0)), 300, DMG_GENERIC ) ); } goto ReturnSpot; } if ( !pSpot ) { char szMapName[256]; Q_strncpy(szMapName, STRING(gpGlobals->mapname), sizeof(szMapName)); Q_strlower(szMapName); //TDT - Information: Although we don't support official maps for gaming, they are useful for testing and these maps for whatever reason spawn you in the wrong location. As such this // code is here to force players to spawn at the beginning of the selected maps. Custom maps won't require this as they will have deathmatch/class based player starts. if (!Q_strnicmp(szMapName, "d1_canals_01a", 13) || !Q_strnicmp(szMapName, "d1_canals_03", 12) || !Q_strnicmp(szMapName, "d1_canals_13", 12) || !Q_strnicmp(szMapName, "d1_town_01", 10) || !Q_strnicmp(szMapName, "d1_town_01a", 11) || !Q_strnicmp(szMapName, "d1_town_02", 10) || !Q_strnicmp(szMapName, "d1_town_02a", 11) || !Q_strnicmp(szMapName, "d1_town_03", 10) || !Q_strnicmp(szMapName, "d1_town_04", 10) || !Q_strnicmp(szMapName, "d1_town_05", 10) || !Q_strnicmp(szMapName, "d2_coast_03", 11) || !Q_strnicmp(szMapName, "d2_coast_08", 11) || !Q_strnicmp(szMapName, "d2_coast_11", 11) || !Q_strnicmp(szMapName, "d2_prison_01", 12) || !Q_strnicmp(szMapName, "d2_prison_02", 12) || !Q_strnicmp(szMapName, "d2_prison_03", 12) || !Q_strnicmp(szMapName, "d2_prison_04", 12) || !Q_strnicmp(szMapName, "d2_prison_05", 12) || !Q_strnicmp(szMapName, "d2_prison_06", 12) || !Q_strnicmp(szMapName, "d2_prison_07", 12) || !Q_strnicmp(szMapName, "d2_prison_08", 12) || !Q_strnicmp(szMapName, "d3_c17_08", 9) || !Q_strnicmp(szMapName, "d3_citadel_01", 13) || !Q_strnicmp(szMapName, "d3_citadel_02", 13) || !Q_strnicmp(szMapName, "d3_citadel_03", 13) || !Q_strnicmp(szMapName, "d3_citadel_04", 13) || !Q_strnicmp(szMapName, "d3_citadel_05", 13) || !Q_strnicmp(szMapName, "d3_breen_01", 11) || !Q_strnicmp(szMapName, "ep1_c17_00", 10) || !Q_strnicmp(szMapName, "ep1_c17_00a", 11) || !Q_strnicmp(szMapName, "ep1_c17_02b", 11) || !Q_strnicmp(szMapName, "ep1_c17_05", 10) || !Q_strnicmp(szMapName, "ep2_outland_01a", 15) || !Q_strnicmp(szMapName, "ep2_outland_03", 14) || !Q_strnicmp(szMapName, "ep2_outland_08", 14) || !Q_strnicmp(szMapName, "ep2_outland_06", 14) ) { CBaseEntity *pEntity = NULL; CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); Vector vecOrigin = pPlayer->GetAbsOrigin(); pEntity = gEntList.FindEntityByClassnameNearest("item_suit", vecOrigin, 0); if (pEntity != NULL) { vecOrigin = pEntity->GetAbsOrigin(); pEntity = gEntList.FindEntityByClassnameNearest("info_player_start", vecOrigin, 0); pSpot = pEntity; pSpawnpointName = "info_player_start"; goto ReturnSpot; } else { pSpot = gEntList.FindEntityByClassname(pSpot, "info_player_start"); } } else if (!Q_strnicmp(szMapName, "d1_trainstation_05", 18)) { CBaseEntity *pEntity = NULL; CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); Vector vecOrigin = pPlayer->GetAbsOrigin(); pEntity = gEntList.FindEntityByClassnameNearest("npc_alyx", vecOrigin, 0); if (pEntity != NULL) { vecOrigin = pEntity->GetAbsOrigin(); pEntity = gEntList.FindEntityByClassnameNearest("info_player_start", vecOrigin, 0); pSpot = pEntity; pSpawnpointName = "info_player_start"; goto ReturnSpot; } else { pSpot = gEntList.FindEntityByClassname(pSpot, "info_player_start"); } } else { pSpot = gEntList.FindEntityByClassname(pSpot, "info_player_start"); } if ( pSpot ) goto ReturnSpot; } ReturnSpot: g_pLastSpawn = pSpot; m_flSlamProtectTime = gpGlobals->curtime + 0.5; return pSpot; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- float CalculatePhysicsImpactDamage( int index, gamevcollisionevent_t *pEvent, const impactdamagetable_t &table, float energyScale, bool allowStaticDamage, int &damageType, bool bDamageFromHeldObjects ) { damageType = DMG_CRUSH; int otherIndex = !index; // UNDONE: Expose a flag for self-inflicted damage? Can't think of a valid case so far. if ( pEvent->pEntities[0] == pEvent->pEntities[1] ) return 0; if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_NO_NPC_IMPACT_DMG ) { if( pEvent->pEntities[index]->IsNPC() || pEvent->pEntities[index]->IsPlayer() ) { return 0; } } // use implicit velocities on ragdolls since they may have high constraint velocities that aren't actually executed, just pushed through contacts if (( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_PART_OF_RAGDOLL) && pEvent->pEntities[index]->IsPlayer() ) { pEvent->pObjects[otherIndex]->GetImplicitVelocity( &pEvent->preVelocity[otherIndex], &pEvent->preAngularVelocity[otherIndex] ); } // Dissolving impact damage results in death always. if ( ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_DMG_DISSOLVE ) && !pEvent->pEntities[index]->IsEFlagSet(EFL_NO_DISSOLVE) ) { damageType |= DMG_DISSOLVE; return 1000; } if ( energyScale <= 0.0f ) return 0; const int gameFlagsNoDamage = FVPHYSICS_CONSTRAINT_STATIC | FVPHYSICS_NO_IMPACT_DMG; // NOTE: Crushing damage is handled by stress calcs in vphysics update functions, this is ONLY impact damage // this is a non-moving object due to a constraint - no damage if ( pEvent->pObjects[otherIndex]->GetGameFlags() & gameFlagsNoDamage ) return 0; // If it doesn't take damage from held objects and the object is being held - no damage if ( !bDamageFromHeldObjects && ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) ) { // If it doesn't take damage from held objects - no damage if ( !bDamageFromHeldObjects ) return 0; } if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_MULTIOBJECT_ENTITY ) { // UNDONE: Add up mass here for car wheels and prop_ragdoll pieces? IPhysicsObject *pList[VPHYSICS_MAX_OBJECT_LIST_COUNT]; int count = pEvent->pEntities[otherIndex]->VPhysicsGetObjectList( pList, ARRAYSIZE(pList) ); for ( int i = 0; i < count; i++ ) { if ( pList[i]->GetGameFlags() & gameFlagsNoDamage ) return 0; } } if ( pEvent->pObjects[index]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { // players can't damage held objects if ( pEvent->pEntities[otherIndex]->IsPlayer() ) return 0; allowStaticDamage = false; } #if 0 { PhysGetDamageInflictorVelocityStartOfFrame( pEvent->pObjects[otherIndex], pEvent->preVelocity[otherIndex], pEvent->preAngularVelocity[otherIndex] ); } #endif float otherSpeedSqr = pEvent->preVelocity[otherIndex].LengthSqr(); float otherAngSqr = 0; // factor in angular for sharp objects if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_DMG_SLICE ) { otherAngSqr = pEvent->preAngularVelocity[otherIndex].LengthSqr(); } float otherMass = pEvent->pObjects[otherIndex]->GetMass(); if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { // if the player is holding the object, use it's real mass (player holding reduced the mass) Vector origin; pEvent->pObjects[otherIndex]->GetPosition(&origin,NULL); CBasePlayer *pPlayer = UTIL_GetNearestPlayer(origin); if ( pPlayer ) { otherMass = pPlayer->GetHeldObjectMass( pEvent->pObjects[otherIndex] ); } } // NOTE: sum the mass of each object in this system for the purpose of damage if ( pEvent->pEntities[otherIndex] && (pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_MULTIOBJECT_ENTITY) ) { otherMass = PhysGetEntityMass( pEvent->pEntities[otherIndex] ); } if ( pEvent->pObjects[otherIndex]->GetGameFlags() & FVPHYSICS_HEAVY_OBJECT ) { otherMass = table.largeMassMin; if ( energyScale < 2.0f ) { energyScale = 2.0f; } } // UNDONE: allowStaticDamage is a hack - work out some method for // breakable props to impact the world and break!! if ( !allowStaticDamage ) { if ( otherMass < table.minMass ) return 0; // check to see if the object is small if ( otherMass < table.smallMassMax && otherSpeedSqr < table.smallMassMinSpeedSqr ) return 0; if ( otherSpeedSqr < table.minSpeedSqr && otherAngSqr < table.minRotSpeedSqr ) return 0; } // Add extra oomph for floating objects if ( pEvent->pEntities[index]->IsFloating() && !pEvent->pEntities[otherIndex]->IsWorld() ) { if ( energyScale < 3.0f ) { energyScale = 3.0f; } } float damage = 0; bool bDebug = false;//(&table == &gDefaultPlayerImpactDamageTable); // don't ever take spin damage from slowly spinning objects if ( otherAngSqr > table.minRotSpeedSqr ) { Vector otherInertia = pEvent->pObjects[otherIndex]->GetInertia(); float angularMom = DotProductAbs( otherInertia, pEvent->preAngularVelocity[otherIndex] ); damage = ReadDamageTable( table.angularTable, table.angularCount, angularMom * energyScale, bDebug ); if ( damage > 0 ) { // Msg("Spin : %.1f, Damage %.0f\n", FastSqrt(angularMom), damage ); damageType |= DMG_SLASH; } } float deltaV = pEvent->preVelocity[index].Length() - pEvent->postVelocity[index].Length(); float mass = pEvent->pObjects[index]->GetMass(); // If I lost speed, and I lost less than min velocity, then filter out this energy if ( deltaV > 0 && deltaV < table.myMinVelocity ) { deltaV = 0; } float eliminatedEnergy = deltaV * deltaV * mass; deltaV = pEvent->preVelocity[otherIndex].Length() - pEvent->postVelocity[otherIndex].Length(); float otherEliminatedEnergy = deltaV * deltaV * otherMass; // exaggerate the effects of really large objects if ( otherMass >= table.largeMassMin ) { otherEliminatedEnergy *= table.largeMassScale; float dz = pEvent->preVelocity[otherIndex].z - pEvent->postVelocity[otherIndex].z; if ( deltaV > 0 && dz < 0 && pEvent->preVelocity[otherIndex].z < 0 ) { float factor = fabs(dz / deltaV); otherEliminatedEnergy *= (1 + factor * (table.largeMassFallingScale - 1.0f)); } } eliminatedEnergy += otherEliminatedEnergy; // now in units of this character's speed squared float invMass = pEvent->pObjects[index]->GetInvMass(); if ( !pEvent->pObjects[index]->IsMoveable() ) { // inv mass is zero, but impact damage is enabled on this // prop, so recompute: invMass = 1.0f / pEvent->pObjects[index]->GetMass(); } else if ( pEvent->pObjects[index]->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) { // if the player is holding the object, use it's real mass (player holding reduced the mass) Vector origin; pEvent->pObjects[otherIndex]->GetPosition(&origin,NULL); CBasePlayer *pPlayer = UTIL_GetNearestPlayer(origin); if ( pPlayer ) { float mass = pPlayer->GetHeldObjectMass( pEvent->pObjects[index] ); if ( mass > 0 ) { invMass = 1.0f / mass; } } } eliminatedEnergy *= invMass * energyScale; damage += ReadDamageTable( table.linearTable, table.linearCount, eliminatedEnergy, bDebug ); if ( !pEvent->pObjects[otherIndex]->IsStatic() && otherMass < table.smallMassMax && table.smallMassCap > 0 ) { damage = clamp( damage, 0.f, table.smallMassCap ); } return damage; }
CNPCSpawnDestination *CTemplateNPCMaker::FindSpawnDestination() { CNPCSpawnDestination *pDestinations[ MAX_DESTINATION_ENTS ]; CBaseEntity *pEnt = NULL; CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); int count = 0; if( !pPlayer ) { return NULL; } // Collect all the qualifiying destination ents pEnt = gEntList.FindEntityByName( NULL, m_iszDestinationGroup ); if( !pEnt ) { DevWarning("Template NPC Spawner (%s) doesn't have any spawn destinations!\n", GetDebugName() ); return NULL; } while( pEnt ) { CNPCSpawnDestination *pDestination; pDestination = dynamic_cast <CNPCSpawnDestination*>(pEnt); if( pDestination && pDestination->IsAvailable() ) { bool fValid = true; Vector vecTest = pDestination->GetAbsOrigin(); pPlayer = UTIL_GetNearestPlayer(vecTest); if( m_CriterionVisibility != TS_YN_DONT_CARE ) { // Right now View Cone check is omitted intentionally. Vector vecTopOfHull = NAI_Hull::Maxs( HULL_HUMAN ); vecTopOfHull.x = 0; vecTopOfHull.y = 0; bool fVisible = (pPlayer->FVisible( vecTest ) || pPlayer->FVisible( vecTest + vecTopOfHull ) ); if( m_CriterionVisibility == TS_YN_YES ) { if( !fVisible ) fValid = false; } else { if( fVisible ) { if ( !(pPlayer->GetFlags() & FL_NOTARGET) ) fValid = false; else DevMsg( 2, "Spawner %s spawning even though seen due to notarget\n", STRING( GetEntityName() ) ); } } } if( fValid ) { pDestinations[ count ] = pDestination; count++; } } pEnt = gEntList.FindEntityByName( pEnt, m_iszDestinationGroup ); } if( count < 1 ) return NULL; // Now find the nearest/farthest based on distance criterion if( m_CriterionDistance == TS_DIST_DONT_CARE ) { // Pretty lame way to pick randomly. Try a few times to find a random // location where a hull can fit. Don't try too many times due to performance // concerns. for( int i = 0 ; i < 5 ; i++ ) { CNPCSpawnDestination *pRandomDest = pDestinations[ rand() % count ]; if( HumanHullFits( pRandomDest->GetAbsOrigin() ) ) { return pRandomDest; } } return NULL; } else { if( m_CriterionDistance == TS_DIST_NEAREST ) { float flNearest = FLT_MAX; CNPCSpawnDestination *pNearest = NULL; for( int i = 0 ; i < count ; i++ ) { Vector vecTest = pDestinations[ i ]->GetAbsOrigin(); pPlayer = UTIL_GetNearestPlayer(vecTest); float flDist = ( vecTest - pPlayer->GetAbsOrigin() ).Length(); if ( m_iMinSpawnDistance != 0 && m_iMinSpawnDistance > flDist ) continue; if( flDist < flNearest && HumanHullFits( vecTest ) ) { flNearest = flDist; pNearest = pDestinations[ i ]; } } return pNearest; } else { float flFarthest = 0; CNPCSpawnDestination *pFarthest = NULL; for( int i = 0 ; i < count ; i++ ) { Vector vecTest = pDestinations[ i ]->GetAbsOrigin(); float flDist = ( vecTest - pPlayer->GetAbsOrigin() ).Length(); if ( m_iMinSpawnDistance != 0 && m_iMinSpawnDistance > flDist ) continue; if( flDist > flFarthest && HumanHullFits( vecTest ) ) { flFarthest = flDist; pFarthest = pDestinations[ i ]; } } return pFarthest; } } return NULL; }
void CNPC_Dog::SetPlayerAvoidState( void ) { bool bIntersectingBoneFollowers = false; bool bIntersectingNPCBox = false; Vector vNothing; GetSequenceLinearMotion( GetSequence(), &vNothing ); bool bIsMoving = ( IsMoving() || ( vNothing != vec3_origin ) ); //If we are coming out of a script, check if we are stuck inside the player. if ( m_bPerformAvoidance || ( ShouldPlayerAvoid() && bIsMoving ) ) { trace_t trace; Vector vMins, vMaxs; Vector vWorldMins, vWorldMaxs; Vector vPlayerMins, vPlayerMaxs; physfollower_t *pBone; int i; #ifdef SecobMod__Enable_Fixed_Multiplayer_AI CBasePlayer *pLocalPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); #else CBasePlayer *pLocalPlayer = AI_GetSinglePlayer(); #endif //SecobMod__Enable_Fixed_Multiplayer_AI if ( pLocalPlayer ) { vWorldMins = WorldAlignMins(); vWorldMaxs = WorldAlignMaxs(); vPlayerMins = pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMins(); vPlayerMaxs = pLocalPlayer->GetAbsOrigin() + pLocalPlayer->WorldAlignMaxs(); // check if the player intersects the bounds of any of the bone followers for ( i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ ) { pBone = m_BoneFollowerManager.GetBoneFollower( i ); if ( pBone && pBone->hFollower ) { pBone->hFollower->CollisionProp()->WorldSpaceSurroundingBounds( &vMins, &vMaxs ); if ( IsBoxIntersectingBox( vMins, vMaxs, vPlayerMins, vPlayerMaxs ) ) { bIntersectingBoneFollowers = true; break; } } } bIntersectingNPCBox = IsBoxIntersectingBox( GetAbsOrigin() + vWorldMins, GetAbsOrigin() + vWorldMaxs, vPlayerMins, vPlayerMaxs ); if ( ai_debug_avoidancebounds.GetBool() ) { int iRed = ( bIntersectingNPCBox == true ) ? 255 : 0; NDebugOverlay::Box( GetAbsOrigin(), vWorldMins, vWorldMaxs, iRed, 0, 255, 64, 0.1 ); // draw the bounds of the bone followers for ( i = 0; i < m_BoneFollowerManager.GetNumBoneFollowers(); i++ ) { pBone = m_BoneFollowerManager.GetBoneFollower( i ); if ( pBone && pBone->hFollower ) { pBone->hFollower->CollisionProp()->WorldSpaceSurroundingBounds( &vMins, &vMaxs ); iRed = ( IsBoxIntersectingBox( vMins, vMaxs, vPlayerMins, vPlayerMaxs ) ) ? 255 : 0; NDebugOverlay::Box( vec3_origin, vMins, vMaxs, iRed, 0, 255, 64, 0.1 ); } } } } } m_bPlayerAvoidState = ShouldPlayerAvoid(); m_bPerformAvoidance = bIntersectingNPCBox || bIntersectingBoneFollowers; if ( GetCollisionGroup() == COLLISION_GROUP_NPC || GetCollisionGroup() == COLLISION_GROUP_NPC_ACTOR ) { if ( bIntersectingNPCBox == true ) { SetCollisionGroup( COLLISION_GROUP_NPC_ACTOR ); } else { SetCollisionGroup( COLLISION_GROUP_NPC ); } if ( bIntersectingBoneFollowers == true ) { MantainBoneFollowerCollisionGroups( COLLISION_GROUP_NPC_ACTOR ); } else { MantainBoneFollowerCollisionGroups( COLLISION_GROUP_NPC ); } } }
void CNPC_Zombine::GatherGrenadeConditions( void ) { if ( m_iGrenadeCount <= 0 ) return; if ( g_flZombineGrenadeTimes > gpGlobals->curtime ) return; if ( m_flGrenadePullTime > gpGlobals->curtime ) return; if ( m_flSuperFastAttackTime >= gpGlobals->curtime ) return; if ( HasGrenade() ) return; if ( GetEnemy() == NULL ) return; if ( FVisible( GetEnemy() ) == false ) return; if ( IsSprinting() ) return; if ( IsOnFire() ) return; if ( IsRunningDynamicInteraction() == true ) return; if ( m_ActBusyBehavior.IsActive() ) return; //Secobmod FixMe //CBasePlayer *pPlayer = AI_GetSinglePlayer(); CBasePlayer *pPlayer = UTIL_GetNearestPlayer(GetAbsOrigin()); if ( pPlayer && pPlayer->FVisible( this ) ) { float flLengthToPlayer = (pPlayer->GetAbsOrigin() - GetAbsOrigin()).Length(); float flLengthToEnemy = flLengthToPlayer; if ( pPlayer != GetEnemy() ) { flLengthToEnemy = ( GetEnemy()->GetAbsOrigin() - GetAbsOrigin()).Length(); } if ( flLengthToPlayer <= GRENADE_PULL_MAX_DISTANCE && flLengthToEnemy <= GRENADE_PULL_MAX_DISTANCE ) { float flPullChance = 1.0f - ( flLengthToEnemy / GRENADE_PULL_MAX_DISTANCE ); m_flGrenadePullTime = gpGlobals->curtime + 0.5f; if ( flPullChance >= random->RandomFloat( 0.0f, 1.0f ) ) { g_flZombineGrenadeTimes = gpGlobals->curtime + 10.0f; SetCondition( COND_ZOMBINE_GRENADE ); } } } }