//========================================================= // NoFriendlyFire - checks for possibility of friendly fire // // Builds a large box in front of the grunt and checks to see // if any squad members are in that box. //========================================================= bool CHL1BaseNPC::NoFriendlyFire( void ) { if ( !m_pSquad ) { return true; } CPlane backPlane; CPlane leftPlane; CPlane rightPlane; Vector vecLeftSide; Vector vecRightSide; Vector v_left; Vector vForward, vRight, vUp; QAngle vAngleToEnemy; if ( GetEnemy() != NULL ) { //!!!BUGBUG - to fix this, the planes must be aligned to where the monster will be firing its gun, not the direction it is facing!!! VectorAngles( ( GetEnemy()->WorldSpaceCenter() - GetAbsOrigin() ), vAngleToEnemy ); AngleVectors ( vAngleToEnemy, &vForward, &vRight, &vUp ); } else { // if there's no enemy, pretend there's a friendly in the way, so the grunt won't shoot. return false; } vecLeftSide = GetAbsOrigin() - ( vRight * ( WorldAlignSize().x * 1.5 ) ); vecRightSide = GetAbsOrigin() + ( vRight * ( WorldAlignSize().x * 1.5 ) ); v_left = vRight * -1; leftPlane.InitializePlane ( vRight, vecLeftSide ); rightPlane.InitializePlane ( v_left, vecRightSide ); backPlane.InitializePlane ( vForward, GetAbsOrigin() ); AISquadIter_t iter; for ( CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) ) { if ( pSquadMember == NULL ) continue; if ( pSquadMember == this ) continue; if ( backPlane.PointInFront ( pSquadMember->GetAbsOrigin() ) && leftPlane.PointInFront ( pSquadMember->GetAbsOrigin() ) && rightPlane.PointInFront ( pSquadMember->GetAbsOrigin()) ) { // this guy is in the check volume! Don't shoot! return false; } } return true; }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CAI_RappelBehavior::CreateZipline() { #if 1 if( !m_hLine ) { int attachment = GetOuter()->LookupAttachment( "zipline" ); if( attachment != -1 ) { CBeam *pBeam; pBeam = CBeam::BeamCreate( "cable/cable.vmt", 1 ); pBeam->SetColor( 150, 150, 150 ); pBeam->SetWidth( 0.3 ); pBeam->SetEndWidth( 0.3 ); CAI_BaseNPC *pNPC = GetOuter(); pBeam->PointEntInit( pNPC->GetAbsOrigin() + Vector( 0, 0, 80 ), pNPC ); pBeam->SetEndAttachment( attachment ); m_hLine.Set( pBeam ); } } #endif }
bool CPhysicsNPCSolver::IsIntersecting() { CAI_BaseNPC *pNPC = m_hNPC.Get(); CBaseEntity *pPhysics = m_hEntity.Get(); if ( pNPC && pPhysics ) { Ray_t ray; // bloated bounds to force slight separation Vector mins = pNPC->WorldAlignMins() - Vector(1,1,1); Vector maxs = pNPC->WorldAlignMaxs() + Vector(1,1,1); ray.Init( pNPC->GetAbsOrigin(), pNPC->GetAbsOrigin(), mins, maxs ); trace_t tr; enginetrace->ClipRayToEntity( ray, pNPC->PhysicsSolidMaskForEntity(), pPhysics, &tr ); if ( tr.startsolid ) return true; } return false; }
//----------------------------------------------------------------------------- // Purpose: Gets event from anim stream and throws the object // Input : // Output : //----------------------------------------------------------------------------- void CWeaponMolotov::Operator_HandleAnimEvent( animevent_t *pEvent, CBaseCombatCharacter *pOperator ) { switch( pEvent->event ) { case EVENT_WEAPON_THROW: { CAI_BaseNPC *pNPC = GetOwner()->MyNPCPointer(); if (!pNPC) { return; } CBaseEntity *pEnemy = pNPC->GetEnemy(); if (!pEnemy) { return; } Vector vec_target = pNPC->GetEnemyLKP(); // ----------------------------------------------------- // Get position of throw // ----------------------------------------------------- // If owner has a hand, set position to the hand bone position Vector launchPos; int iBIndex = pNPC->LookupBone("Bip01 R Hand"); if (iBIndex != -1) { Vector origin; QAngle angles; pNPC->GetBonePosition( iBIndex, launchPos, angles); } // Otherwise just set to in front of the owner else { Vector vFacingDir = pNPC->BodyDirection2D( ); vFacingDir = vFacingDir * 60.0; launchPos = pNPC->GetAbsOrigin()+vFacingDir; } //Vector vecVelocity = VecCheckToss( pNPC, launchPos, vec_target, 1.0 ); ThrowMolotov( launchPos, m_vecTossVelocity); // Drop the weapon and remove as no more ammo pNPC->Weapon_Drop( this ); UTIL_Remove( this ); } break; default: BaseClass::Operator_HandleAnimEvent( pEvent, pOperator ); break; } }
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 ); } } } } }
void CNPC_Vortigaunt::CallForHelp( char *szClassname, float flDist, CBaseEntity * pEnemy, Vector &vecLocation ) { // ALERT( at_aiconsole, "help " ); // skip ones not on my netname if ( !m_pSquad ) return; AISquadIter_t iter; for (CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) ) { float d = ( GetAbsOrigin() - pSquadMember->GetAbsOrigin() ).Length(); if ( d < flDist ) { pSquadMember->Remember( bits_MEMORY_PROVOKED ); pSquadMember->UpdateEnemyMemory( pEnemy, vecLocation ); } } }
//----------------------------------------------------------------------------- // Purpose: Check the weapon LOS for an owner at an arbitrary position // If bSetConditions is true, LOS related conditions will also be set //----------------------------------------------------------------------------- bool CASW_Weapon::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) { bool bHasLOS = BaseClass::WeaponLOSCondition(ownerPos, targetPos, bSetConditions); // if the weapon has LOS, then do another wider trace to check we don't hit any friendlies // this is to stop the AI marines shooting way too close to other marines, which stops the player thinking about positioning so much if (bHasLOS && GetOwner() && asw_weapon_safety_hull.GetFloat() > 0) { CAI_BaseNPC* npcOwner = GetOwner()->MyNPCPointer(); Vector vecRelativeShootPosition; VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition ); // Find its relative shoot position Vector barrelPos = ownerPos + vecRelativeShootPosition; CASWWeaponLOSFilter traceFilter( GetOwner(), npcOwner->GetEnemy(), COLLISION_GROUP_BREAKABLE_GLASS ); // Use the custom LOS trace filter trace_t tr; UTIL_TraceHull( barrelPos, targetPos, Vector(-asw_weapon_safety_hull.GetFloat(), -asw_weapon_safety_hull.GetFloat(), -asw_weapon_safety_hull.GetFloat()), Vector(asw_weapon_safety_hull.GetFloat(), asw_weapon_safety_hull.GetFloat(), asw_weapon_safety_hull.GetFloat()), MASK_SHOT, &traceFilter, &tr ); if ( tr.fraction == 1.0 || tr.m_pEnt == npcOwner->GetEnemy() ) return true; // if a friendly is in the way, then we report failure CBaseCombatCharacter *pBCC = ToBaseCombatCharacter( tr.m_pEnt ); if ( pBCC ) { if ( npcOwner->IRelationType( pBCC ) == D_HT ) return true; if ( bSetConditions ) { npcOwner->SetCondition( COND_WEAPON_BLOCKED_BY_FRIEND ); } return false; } } return bHasLOS; }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_Monk::GatherConditions() { BaseClass::GatherConditions(); // Build my zombie danger index! m_iNumZombies = 0; m_iDangerousZombies = 0; AISightIter_t iter; CBaseEntity *pSightEnt; pSightEnt = GetSenses()->GetFirstSeenEntity( &iter ); while( pSightEnt ) { if( pSightEnt->Classify() == CLASS_ZOMBIE && pSightEnt->IsAlive() ) { // Is this zombie coming for me? CAI_BaseNPC *pZombie = dynamic_cast<CAI_BaseNPC*>(pSightEnt); if( pZombie && pZombie->GetEnemy() == this ) { m_iNumZombies++; // if this zombie is close enough to attack, add him to the zombie danger! float flDist; flDist = (pZombie->GetAbsOrigin() - GetAbsOrigin()).Length2DSqr(); if( flDist <= 128.0f * 128.0f ) { m_iDangerousZombies++; } } } pSightEnt = GetSenses()->GetNextSeenEntity( &iter ); } if( m_iDangerousZombies >= 3 || (GetEnemy() && GetHealth() < 25) ) { // I see many zombies, or I'm quite injured. SpeakIfAllowed( TLK_HELP_ME ); } // NOTE!!!!!! This code assumes grigori is using annabelle! ClearCondition(COND_LOW_PRIMARY_AMMO); if ( GetActiveWeapon() ) { if ( GetActiveWeapon()->UsesPrimaryAmmo() ) { if (!GetActiveWeapon()->HasPrimaryAmmo() ) { SetCondition(COND_NO_PRIMARY_AMMO); } else if ( m_NPCState != NPC_STATE_COMBAT && GetActiveWeapon()->UsesClipsForAmmo1() && GetActiveWeapon()->Clip1() < 2 ) { // Don't send a low ammo message unless we're not in combat. SetCondition(COND_LOW_PRIMARY_AMMO); } } } }
//----------------------------------------------------------------------------- // Purpose: Check the weapon LOS for an owner at an arbitrary position // If bSetConditions is true, LOS related conditions will also be set //----------------------------------------------------------------------------- bool CBaseCombatWeapon::WeaponLOSCondition( const Vector &ownerPos, const Vector &targetPos, bool bSetConditions ) { // -------------------- // Check for occlusion // -------------------- CAI_BaseNPC* npcOwner = m_hOwner.Get()->MyNPCPointer(); // Find its relative shoot position Vector vecRelativeShootPosition; VectorSubtract( npcOwner->Weapon_ShootPosition(), npcOwner->GetAbsOrigin(), vecRelativeShootPosition ); Vector barrelPos = ownerPos + vecRelativeShootPosition; // Use the custom LOS trace filter CWeaponLOSFilter traceFilter( m_hOwner.Get(), npcOwner->GetEnemy(), COLLISION_GROUP_BREAKABLE_GLASS ); trace_t tr; UTIL_TraceLine( barrelPos, targetPos, MASK_SHOT, &traceFilter, &tr ); // See if we completed the trace without interruption if ( tr.fraction == 1.0 ) { if ( ai_debug_shoot_positions.GetBool() ) { NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 ); } return true; } CBaseEntity *pHitEnt = tr.m_pEnt; CBasePlayer *pEnemyPlayer = ToBasePlayer( npcOwner->GetEnemy() ); // is player in a vehicle? if so, verify vehicle is target and return if so (so npc shoots at vehicle) if ( pEnemyPlayer && pEnemyPlayer->IsInAVehicle() ) { // Ok, player in vehicle, check if vehicle is target we're looking at, fire if it is // Also, check to see if the owner of the entity is the vehicle, in which case it's valid too. // This catches vehicles that use bone followers. CBaseEntity *pVehicle = pEnemyPlayer->GetVehicle()->GetVehicleEnt(); if ( pHitEnt == pVehicle || pHitEnt->GetOwnerEntity() == pVehicle ) return true; } // Hitting our enemy is a success case if ( pHitEnt == npcOwner->GetEnemy() ) { if ( ai_debug_shoot_positions.GetBool() ) { NDebugOverlay::Line( barrelPos, targetPos, 0, 255, 0, false, 1.0 ); } return true; } // If a vehicle is blocking the view, grab its driver and use that as the combat character CBaseCombatCharacter *pBCC; IServerVehicle *pVehicle = pHitEnt->GetServerVehicle(); if ( pVehicle ) { pBCC = pVehicle->GetPassenger( ); } else { pBCC = ToBaseCombatCharacter( pHitEnt ); } if ( pBCC ) { if ( npcOwner->IRelationType( pBCC ) == D_HT ) return true; if ( bSetConditions ) { npcOwner->SetCondition( COND_WEAPON_BLOCKED_BY_FRIEND ); } } else if ( bSetConditions ) { npcOwner->SetCondition( COND_WEAPON_SIGHT_OCCLUDED ); npcOwner->SetEnemyOccluder( pHitEnt ); if( ai_debug_shoot_positions.GetBool() ) { NDebugOverlay::Line( tr.startpos, tr.endpos, 255, 0, 0, false, 1.0 ); } } return false; }
//--------------------------------------------------------- // Purpose: //--------------------------------------------------------- void CNPC_Assassin::StartTask( const Task_t *pTask ) { switch( pTask->iTask ) { case TASK_ASSASSIN_SET_EYE_STATE: { SetEyeState( (eyeState_t) ( (int) pTask->flTaskData ) ); TaskComplete(); } break; case TASK_ASSASSIN_EVADE: { Activity flipAct = ACT_INVALID; const Vector *avoidPos = ( GetEnemy() != NULL ) ? &(GetEnemy()->GetAbsOrigin()) : NULL; for ( int i = FLIP_LEFT; i < NUM_FLIP_TYPES; i++ ) { if ( CanFlip( i, flipAct, avoidPos ) ) { // Don't flip back to where we just were if ( ( ( i == FLIP_LEFT ) && ( m_nLastFlipType == FLIP_RIGHT ) ) || ( ( i == FLIP_RIGHT ) && ( m_nLastFlipType == FLIP_LEFT ) ) || ( ( i == FLIP_FORWARD ) && ( m_nLastFlipType == FLIP_BACKWARD ) ) || ( ( i == FLIP_BACKWARD ) && ( m_nLastFlipType == FLIP_FORWARD ) ) ) { flipAct = ACT_INVALID; continue; } m_nNumFlips--; ResetIdealActivity( flipAct ); m_flNextFlipTime = gpGlobals->curtime + 2.0f; m_nLastFlipType = i; break; } } if ( flipAct == ACT_INVALID ) { m_nNumFlips = 0; m_nLastFlipType = -1; m_flNextFlipTime = gpGlobals->curtime + 2.0f; TaskFail( "Unable to find flip evasion direction!\n" ); } } break; case TASK_ASSASSIN_GET_PATH_TO_VANTAGE_POINT: { assert( GetEnemy() != NULL ); if ( GetEnemy() == NULL ) break; Vector goalPos; CHintCriteria hint; // Find a disadvantage node near the player, but away from ourselves hint.SetHintType( HINT_TACTICAL_ENEMY_DISADVANTAGED ); hint.AddExcludePosition( GetAbsOrigin(), 256 ); hint.AddExcludePosition( GetEnemy()->GetAbsOrigin(), 256 ); if ( ( m_pSquad != NULL ) && ( m_pSquad->NumMembers() > 1 ) ) { AISquadIter_t iter; for ( CAI_BaseNPC *pSquadMember = m_pSquad->GetFirstMember( &iter ); pSquadMember; pSquadMember = m_pSquad->GetNextMember( &iter ) ) { if ( pSquadMember == NULL ) continue; hint.AddExcludePosition( pSquadMember->GetAbsOrigin(), 128 ); } } hint.SetFlag( bits_HINT_NODE_NEAREST ); CAI_Hint *pHint = CAI_HintManager::FindHint( this, GetEnemy()->GetAbsOrigin(), &hint ); if ( pHint == NULL ) { TaskFail( "Unable to find vantage point!\n" ); break; } pHint->GetPosition( this, &goalPos ); AI_NavGoal_t goal( goalPos ); //Try to run directly there if ( GetNavigator()->SetGoal( goal ) == false ) { TaskFail( "Unable to find path to vantage point!\n" ); break; } TaskComplete(); } break; default: BaseClass::StartTask( pTask ); break; } }
//--------------------------------------------------------- //--------------------------------------------------------- void CAI_Relationship::ChangeRelationships( int disposition, int iReverting, CBaseEntity *pActivator, CBaseEntity *pCaller ) { if( iReverting != NOT_REVERTING && m_iPreviousDisposition == -1 ) { // Trying to revert without having ever set the relationships! DevMsg( 2, "ai_relationship cannot revert changes before they are applied!\n"); return; } const int MAX_HANDLED = 512; CUtlVectorFixed<CBaseCombatCharacter *, MAX_HANDLED> subjectList; CUtlVectorFixed<CBaseCombatCharacter *, MAX_HANDLED> targetList; // Add any special subjects we found CBaseEntity *pSpecialSubject = FindEntityForProceduralName( m_iszSubject, pActivator, pCaller ); if ( pSpecialSubject && pSpecialSubject->MyCombatCharacterPointer() ) { subjectList.AddToTail( pSpecialSubject->MyCombatCharacterPointer() ); } // Add any special targets we found CBaseEntity *pSpecialTarget = FindEntityForProceduralName( m_target, pActivator, pCaller ); if ( pSpecialTarget && pSpecialTarget->MyCombatCharacterPointer() ) { targetList.AddToTail( pSpecialTarget->MyCombatCharacterPointer() ); } // ------------------------------- // Search for targets and subjects // ------------------------------- float radiusSq = Square( m_flRadius ); // Search players first for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { if ( subjectList.Count() == MAX_HANDLED || targetList.Count() == MAX_HANDLED ) { DevMsg( "Too many entities handled by ai_relationship %s\n", GetDebugName() ); break; } CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if ( pPlayer ) { if( IsASubject( pPlayer ) ) { if ( m_flRadius == 0.0 || GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ) <= radiusSq ) subjectList.AddToTail( pPlayer ); } else if( IsATarget( pPlayer ) ) { targetList.AddToTail( pPlayer ); } } } // Search NPCs for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) { if ( subjectList.Count() == MAX_HANDLED || targetList.Count() == MAX_HANDLED ) { DevMsg( "Too many entities handled by ai_relationship %s\n", GetDebugName() ); break; } CAI_BaseNPC *pNPC = (g_AI_Manager.AccessAIs())[i]; if ( pNPC ) { if( IsASubject( pNPC ) ) { if ( m_flRadius == 0.0 || GetAbsOrigin().DistToSqr( pNPC->GetAbsOrigin() ) <= radiusSq ) subjectList.AddToTail( pNPC ); } else if( IsATarget( pNPC ) ) { targetList.AddToTail( pNPC ); } } } // If either list is still empty, we have a problem. if( subjectList.Count() == 0 ) { DevMsg( 2, "ai_relationship '%s' finds no subject(s) called: %s\n", GetDebugName(), STRING( m_iszSubject ) ); return; } else if ( targetList.Count() == 0 ) { DevMsg( 2, "ai_relationship '%s' finds no target(s) called: %s\n", GetDebugName(), STRING( m_target ) ); return; } // Ok, lists are populated. Apply all relationships. for ( int i = 0 ; i < subjectList.Count(); i++ ) { CBaseCombatCharacter *pSubject = subjectList[ i ]; for ( int j = 0 ; j < targetList.Count(); j++ ) { CBaseCombatCharacter *pTarget = targetList[ j ]; if ( m_iPreviousDisposition == -1 && iReverting == NOT_REVERTING ) { // Set previous disposition. m_iPreviousDisposition = pSubject->IRelationType( pTarget ); m_iPreviousRank = pSubject->IRelationPriority( pTarget ); } if ( iReverting == REVERTING_TO_PREV ) { pSubject->AddEntityRelationship( pTarget, (Disposition_t)m_iPreviousDisposition, m_iPreviousRank ); if( m_bReciprocal ) { pTarget->AddEntityRelationship( pSubject, (Disposition_t)m_iPreviousDisposition, m_iPreviousRank ); } } else if ( iReverting == REVERTING_TO_DEFAULT ) { pSubject->RemoveEntityRelationship( pTarget ); if( m_bReciprocal ) { pTarget->RemoveEntityRelationship( pSubject ); } } else if( pSubject->IRelationType(pTarget) != disposition || pSubject->IRelationPriority(pTarget) != m_iRank || HasSpawnFlags( SF_RELATIONSHIP_NOTIFY_SUBJECT ) || HasSpawnFlags( SF_RELATIONSHIP_NOTIFY_TARGET ) ) { // Apply the relationship to the subject pSubject->AddEntityRelationship( pTarget, (Disposition_t)disposition, m_iRank ); // Make the subject aware of the target if ( HasSpawnFlags( SF_RELATIONSHIP_NOTIFY_SUBJECT ) ) { DiscloseNPCLocation( pSubject, pTarget ); } // Make the target aware of the subject if ( HasSpawnFlags( SF_RELATIONSHIP_NOTIFY_TARGET ) ) { DiscloseNPCLocation( pTarget, pSubject ); } // This relationship is applied to target and subject alike if ( m_bReciprocal ) { // Apply the relationship to the target pTarget->AddEntityRelationship( pSubject, (Disposition_t)disposition, m_iRank ); } } } } }
//--------------------------------------------------------- // Returns distance to the nearest BaseCombatCharacter. //--------------------------------------------------------- float CBounceBomb::FindNearestNPC() { float flNearest = (BOUNCEBOMB_WARN_RADIUS * BOUNCEBOMB_WARN_RADIUS) + 1.0; // Assume this search won't find anyone. SetNearestNPC( NULL ); CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); int nAIs = g_AI_Manager.NumAIs(); for ( int i = 0; i < nAIs; i++ ) { CAI_BaseNPC *pNPC = ppAIs[ i ]; if( pNPC->IsAlive() ) { // ignore hidden objects if ( pNPC->IsEffectActive( EF_NODRAW ) ) continue; // Don't bother with NPC's that are below me. if( pNPC->EyePosition().z < GetAbsOrigin().z ) continue; // Disregard things that want to be disregarded if( pNPC->Classify() == CLASS_NONE ) continue; // Disregard bullseyes if( pNPC->Classify() == CLASS_BULLSEYE ) continue; // Disregard turrets if( pNPC->m_iClassname == gm_iszFloorTurretClassname || pNPC->m_iClassname == gm_iszGroundTurretClassname ) continue; float flDist = (GetAbsOrigin() - pNPC->GetAbsOrigin()).LengthSqr(); if( flDist < flNearest ) { // Now do a visibility test. if( FVisible( pNPC, MASK_SOLID_BRUSHONLY ) ) { flNearest = flDist; SetNearestNPC( pNPC ); } } } } // finally, check the player. CBasePlayer *pPlayer = UTIL_GetLocalPlayer(); if( pPlayer && !(pPlayer->GetFlags() & FL_NOTARGET) ) { float flDist = (pPlayer->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr(); if( flDist < flNearest && FVisible( pPlayer, MASK_SOLID_BRUSHONLY ) ) { flNearest = flDist; SetNearestNPC( pPlayer ); } } if( m_hNearestNPC.Get() ) { // If sprite is active, update its color to reflect who is nearest. if( IsFriend( m_hNearestNPC ) ) { if( m_bFoeNearest ) { // Changing state to where a friend is nearest. if( IsFriend( m_hNearestNPC ) ) { // Friend UpdateLight( true, 0, 255, 0, 190 ); m_bFoeNearest = false; } } } else // it's a foe { if( !m_bFoeNearest ) { // Changing state to where a foe is nearest. UpdateLight( true, 255, 0, 0, 190 ); m_bFoeNearest = true; } } } return sqrt( flNearest ); }
//----------------------------------------------------------------------------- // Look for a target //----------------------------------------------------------------------------- bool CObjectSentrygun::FindTarget() { // Disable the sentry guns for ifm. if ( tf_sentrygun_notarget.GetBool() ) return false; if ( IsInCommentaryMode() ) return false; // Sapper, etc. if ( IsDisabled() ) return false; // Loop through players within 1100 units (sentry range). Vector vecSentryOrigin = EyePosition(); // Find the opposing team list. CTFPlayer *pPlayer = ToTFPlayer( GetOwner() ); CUtlVector<CTFTeam *> pTeamList; CTFTeam *pTeam = NULL; //CTFTeam *pTeam = pPlayer->GetOpposingTFTeam(); //if ( !pTeam ) // return false; if ( pPlayer ) { // Try builder's team. pTeam = pPlayer->GetTFTeam(); } else { // If we have no builder use our own team number instead. pTeam = GetTFTeam(); } if ( pTeam ) pTeam->GetOpposingTFTeamList( &pTeamList ); else return false; // If we have an enemy get his minimum distance to check against. Vector vecSegment; Vector vecTargetCenter; float flMinDist2 = 1100.0f * 1100.0f; CBaseEntity *pTargetCurrent = NULL; CBaseEntity *pTargetOld = m_hEnemy.Get(); float flOldTargetDist2 = FLT_MAX; // Sentries will try to target players first, then objects. However, if the enemy held was an object it will continue // to try and attack it first. for (int i = 0; i < pTeamList.Size(); i++) { int nTeamCount = pTeamList[i]->GetNumPlayers(); for (int iPlayer = 0; iPlayer < nTeamCount; ++iPlayer) { CTFPlayer *pTargetPlayer = static_cast<CTFPlayer*>(pTeamList[i]->GetPlayer(iPlayer)); if (pTargetPlayer == NULL) continue; // Make sure the player is alive. if (!pTargetPlayer->IsAlive()) continue; if (pTargetPlayer->GetFlags() & FL_NOTARGET) continue; vecTargetCenter = pTargetPlayer->GetAbsOrigin(); vecTargetCenter += pTargetPlayer->GetViewOffset(); VectorSubtract(vecTargetCenter, vecSentryOrigin, vecSegment); float flDist2 = vecSegment.LengthSqr(); // Store the current target distance if we come across it if (pTargetPlayer == pTargetOld) { flOldTargetDist2 = flDist2; } // Check to see if the target is closer than the already validated target. if (flDist2 > flMinDist2) continue; // It is closer, check to see if the target is valid. if (ValidTargetPlayer(pTargetPlayer, vecSentryOrigin, vecTargetCenter)) { flMinDist2 = flDist2; pTargetCurrent = pTargetPlayer; } } // If we already have a target, don't check objects. if (pTargetCurrent == NULL) { int nTeamObjectCount = pTeamList[i]->GetNumObjects(); for (int iObject = 0; iObject < nTeamObjectCount; ++iObject) { CBaseObject *pTargetObject = pTeamList[i]->GetObject(iObject); if (!pTargetObject) continue; vecTargetCenter = pTargetObject->GetAbsOrigin(); vecTargetCenter += pTargetObject->GetViewOffset(); VectorSubtract(vecTargetCenter, vecSentryOrigin, vecSegment); float flDist2 = vecSegment.LengthSqr(); // Store the current target distance if we come across it if (pTargetObject == pTargetOld) { flOldTargetDist2 = flDist2; } // Check to see if the target is closer than the already validated target. if (flDist2 > flMinDist2) continue; // It is closer, check to see if the target is valid. if (ValidTargetObject(pTargetObject, vecSentryOrigin, vecTargetCenter)) { flMinDist2 = flDist2; pTargetCurrent = pTargetObject; } } } // NPCs have lowest priority. if (pTargetCurrent == NULL) { int nTeamNPCCount = pTeamList[i]->GetNumNPCs(); for (int iNPC = 0; iNPC < nTeamNPCCount; ++iNPC) { CAI_BaseNPC *pTargetNPC = pTeamList[i]->GetNPC(iNPC); if (!pTargetNPC) continue; // Make sure NPC is alive. if (!pTargetNPC->IsAlive()) continue; vecTargetCenter = pTargetNPC->GetAbsOrigin(); vecTargetCenter += pTargetNPC->GetViewOffset(); //vecTargetCenter = pTargetNPC->WorldSpaceCenter(); VectorSubtract(vecTargetCenter, vecSentryOrigin, vecSegment); float flDist2 = vecSegment.LengthSqr(); // Store the current target distance if we come across it if (pTargetNPC == pTargetOld) { flOldTargetDist2 = flDist2; } // Check to see if the target is closer than the already validated target. if (flDist2 > flMinDist2) continue; // It is closer, check to see if the target is valid. if (ValidTargetNPC(pTargetNPC, vecSentryOrigin, vecTargetCenter)) { flMinDist2 = flDist2; pTargetCurrent = pTargetNPC; } } } // We have a target. if (pTargetCurrent) { if (pTargetCurrent != pTargetOld) { // flMinDist2 is the new target's distance // flOldTargetDist2 is the old target's distance // Don't switch unless the new target is closer by some percentage if (flMinDist2 < (flOldTargetDist2 * 0.75f)) { FoundTarget(pTargetCurrent, vecSentryOrigin); } } return true; } } return false; }
//========================================================= // Crea un hijo en la ubicación especificada. //========================================================= bool CDirector_Manager::AddChild(const Vector &vecPosition, int flag) { // No se ha podido acceder al Director. if ( !Director() ) return false; // No es posible crear un hijo aquí. if ( !CanMake(vecPosition) ) return false; // Creamos un hijo de la lista. const char *pChildName = GetChildClass(); CAI_BaseNPC *pChild = (CAI_BaseNPC *)CreateEntityByName(pChildName); QAngle angles = RandomAngle(0, 360); angles.x = 0.0; angles.z = 0.0; pChild->SetAbsAngles(angles); // Establecemos la ubicación de creación. pChild->SetAbsOrigin(vecPosition); // Debe caer al suelo y desaparecer. pChild->AddSpawnFlags(SF_NPC_FALL_TO_GROUND); pChild->AddSpawnFlags(SF_NPC_FADE_CORPSE); UTIL_DropToFloor(pChild, MASK_SOLID); ConVarRef director_debug("director_debug"); DispatchSpawn(pChild); pChild->SetOwnerEntity(Director()); DispatchActivate(pChild); // ¡¡NO CAMBIAR!! pChild->SetName(MAKE_STRING(CHILD_NAME)); // Sin colisiones. if ( flag == SPAWN_NO_COLLISION || flag == SPAWN_NO_COLLISION_AND_POWERFUL ) { pChild->SetCollisionGroup(COLLISION_GROUP_SPECIAL_NPC); Vector vecOriginRadius; if ( Director()->Status == PANIC && RandomInt(0, 4) == 2 ) pChild->SetCollisionGroup(COLLISION_GROUP_NPC); // Intentamos crearlo en un radio de 100 if ( CAI_BaseNPC::FindSpotForNPCInRadius(&vecOriginRadius, vecPosition, pChild, 150, false) ) { // Evitamos que se mueva por debajo del suelo. vecOriginRadius.z = vecPosition.z; // Movemos hacia esta ubicación. pChild->SetAbsOrigin(vecOriginRadius); // Marcamos al nodo afortunado. (Naranja) if ( director_debug.GetBool() ) NDebugOverlay::Box(vecOriginRadius, -Vector(10, 10, 10), Vector(10, 10, 10), 255, 128, 0, 10, 20.0f); } } // Poderoso. if ( flag == SPAWN_POWERFUL || flag == SPAWN_NO_COLLISION_AND_POWERFUL ) { int moreHealth = 3; // Normal: 5 más de salud. if ( InGameRules()->IsSkillLevel(SKILL_MEDIUM) ) moreHealth = 5; // Dificil: 8 más de salud. if ( InGameRules()->IsSkillLevel(SKILL_HARD) ) moreHealth = 8; // Más rápido. pChild->SetAddAccel(40); // Establecemos la nueva salud. pChild->SetMaxHealth(pChild->GetMaxHealth() + moreHealth); pChild->SetHealth(pChild->GetMaxHealth()); // Seleccionamos al jugador más cercano. float flDistance = 0.0f; CIN_Player *pPlayer = UTIL_GetNearestInPlayer(pChild->GetAbsOrigin(), flDistance); if ( pPlayer ) { // Le decimos que su nuevo enemigo es el jugador y le damos la ubicación de este. pChild->SetEnemy(pPlayer); pChild->UpdateEnemyMemory(pPlayer, pPlayer->GetAbsOrigin()); } } // Al parecer se atoro en una pared. if ( !PostSpawn(pChild) ) { // Marcamos al nodo desafortunado. (Negro) if ( director_debug.GetBool() ) NDebugOverlay::Box(vecPosition, -Vector(10, 10, 10), Vector(10, 10, 10), 0, 0, 0, 10, 3.0f); return false; } // Marcamos al nodo afortunado. (Rojo) if ( director_debug.GetBool() ) NDebugOverlay::Box(vecPosition, -Vector(10, 10, 10), Vector(10, 10, 10), 223, 1, 1, 10, 3.0f); DevMsg("[MANAGER] Se ha creado <%s> (faltan %i) \r\n", pChildName, Director()->SpawnQueue); ++Director()->ChildsSpawned; return true; }