//----------------------------------------------------------------------------- // 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; }
//----------------------------------------------------------------------------- // Purpose: // Input : flDot - // flDist - // Output : int //----------------------------------------------------------------------------- int CWeaponSMG1::WeaponRangeAttack2Condition( float flDot, float flDist ) { CAI_BaseNPC *npcOwner = GetOwner()->MyNPCPointer(); return COND_NONE; /* // -------------------------------------------------------- // Assume things haven't changed too much since last time // -------------------------------------------------------- if (gpGlobals->curtime < m_flNextGrenadeCheck ) return m_lastGrenadeCondition; */ // ----------------------- // If moving, don't check. // ----------------------- if ( npcOwner->IsMoving()) return COND_NONE; CBaseEntity *pEnemy = npcOwner->GetEnemy(); if (!pEnemy) return COND_NONE; Vector vecEnemyLKP = npcOwner->GetEnemyLKP(); if ( !( pEnemy->GetFlags() & FL_ONGROUND ) && pEnemy->GetWaterLevel() == 0 && vecEnemyLKP.z > (GetAbsOrigin().z + WorldAlignMaxs().z) ) { //!!!BUGBUG - we should make this check movetype and make sure it isn't FLY? Players who jump a lot are unlikely to // be grenaded. // don't throw grenades at anything that isn't on the ground! return COND_NONE; } // -------------------------------------- // Get target vector // -------------------------------------- Vector vecTarget; if (random->RandomInt(0,1)) { // magically know where they are vecTarget = pEnemy->WorldSpaceCenter(); } else { // toss it to where you last saw them vecTarget = vecEnemyLKP; } // vecTarget = m_vecEnemyLKP + (pEnemy->BodyTarget( GetLocalOrigin() ) - pEnemy->GetLocalOrigin()); // estimate position // vecTarget = vecTarget + pEnemy->m_vecVelocity * 2; if ( ( vecTarget - npcOwner->GetLocalOrigin() ).Length2D() <= COMBINE_MIN_GRENADE_CLEAR_DIST ) { // crap, I don't want to blow myself up m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. return (COND_NONE); } // --------------------------------------------------------------------- // Are any friendlies near the intended grenade impact area? // --------------------------------------------------------------------- CBaseEntity *pTarget = NULL; while ( ( pTarget = gEntList.FindEntityInSphere( pTarget, vecTarget, COMBINE_MIN_GRENADE_CLEAR_DIST ) ) != NULL ) { //Check to see if the default relationship is hatred, and if so intensify that if ( npcOwner->IRelationType( pTarget ) == D_LI ) { // crap, I might blow my own guy up. Don't throw a grenade and don't check again for a while. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. return (COND_WEAPON_BLOCKED_BY_FRIEND); } } // --------------------------------------------------------------------- // Check that throw is legal and clear // --------------------------------------------------------------------- // FIXME: speed is based on difficulty... Vector vecToss = VecCheckThrow( this, npcOwner->GetLocalOrigin() + Vector(0,0,60), vecTarget, 600.0, 0.5 ); if ( vecToss != vec3_origin ) { m_vecTossVelocity = vecToss; // don't check again for a while. // JAY: HL1 keeps checking - test? //m_flNextGrenadeCheck = gpGlobals->curtime; m_flNextGrenadeCheck = gpGlobals->curtime + 0.3; // 1/3 second. return COND_CAN_RANGE_ATTACK2; } else { // don't check again for a while. m_flNextGrenadeCheck = gpGlobals->curtime + 1; // one full second. return COND_WEAPON_SIGHT_OCCLUDED; } }
//----------------------------------------------------------------------------- // 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: //----------------------------------------------------------------------------- CBaseEntity *CAI_FuncTankBehavior::BestEnemy( void ) { // Only use this BestEnemy call when we are on the manned gun. if ( !m_hFuncTank ||!IsMounted() ) return BaseClass::BestEnemy(); CBaseEntity *pBestEnemy = NULL; int iBestDistSq = MAX_COORD_RANGE * MAX_COORD_RANGE; // so first visible entity will become the closest. int iBestPriority = -1000; bool bBestUnreachable = false; // Forces initial check bool bBestSeen = false; bool bUnreachable = false; int iDistSq; AIEnemiesIter_t iter; // Get the current npc for checking from. CAI_BaseNPC *pNPC = GetOuter(); if ( !pNPC ) return NULL; for( AI_EnemyInfo_t *pEMemory = GetEnemies()->GetFirst( &iter ); pEMemory != NULL; pEMemory = GetEnemies()->GetNext( &iter ) ) { CBaseEntity *pEnemy = pEMemory->hEnemy; if ( !pEnemy || !pEnemy->IsAlive() ) continue; // UNDONE: Move relationship checks into IsValidEnemy? if ( ( pEnemy->GetFlags() & FL_NOTARGET ) || ( pNPC->IRelationType( pEnemy ) != D_HT && pNPC->IRelationType( pEnemy ) != D_FR ) || !IsValidEnemy( pEnemy ) ) continue; if ( pEMemory->timeLastSeen < pNPC->GetAcceptableTimeSeenEnemy() ) continue; if ( pEMemory->timeValidEnemy > gpGlobals->curtime ) continue; // Skip enemies that have eluded me to prevent infinite loops if ( GetEnemies()->HasEludedMe( pEnemy ) ) continue; // Establish the reachability of this enemy bUnreachable = pNPC->IsUnreachable( pEnemy ); // Check view cone of the view tank here. bUnreachable = !m_hFuncTank->IsEntityInViewCone( pEnemy ); if ( !bUnreachable ) { // It's in the viewcone. Now make sure we have LOS to it. bUnreachable = !m_hFuncTank->HasLOSTo( pEnemy ); } // If best is reachable and current is unreachable, skip the unreachable enemy regardless of priority if ( !bBestUnreachable && bUnreachable ) continue; // If best is unreachable and current is reachable, always pick the current regardless of priority if ( bBestUnreachable && !bUnreachable ) { bBestSeen = ( pNPC->GetSenses()->DidSeeEntity( pEnemy ) || pNPC->FVisible( pEnemy ) ); // @TODO (toml 04-02-03): Need to optimize CanSeeEntity() so multiple calls in frame do not recalculate, rather cache iBestPriority = pNPC->IRelationPriority( pEnemy ); iBestDistSq = static_cast<int>((pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); pBestEnemy = pEnemy; bBestUnreachable = bUnreachable; } // If both are unreachable or both are reachable, chose enemy based on priority and distance else if ( pNPC->IRelationPriority( pEnemy ) > iBestPriority ) { // this entity is disliked MORE than the entity that we // currently think is the best visible enemy. No need to do // a distance check, just get mad at this one for now. iBestPriority = pNPC->IRelationPriority ( pEnemy ); iBestDistSq = static_cast<int>(( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); pBestEnemy = pEnemy; bBestUnreachable = bUnreachable; } else if ( pNPC->IRelationPriority( pEnemy ) == iBestPriority ) { // this entity is disliked just as much as the entity that // we currently think is the best visible enemy, so we only // get mad at it if it is closer. iDistSq = static_cast<int>(( pEnemy->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr()); bool bCloser = ( iDistSq < iBestDistSq ) ; if ( bCloser || !bBestSeen ) { // @TODO (toml 04-02-03): Need to optimize FVisible() so multiple calls in frame do not recalculate, rather cache bool fSeen = ( pNPC->GetSenses()->DidSeeEntity( pEnemy ) || pNPC->FVisible( pEnemy ) ); if ( ( bCloser && ( fSeen || !bBestSeen ) ) || ( !bCloser && !bBestSeen && fSeen ) ) { bBestSeen = fSeen; iBestDistSq = iDistSq; iBestPriority = pNPC->IRelationPriority( pEnemy ); pBestEnemy = pEnemy; bBestUnreachable = bUnreachable; } } } } return pBestEnemy; }
void CNPC_Zombine::HandleAnimEvent( animevent_t *pEvent ) { if ( pEvent->event == AE_ZOMBINE_PULLPIN ) { Vector vecStart; QAngle angles; GetAttachment( "grenade_attachment", vecStart, angles ); CBaseGrenade *pGrenade = Fraggrenade_Create( vecStart, vec3_angle, vec3_origin, AngularImpulse( 0, 0, 0 ), this, 3.5f, true ); if ( pGrenade ) { // Move physobject to shadow IPhysicsObject *pPhysicsObject = pGrenade->VPhysicsGetObject(); if ( pPhysicsObject ) { pGrenade->VPhysicsDestroyObject(); int iAttachment = LookupAttachment( "grenade_attachment"); pGrenade->SetMoveType( MOVETYPE_NONE ); pGrenade->SetSolid( SOLID_NONE ); pGrenade->SetCollisionGroup( COLLISION_GROUP_DEBRIS ); pGrenade->SetAbsOrigin( vecStart ); pGrenade->SetAbsAngles( angles ); pGrenade->SetParent( this, iAttachment ); pGrenade->SetDamage( 200.0f ); m_hGrenade = pGrenade; EmitSound( "Zombine.ReadyGrenade" ); // Tell player allies nearby to regard me! CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); CAI_BaseNPC *pNPC; for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) { pNPC = ppAIs[i]; if( pNPC->Classify() == CLASS_PLAYER_ALLY || pNPC->Classify() == CLASS_PLAYER_ALLY_VITAL && pNPC->FVisible(this) ) { int priority; Disposition_t disposition; priority = pNPC->IRelationPriority(this); disposition = pNPC->IRelationType(this); pNPC->AddEntityRelationship( this, disposition, priority + 1 ); } } } m_iGrenadeCount--; } return; } if ( pEvent->event == AE_NPC_ATTACK_BROADCAST ) { if ( HasGrenade() ) return; } BaseClass::HandleAnimEvent( pEvent ); }