void CNPC_SO_BaseZombie::ForceMove( const Vector &targetPos, const Vector &traceDir, bool bRun ) { // Make sure our zombie gets the message (sometimes it is stubborn) // This bit should force our zombie to listen to us SetEnemy( NULL ); SetSchedule( SCHED_ZOMBIE_AMBUSH_MODE ); Vector chasePosition = targetPos; Vector vUpBit = chasePosition; vUpBit.z += 1; trace_t tr; AI_TraceHull( chasePosition, vUpBit, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); m_vecLastPosition = chasePosition; if ( m_hCine != NULL ) ExitScriptedSequence(); SetCondition( COND_RECEIVED_ORDERS ); if ( bRun ) SetSchedule( SCHED_FORCED_GO_RUN ); else SetSchedule( SCHED_FORCED_GO ); m_flMoveWaitFinished = gpGlobals->curtime; }
//----------------------------------------------------------------------------- // Purpose: // Input : *pTargetEnt - // vecDir - // flDistance - // flInterval - //----------------------------------------------------------------------------- void CNPC_Ichthyosaur::MoveFlyExecute( CBaseEntity *pTargetEnt, const Vector &vecDir, float flDistance, float flInterval ) { IchthyosaurMoveType_t eMoveType = ( GetNavigator()->CurWaypointIsGoal() ) ? ICH_MOVETYPE_ARRIVE : ICH_MOVETYPE_SEEK; m_flGroundSpeed = GetGroundSpeed(); Vector moveGoal = GetNavigator()->GetCurWaypointPos(); //See if we can move directly to our goal if ( ( GetEnemy() != NULL ) && ( GetNavigator()->GetGoalTarget() == (CBaseEntity *) GetEnemy() ) ) { trace_t tr; Vector goalPos = GetEnemy()->GetAbsOrigin() + ( GetEnemy()->GetSmoothedVelocity() * 0.5f ); AI_TraceHull( GetAbsOrigin(), goalPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, GetEnemy(), COLLISION_GROUP_NONE, &tr ); if ( tr.fraction == 1.0f ) { moveGoal = tr.endpos; } } //Move DoMovement( flInterval, moveGoal, eMoveType ); //Save the info from that run m_vecLastMoveTarget = moveGoal; m_bHasMoveTarget = true; }
int CASW_Parasite::RangeAttack1Conditions( float flDot, float flDist ) { if ( gpGlobals->curtime < m_flNextAttack ) return 0; if ( ( GetFlags() & FL_ONGROUND ) == false ) return 0; // This code stops lots of headcrabs swarming you and blocking you // whilst jumping up and down in your face over and over. It forces // them to back up a bit. If this causes problems, consider using it // for the fast headcrabs only, rather than just removing it.(sjb) if ( flDist < ASW_PARASITE_MIN_JUMP_DIST ) return COND_TOO_CLOSE_TO_ATTACK; if ( flDist > ASW_PARASITE_MAX_JUMP_DIST ) return COND_TOO_FAR_TO_ATTACK; // Make sure the way is clear! CBaseEntity *pEnemy = GetEnemy(); if( pEnemy ) { bool bEnemyIsBullseye = ( dynamic_cast<CNPC_Bullseye *>(pEnemy) != NULL ); trace_t tr; AI_TraceLine( EyePosition(), pEnemy->EyePosition(), MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); if ( tr.m_pEnt != GetEnemy() ) { if ( !bEnemyIsBullseye || tr.m_pEnt != NULL ) return COND_NONE; } if( GetEnemy()->EyePosition().z - 36.0f > GetAbsOrigin().z ) { // Only run this test if trying to jump at a player who is higher up than me, else this // code will always prevent a headcrab from jumping down at an enemy, and sometimes prevent it // jumping just slightly up at an enemy. Vector vStartHullTrace = GetAbsOrigin(); vStartHullTrace.z += 1.0; Vector vEndHullTrace = GetEnemy()->EyePosition() - GetAbsOrigin(); vEndHullTrace.NormalizeInPlace(); vEndHullTrace *= 8.0; vEndHullTrace += GetAbsOrigin(); AI_TraceHull( vStartHullTrace, vEndHullTrace,GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, GetCollisionGroup(), &tr ); if ( tr.m_pEnt != NULL && tr.m_pEnt != GetEnemy() ) { return COND_TOO_CLOSE_TO_ATTACK; } } } return COND_CAN_RANGE_ATTACK1; }
//----------------------------------------------------------------------------- // Purpose: // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CNPC_Ichthyosaur::Beached( void ) { trace_t tr; Vector testPos; testPos = GetAbsOrigin() - Vector( 0, 0, ICH_DEPTH_PREFERENCE ); AI_TraceHull( GetAbsOrigin(), testPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); return ( tr.fraction < 1.0f ); }
// Purpose: Monitor the antlion's jump to play the proper landing sequence bool CASW_Alien_Jumper::CheckLanding( void ) { trace_t tr; Vector testPos; //Amount of time to predict forward const float timeStep = 0.1f; //Roughly looks one second into the future testPos = GetAbsOrigin() + ( GetAbsVelocity() * timeStep ); testPos[2] -= ( 0.5 * sv_gravity.GetFloat() * GetGravity() * timeStep * timeStep); if ( asw_debug_aliens.GetInt() == 2 ) { NDebugOverlay::Line( GetAbsOrigin(), testPos, 255, 0, 0, 0, 0.5f ); NDebugOverlay::Cross3D( m_vecSavedJump, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, true, 0.5f ); } // Look below AI_TraceHull( GetAbsOrigin(), testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); //See if we're about to contact, or have already contacted the ground if ( ( tr.fraction != 1.0f ) || ( GetFlags() & FL_ONGROUND ) ) { int sequence = SelectWeightedSequence( (Activity)ACT_ASW_ALIEN_LAND ); if ( GetSequence() != sequence ) { VacateStrategySlot(); SetIdealActivity( (Activity) ACT_ASW_ALIEN_LAND ); CreateDust( false ); EmitSound( "ASW_Drone.Land" ); // asw todo: make the alien attack here? //if ( GetEnemy() && GetEnemy()->IsPlayer() ) //{ //CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() ); //if ( pPlayer && pPlayer->IsInAVehicle() == false ) //MeleeAttack( ANTLION_MELEE1_RANGE, sk_antlion_swipe_damage.GetFloat(), QAngle( 4.0f, 0.0f, 0.0f ), Vector( -250.0f, 1.0f, 1.0f ) ); //} SetAbsVelocity( GetAbsVelocity() * 0.33f ); return false; } return IsActivityFinished(); } return false; }
//----------------------------------------------------------------------------- // Purpose: Monitor the alien's jump to play the proper landing sequence //----------------------------------------------------------------------------- bool CAI_ASW_JumpBehavior::CheckLanding( void ) { trace_t tr; Vector testPos; //Amount of time to predict forward const float timeStep = 0.1f; //Roughly looks one second into the future testPos = GetAbsOrigin() + ( GetOuter()->GetAbsVelocity() * timeStep ); testPos[2] -= ( 0.5 * sv_gravity.GetFloat() * GetGravity() * timeStep * timeStep); if ( asw_debug_jump_behavior.GetInt() == 2 ) { NDebugOverlay::Line( GetAbsOrigin(), testPos, 255, 0, 0, 0, 0.5f ); NDebugOverlay::Cross3D( m_vecSavedJump, -Vector(2,2,2), Vector(2,2,2), 0, 255, 0, true, 0.5f ); } // Look below AI_TraceHull( GetAbsOrigin(), testPos, NAI_Hull::Mins( GetHullType() ), NAI_Hull::Maxs( GetHullType() ), GetOuter()->GetAITraceMask(), GetOuter(), COLLISION_GROUP_NONE, &tr ); //See if we're about to contact, or have already contacted the ground if ( ( tr.fraction != 1.0f ) || ( GetOuter()->GetFlags() & FL_ONGROUND ) ) { int sequence = GetOuter()->SelectWeightedSequence( (Activity)ACT_ALIEN_LAND ); if ( GetSequence() != sequence ) { CASW_Alien *pNPC = static_cast< CASW_Alien * >( GetOuter() ); pNPC->VacateStrategySlot(); pNPC->SetIdealActivity( (Activity) ACT_ALIEN_LAND ); //pNPC->Land(); if ( GetEnemy() && GetEnemy()->IsPlayer() ) { CBasePlayer *pPlayer = ToBasePlayer( GetEnemy() ); if ( pPlayer && pPlayer->IsInAVehicle() == false ) { //pNPC->MeleeAttack( m_flDamageDistance, m_flJumpDamage, QAngle( 4.0f, 0.0f, 0.0f ), Vector( -250.0f, 1.0f, 1.0f ) ); } } GetOuter()->SetAbsVelocity( GetOuter()->GetAbsVelocity() * 0.33f ); return false; } return GetOuter()->IsActivityFinished(); } return false; }
bool CNPC_Hydra::IsValidConnection( int iNode0, int iNode1 ) { trace_t tr; // check to make sure new connection is valid AI_TraceHull(m_body[iNode0].vecPos, m_body[iNode1].vecPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); if (tr.fraction == 1.0) { return true; } return false; }
//----------------------------------------------------------------------------- // Purpose: Plays the engine sound. //----------------------------------------------------------------------------- void CNPC_Houndeye::NPCThink(void) { if (m_pEnergyWave) { if (gpGlobals->curtime > m_flEndEnergyWaveTime) { UTIL_Remove(m_pEnergyWave); m_pEnergyWave = NULL; } } // ----------------------------------------------------- // Update collision group // While I'm running I'm allowed to penetrate // other houndeyes // ----------------------------------------------------- Vector vVelocity; GetVelocity( &vVelocity, NULL ); if (vVelocity.Length() > 10) { SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE ); } else { // Don't go solid if resting in another houndeye trace_t tr; AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); if (!tr.startsolid) { SetCollisionGroup( COLLISION_GROUP_NONE ); } else { SetCollisionGroup( HL2COLLISION_GROUP_HOUNDEYE ); } } /* if (GetCollisionGroup() == HL2COLLISION_GROUP_HOUNDEYE) { NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 255, 0, 0, 0); } else { NDebugOverlay::Box(GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 255, 0, 0, 0, 0); } */ BaseClass::NPCThink(); }
// Purpose: Returns true if a reasonable jumping distance bool CASW_Alien_Jumper::IsJumpLegal( const Vector &startPos, const Vector &apex, const Vector &endPos ) const { const float MAX_JUMP_RISE = 512; //const float MIN_JUMP_RISE = 16; const float MAX_JUMP_DROP = 512; const float MAX_JUMP_DISTANCE = 1024; const float MIN_JUMP_DISTANCE = 128; // make sure we don't do really flat jumps //float fHeight = (apex.z - startPos.z); //Msg("checking legality of jump with height %f\n", fHeight); //if ((apex.z - startPos.z) < 10 || fHeight != 0) //return false; //Adrian: Don't try to jump if my destination is right next to me. if ( ( endPos - GetAbsOrigin()).Length() < MIN_JUMP_DISTANCE ) return false; if ( HasSpawnFlags( SF_ANTLION_USE_GROUNDCHECKS ) && asw_test_new_alien_jump.GetBool() == true ) { trace_t tr; AI_TraceHull( endPos, endPos, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); if ( tr.m_pEnt ) { CAI_BaseNPC *pBlocker = tr.m_pEnt->MyNPCPointer(); if ( pBlocker && pBlocker->Classify() == CLASS_ASW_DRONE ) { // HACKHACK - push other jumpers out of the way CASW_Alien_Jumper *pJumper = dynamic_cast< CASW_Alien_Jumper * > ( pBlocker ); if ( pJumper ) { if ( pJumper->AllowedToBePushed() == true ) { // NDebugOverlay::Line( GetAbsOrigin(), endPos, 255, 0, 0, 0, 2 ); // NDebugOverlay::Box( pAntlion->GetAbsOrigin(), GetHullMins(), GetHullMaxs(), 0, 0, 255, 0, 2 ); pJumper->GetMotor()->SetIdealYawToTarget( endPos ); pJumper->SetSchedule( SCHED_MOVE_AWAY ); pJumper->m_flNextJumpPushTime = gpGlobals->curtime + 2.0f; } } } } } return BaseClass::IsJumpLegal( startPos, apex, endPos, MAX_JUMP_RISE, MAX_JUMP_DROP, MAX_JUMP_DISTANCE ); }
//----------------------------------------------------------------------------- // Purpose: Looks ahead to see if we are going to hit something. If we are, a // recommended avoidance path is returned. // Input : vecMoveDir - // flSpeed - // vecDeflect - // Output : Returns true if we hit something and need to deflect our course, // false if all is well. //----------------------------------------------------------------------------- bool CNPC_Crow::Probe( const Vector &vecMoveDir, float flSpeed, Vector &vecDeflect ) { // // Look 1/2 second ahead. // trace_t tr; AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + vecMoveDir * flSpeed, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, HL2COLLISION_GROUP_CROW, &tr ); if ( tr.fraction < 1.0f ) { // // If we hit something, deflect flight path parallel to surface hit. // Vector vecUp; CrossProduct( vecMoveDir, tr.plane.normal, vecUp ); CrossProduct( tr.plane.normal, vecUp, vecDeflect ); VectorNormalize( vecDeflect ); return true; } vecDeflect = vec3_origin; return false; }
//----------------------------------------------------------------------------- // Purpose: // Input : // Output : //----------------------------------------------------------------------------- int CNPC_Houndeye::RangeAttack1Conditions ( float flDot, float flDist ) { // I'm not allowed to attack if standing in another hound eye // (note houndeyes allowed to interpenetrate) trace_t tr; AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,0.1), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); if (tr.startsolid) { CBaseEntity *pEntity = tr.m_pEnt; if (pEntity->Classify() == CLASS_HOUNDEYE) { return( COND_NONE ); } } // If I'm really close to my enemy allow me to attack if // I'm facing regardless of next attack time if (flDist < 100 && flDot >= 0.3) { return COND_CAN_RANGE_ATTACK1; } if ( gpGlobals->curtime < m_flNextAttack ) { return( COND_NONE ); } if (flDist > ( HOUNDEYE_MAX_ATTACK_RADIUS * 0.5 )) { return COND_TOO_FAR_TO_ATTACK; } if (flDot < 0.3) { return COND_NOT_FACING_ATTACK; } return COND_CAN_RANGE_ATTACK1; }
//----------------------------------------------------------------------------- // Purpose: Static debug function to force all selected npcs to go to the // given node // Input : // Output : //----------------------------------------------------------------------------- void CAI_BaseNPC::ForceSelectedGo(CBaseEntity *pPlayer, const Vector &targetPos, const Vector &traceDir, bool bRun) { CAI_BaseNPC *npc = gEntList.NextEntByClass( (CAI_BaseNPC *)NULL ); for ( ; npc; npc = gEntList.NextEntByClass(npc) ) { if ( ( npc->m_debugOverlays & OVERLAY_NPC_SELECTED_BIT ) == 0 ) continue; // If a behavior is active, we need to stop running it npc->SetPrimaryBehavior( NULL ); Vector chasePosition = targetPos; npc->TranslateNavGoal( pPlayer, chasePosition ); // It it legal to drop me here Vector vUpBit = chasePosition; vUpBit.z += 1; trace_t tr; AI_TraceHull( chasePosition, vUpBit, npc->GetHullMins(), npc->GetHullMaxs(), npc->GetAITraceMask(), npc, COLLISION_GROUP_NONE, &tr ); if (tr.startsolid || tr.fraction != 1.0 ) { NDebugOverlay::BoxAngles(chasePosition, npc->GetHullMins(), npc->GetHullMaxs(), npc->GetAbsAngles(), 255,0,0,20,0.5); } npc->m_vecLastPosition = chasePosition; if (npc->m_hCine != NULL) { npc->ExitScriptedSequence(); } npc->SetSchedule( bRun ? SCHED_FORCED_GO_RUN : SCHED_FORCED_GO ); npc->m_flMoveWaitFinished = gpGlobals->curtime; } }
//----------------------------------------------------------------------------- // Purpose: // Input : &Steer - // Output : Returns true on success, false on failure. //----------------------------------------------------------------------------- bool CNPC_Ichthyosaur::SteerAvoidObstacles(Vector &Steer, const Vector &Velocity, const Vector &Forward, const Vector &Right, const Vector &Up) { trace_t tr; bool collided = false; Vector dir = Velocity; float speed = VectorNormalize( dir ); //Look ahead one second and avoid whatever is in our way. AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + (dir*speed), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); Vector forward; GetVectors( &forward, NULL, NULL ); //If we're hitting our enemy, just continue on if ( ( GetEnemy() != NULL ) && ( tr.m_pEnt == GetEnemy() ) ) return false; if ( tr.fraction < 1.0f ) { CBaseEntity *pBlocker = tr.m_pEnt; if ( ( pBlocker != NULL ) && ( pBlocker->MyNPCPointer() != NULL ) ) { DevMsg( 2, "Avoiding an NPC\n" ); Vector HitOffset = tr.endpos - GetAbsOrigin(); Vector SteerUp = CrossProduct( HitOffset, Velocity ); Steer = CrossProduct( SteerUp, Velocity ); VectorNormalize( Steer ); /*Vector probeDir = tr.endpos - GetAbsOrigin(); Vector normalToProbeAndWallNormal = probeDir.Cross( tr.plane.normal ); Steer = normalToProbeAndWallNormal.Cross( probeDir ); VectorNormalize( Steer );*/ if ( tr.fraction > 0 ) { Steer = (Steer * Velocity.Length()) / tr.fraction; //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f ); } else { Steer = (Steer * 1000 * Velocity.Length()); //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f ); } } else { if ( ( pBlocker != NULL ) && ( pBlocker == GetEnemy() ) ) { DevMsg( "Avoided collision\n" ); return false; } DevMsg( 2, "Avoiding the world\n" ); Vector steeringVector = tr.plane.normal; if ( tr.fraction == 0.0f ) return false; Steer = steeringVector * ( Velocity.Length() / tr.fraction ); //NDebugOverlay::Line( GetLocalOrigin(), GetLocalOrigin()+Steer, 255, 0, 0, false, 0.1f ); } //return true; collided = true; } //Try to remain 8 feet above the ground. AI_TraceLine( GetAbsOrigin(), GetAbsOrigin() + Vector(0, 0, -ICH_HEIGHT_PREFERENCE), MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction < 1.0f ) { Steer += Vector( 0, 0, m_vecAccelerationMax.z / tr.fraction ); collided = true; } //Stay under the surface if ( m_bIgnoreSurface == false ) { float waterLevel = ( UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+ICH_DEPTH_PREFERENCE ) - GetAbsOrigin().z ) / ICH_DEPTH_PREFERENCE; if ( waterLevel < 1.0f ) { Steer += -Vector( 0, 0, m_vecAccelerationMax.z / waterLevel ); collided = true; } } return collided; }
Vector CNPC_Ichthyosaur::DoProbe( const Vector &probe ) { trace_t tr; float fraction = 1.0f; bool collided = false; Vector normal = Vector( 0, 0, -1 ); float waterLevel = UTIL_WaterLevel( GetAbsOrigin(), GetAbsOrigin().z, GetAbsOrigin().z+150 ); waterLevel -= GetAbsOrigin().z; waterLevel /= 150; if ( waterLevel < 1.0f ) { collided = true; fraction = waterLevel; } AI_TraceHull( GetAbsOrigin(), probe, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); if ( ( collided == false ) || ( tr.fraction < fraction ) ) { fraction = tr.fraction; normal = tr.plane.normal; } if ( ( fraction < 1.0f ) && ( GetEnemy() == NULL || tr.u.ent != GetEnemy()->pev ) ) { #if FEELER_COLLISION_VISUALIZE NDebugOverlay::Line( GetLocalOrigin(), probe, 255, 0, 0, false, 0.1f ); #endif Vector probeDir = probe - GetLocalOrigin(); Vector normalToProbeAndWallNormal = probeDir.Cross( normal ); Vector steeringVector = normalToProbeAndWallNormal.Cross( probeDir ); Vector velDir = GetAbsVelocity(); VectorNormalize( velDir ); float steeringForce = m_flGroundSpeed * ( 1.0f - fraction ) * normal.Dot( velDir ); if ( steeringForce < 0.0f ) { steeringForce = -steeringForce; } velDir = steeringVector; VectorNormalize( velDir ); steeringVector = steeringForce * velDir; return steeringVector; } #if FEELER_COLLISION_VISUALIZE NDebugOverlay::Line( GetLocalOrigin(), probe, 0, 255, 0, false, 0.1f ); #endif return Vector( 0.0f, 0.0f, 0.0f ); }
//----------------------------------------------------------------------------- // Purpose: See if I can make my leaping attack!! // // //----------------------------------------------------------------------------- int CFastZombie::RangeAttack1Conditions( float flDot, float flDist ) { #define FASTZOMBIE_MINLEAP 128 #define FASTZOMBIE_MAXLEAP 508 // see me if you have questions about this (sjb - telegraphing) if (GetEnemy() == NULL) { return( COND_NONE ); } if( !(GetFlags() & FL_ONGROUND) ) { return COND_NONE; } if( gpGlobals->curtime < m_flNextAttack ) { return( COND_NONE ); } if (flDot < 0.8) { //return COND_NOT_FACING_ATTACK; return COND_NONE; } // make sure the enemy isn't on a roof and I'm in the streets (Ravenholm) float flZDist; flZDist = GetEnemy()->GetLocalOrigin().z - GetLocalOrigin().z; if( flZDist > 128 ) { return COND_TOO_FAR_TO_ATTACK; } if( flDist > FASTZOMBIE_MAXLEAP ) { return COND_TOO_FAR_TO_ATTACK; } if( flDist < FASTZOMBIE_MINLEAP ) { //return COND_TOO_CLOSE_TO_ATTACK; return COND_NONE; } if ( !IsMoving() ) { // I Have to be running!!! return COND_NONE; } // Don't jump at the player unless he's facing me. // This allows the player to get away if he turns and sprints CBasePlayer *pPlayer = static_cast<CBasePlayer*>( GetEnemy() ); if( pPlayer ) { // If the enemy is a player, don't attack from behind! if( !pPlayer->FInViewCone( this ) ) { return COND_NONE; } } // Drumroll please! // The final check! Is the path from my position to halfway between me // and the player clear? trace_t tr; Vector vecDirToEnemy; vecDirToEnemy = GetEnemy()->WorldSpaceCenter() - WorldSpaceCenter(); Vector vecHullMin( -16, -16, -16 ); Vector vecHullMax( 16, 16, 16 ); // only check half the distance. (the first part of the jump) vecDirToEnemy = vecDirToEnemy * 0.5; AI_TraceHull( WorldSpaceCenter(), WorldSpaceCenter() + vecDirToEnemy, vecHullMin, vecHullMax, MASK_SHOT, this, COLLISION_GROUP_NONE, &tr ); if( tr.fraction != 1.0 ) { // There's some sort of obstacle pretty much right in front of me. return COND_NONE; } return COND_CAN_RANGE_ATTACK1; }
//========================================================= // SelectSchedule //========================================================= int CNPC_Houndeye::SelectSchedule( void ) { switch ( m_NPCState ) { case NPC_STATE_IDLE: case NPC_STATE_ALERT: { if ( HasCondition(COND_LIGHT_DAMAGE) || HasCondition(COND_HEAVY_DAMAGE) ) { return SCHED_TAKE_COVER_FROM_ORIGIN; } break; } case NPC_STATE_COMBAT: { // dead enemy if ( HasCondition( COND_ENEMY_DEAD ) ) { // call base class, all code to handle dead enemies is centralized there. return BaseClass::SelectSchedule(); } // If a group attack was requested attack even if attack conditions not met if ( HasCondition( COND_HOUND_GROUP_ATTACK )) { // Check that I'm not standing in another hound eye // before attacking trace_t tr; AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + Vector(0,0,1), GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); if (!tr.startsolid) { return SCHED_HOUND_GROUP_ATTACK; } // Otherwise attack as soon as I can else { m_flNextAttack = gpGlobals->curtime; SCHED_HOUND_ATTACK_STRAFE; } } // If a group retread was requested if ( HasCondition( COND_HOUND_GROUP_RETREAT )) { return SCHED_HOUND_GROUP_RETREAT; } if ( HasCondition( COND_LIGHT_DAMAGE ) | HasCondition( COND_HEAVY_DAMAGE ) ) { if ( random->RandomFloat( 0 , 1 ) <= 0.4 ) { trace_t tr; Vector forward; AngleVectors( GetAbsAngles(), &forward ); AI_TraceHull( GetAbsOrigin(), GetAbsOrigin() + forward * -128, GetHullMins(), GetHullMaxs(), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction == 1.0 ) { // it's clear behind, so the hound will jump return SCHED_HOUND_HOP_RETREAT; } } return SCHED_TAKE_COVER_FROM_ENEMY; } // If a group rally was requested if ( HasCondition( COND_HOUND_GROUP_RALLEY )) { return SCHED_HOUND_GROUP_RALLEY; } if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) { if (m_pSquad && random->RandomInt(0,4) == 0) { if (!IsAnyoneInSquadAttacking()) { EmitSound( "NPC_Houndeye.GroupAttack" ); m_flSoundWaitTime = gpGlobals->curtime + 1.0; m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupAttack, NULL, this ); return SCHED_HOUND_GROUP_ATTACK; } } //<<TEMP>>comment SetCollisionGroup( COLLISION_GROUP_NONE ); return SCHED_RANGE_ATTACK1; } else { if (m_pSquad && random->RandomInt(0,5) == 0) { if (!IsAnyoneInSquadAttacking()) { EmitSound( "NPC_Houndeye.GroupFollow" ); m_flSoundWaitTime = gpGlobals->curtime + 1.0; m_pSquad->BroadcastInteraction( g_interactionHoundeyeGroupRalley, NULL, this ); return SCHED_HOUND_ATTACK_STRAFE; } } return SCHED_HOUND_ATTACK_STRAFE; } break; } } return BaseClass::SelectSchedule(); }
void CNPC_Hydra::MoveBody( ) { int i; int iFirst = 2; int iLast = m_body.Count() - 1; // clear stuck flags for (i = 0; i <= iLast; i++) { m_body[i].bStuck = false; } // try to move all the nodes for (i = iFirst; i <= iLast; i++) { trace_t tr; // check direct movement AI_TraceHull(m_body[i].vecPos, m_body[i].vecPos + m_body[i].vecDelta, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); Vector direct = tr.endpos; Vector delta = Vector( 0, 0, 0 ); Vector slide = m_body[i].vecDelta; if (tr.fraction != 1.0) { // slow down and remove all motion in the direction of the plane direct += tr.plane.normal; Vector impactSpeed = (slide * tr.plane.normal) * tr.plane.normal; slide = (slide - impactSpeed) * 0.8; if (tr.m_pEnt) { if (i == iLast) { Stab( tr.m_pEnt, impactSpeed, tr ); } else { Nudge( tr.m_pEnt, direct, impactSpeed ); } } // slow down and remove all motion in the direction of the plane slide = (slide - (slide * tr.plane.normal) * tr.plane.normal) * 0.8; // try to move the remaining distance anyways AI_TraceHull(direct, direct + slide * (1 - tr.fraction), Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); // NDebugOverlay::Line( m_body[i].vecPos, tr.endpos, 255, 255, 0, true, 1); direct = tr.endpos; m_body[i].bStuck = true; } // make sure the new segment doesn't intersect the world AI_TraceHull(direct, m_body[i-1].vecPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); if (tr.fraction == 1.0) { if (i+1 < iLast) { AI_TraceHull(direct, m_body[i+1].vecPos, Vector( -2, -2, -2 ), Vector( 2, 2, 2 ), MASK_NPCSOLID, this, COLLISION_GROUP_NONE, &tr); } if (tr.fraction == 1.0) { m_body[i].vecPos = direct; delta = slide; } else { // FIXME: compute nudge force m_body[i].bStuck = true; //m_body[i+1].bStuck = true; } } else { // FIXME: compute nudge force m_body[i].bStuck = true; //m_body[i-1].bStuck = true; } // m_body[i-1].vecDelta += (m_body[i].vecDelta - delta) * 0.25; // m_body[i+1].vecDelta += (m_body[i].vecDelta - delta) * 0.25; m_body[i].vecDelta = delta; } }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CNPC_CombineDropship::PrescheduleThink( void ) { BaseClass::PrescheduleThink(); // keep track of think time deltas for burn calc below float dt = gpGlobals->curtime - m_flLastTime; m_flLastTime = gpGlobals->curtime; switch( m_iLandState ) { case LANDING_NO: { if ( IsActivityFinished() && (GetActivity() != ACT_DROPSHIP_FLY_IDLE_EXAGG && GetActivity() != ACT_DROPSHIP_FLY_IDLE_CARGO) ) { if ( m_hContainer ) { SetIdealActivity( (Activity)ACT_DROPSHIP_FLY_IDLE_CARGO ); } else { SetIdealActivity( (Activity)ACT_DROPSHIP_FLY_IDLE_EXAGG ); } } DoRotorWash(); } break; case LANDING_LEVEL_OUT: { // Approach the drop point Vector vecToTarget = (GetDesiredPosition() - GetAbsOrigin()); float flDistance = vecToTarget.Length(); // If we're slowing, make it look like we're slowing /* if ( IsActivityFinished() && GetActivity() != ACT_DROPSHIP_DESCEND_IDLE ) { SetActivity( (Activity)ACT_DROPSHIP_DESCEND_IDLE ); } */ // Are we there yet? float flSpeed = GetAbsVelocity().Length(); if ( flDistance < 70 && flSpeed < 100 ) { m_flLandingSpeed = flSpeed; m_iLandState = LANDING_DESCEND; // save off current angles so we can work them out over time QAngle angles = GetLocalAngles(); m_existPitch = angles.x; m_existRoll = angles.z; } DoRotorWash(); } break; case LANDING_DESCEND: { float flAltitude; SetLocalAngularVelocity( vec3_angle ); // Ensure we land on the drop point Vector vecToTarget = (GetDesiredPosition() - GetAbsOrigin()); float flDistance = vecToTarget.Length(); float flRampedSpeed = m_flLandingSpeed * (flDistance / 70); Vector vecVelocity = (flRampedSpeed / flDistance) * vecToTarget; vecVelocity.z = -75; SetAbsVelocity( vecVelocity ); flAltitude = GetAltitude(); if ( IsActivityFinished() && GetActivity() != ACT_DROPSHIP_DESCEND_IDLE ) { SetActivity( (Activity)ACT_DROPSHIP_DESCEND_IDLE ); } if ( flAltitude < 72 ) { QAngle angles = GetLocalAngles(); // Level out quickly. angles.x = UTIL_Approach( 0.0, angles.x, 0.2 ); angles.z = UTIL_Approach( 0.0, angles.z, 0.2 ); SetLocalAngles( angles ); } else { // randomly move as if buffeted by ground effects // gently flatten ship from starting pitch/yaw m_existPitch = UTIL_Approach( 0.0, m_existPitch, 1 ); m_existRoll = UTIL_Approach( 0.0, m_existRoll, 1 ); QAngle angles = GetLocalAngles(); angles.x = m_existPitch + ( sin( gpGlobals->curtime * 3.5f ) * DROPSHIP_MAX_LAND_TILT ); angles.z = m_existRoll + ( sin( gpGlobals->curtime * 3.75f ) * DROPSHIP_MAX_LAND_TILT ); SetLocalAngles( angles ); // figure out where to face (nav point) Vector targetDir = GetDesiredPosition() - GetAbsOrigin(); // NDebugOverlay::Cross3D( m_pGoalEnt->GetAbsOrigin(), -Vector(2,2,2), Vector(2,2,2), 255, 0, 0, false, 20 ); QAngle targetAngles = GetAbsAngles(); targetAngles.y += UTIL_AngleDiff(UTIL_VecToYaw( targetDir ), targetAngles.y); // orient ship towards path corner on the way down angles = GetAbsAngles(); angles.y = UTIL_Approach(targetAngles.y, angles.y, 2 ); SetAbsAngles( angles ); } if ( flAltitude <= 0.5f ) { m_iLandState = LANDING_TOUCHDOWN; // upon landing, make sure ship is flat QAngle angles = GetLocalAngles(); angles.x = 0; angles.z = 0; SetLocalAngles( angles ); // TODO: Release cargo anim SetActivity( (Activity)ACT_DROPSHIP_DESCEND_IDLE ); } DoRotorWash(); // place danger sounds 1 foot above ground to get troops to scatter if they are below dropship Vector vecBottom = GetAbsOrigin(); vecBottom.z += WorldAlignMins().z; Vector vecSpot = vecBottom + Vector(0, 0, -1) * (GetAltitude() - 12 ); CSoundEnt::InsertSound( SOUND_DANGER, vecSpot, 400, 0.2, this, 0 ); CSoundEnt::InsertSound( SOUND_PHYSICS_DANGER, vecSpot, 400, 0.2, this, 1 ); // NDebugOverlay::Cross3D( vecSpot, -Vector(4,4,4), Vector(4,4,4), 255, 0, 255, false, 10.0f ); // now check to see if player is below us, if so, cause heat damage to them (i.e. get them to move) trace_t tr; Vector vecBBoxMin = CRATE_BBOX_MIN; // use flat box for check vecBBoxMin.z = -5; Vector vecBBoxMax = CRATE_BBOX_MAX; vecBBoxMax.z = 5; Vector pEndPoint = vecBottom + Vector(0, 0, -1) * ( GetAltitude() - 12 ); AI_TraceHull( vecBottom, pEndPoint, vecBBoxMin, vecBBoxMax, MASK_SOLID, this, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction < 1.0f ) { if ( tr.GetEntityIndex() == 1 ) // player??? { CTakeDamageInfo info( this, this, 20 * dt, DMG_BURN ); CBasePlayer *pPlayer = UTIL_PlayerByIndex(1); pPlayer->TakeDamage( info ); } } } break; case LANDING_TOUCHDOWN: { if ( IsActivityFinished() && ( GetActivity() != ACT_DROPSHIP_DESCEND_IDLE ) ) { SetActivity( (Activity)ACT_DROPSHIP_DESCEND_IDLE ); } m_iLandState = LANDING_UNLOADING; m_flTroopDeployPause = gpGlobals->curtime + DROPSHIP_PAUSE_B4_TROOP_UNLOAD; m_flTimeTakeOff = m_flTroopDeployPause + DROPSHIP_DEPLOY_TIME; } break; case LANDING_UNLOADING: { // pause before dropping troops if ( gpGlobals->curtime > m_flTroopDeployPause ) { if ( m_hContainer ) // don't drop troops if we don't have a crate any more { SpawnTroops(); m_flTroopDeployPause = m_flTimeTakeOff + 2; // only drop once } } // manage engine wash and volume if ( m_flTimeTakeOff - gpGlobals->curtime < 0.5f ) { m_engineThrust = UTIL_Approach( 1.0f, m_engineThrust, 0.1f ); DoRotorWash(); } else { float idleVolume = 0.2f; m_engineThrust = UTIL_Approach( idleVolume, m_engineThrust, 0.04f ); if ( m_engineThrust > idleVolume ) { DoRotorWash(); // make sure we're kicking up dust/water as long as engine thrust is up } } if( gpGlobals->curtime > m_flTimeTakeOff ) { m_iLandState = LANDING_LIFTOFF; SetActivity( (Activity)ACT_DROPSHIP_LIFTOFF ); m_engineThrust = 1.0f; // ensure max volume once we're airborne if ( m_bIsFiring ) { StopCannon(); // kill cannon sounds if they are on } // detach container from ship if ( m_hContainer && m_leaveCrate ) { m_hContainer->SetParent(NULL); m_hContainer->SetMoveType( (MoveType_t)m_iContainerMoveType ); // If the container has a physics object, remove it's shadow IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject(); if ( pPhysicsObject ) { pPhysicsObject->RemoveShadowController(); } m_hContainer = NULL; } } } break; case LANDING_LIFTOFF: { // give us some clearance before changing back to larger hull -- keeps ship from getting stuck on // things like the player, etc since we "pop" the hull... if ( GetAltitude() > 120 ) { m_OnFinishedDropoff.FireOutput( this, this ); m_iLandState = LANDING_NO; // change bounding box back to normal ship hull Vector vecBBMin, vecBBMax; ExtractBbox( SelectHeaviestSequence( ACT_DROPSHIP_DEPLOY_IDLE ), vecBBMin, vecBBMax ); UTIL_SetSize( this, vecBBMin, vecBBMax ); Relink(); } } break; case LANDING_SWOOPING: { // Did we lose our pickup target? if ( !m_hPickupTarget ) { m_iLandState = LANDING_NO; } else { // Decrease altitude and speed to hit the target point. Vector vecToTarget = (GetDesiredPosition() - GetAbsOrigin()); float flDistance = vecToTarget.Length(); // Start cheating when we get near it if ( flDistance < 50 ) { /* if ( flDistance > 10 ) { // Cheat and ensure we touch the target float flSpeed = GetAbsVelocity().Length(); Vector vecVelocity = vecToTarget; VectorNormalize( vecVelocity ); SetAbsVelocity( vecVelocity * min(flSpeed,flDistance) ); } else */ { // Grab the target m_hContainer = m_hPickupTarget; m_hPickupTarget = NULL; m_iContainerMoveType = m_hContainer->GetMoveType(); // If the container has a physics object, move it to shadow IPhysicsObject *pPhysicsObject = m_hContainer->VPhysicsGetObject(); if ( pPhysicsObject ) { pPhysicsObject->SetShadow( Vector(1e4,1e4,1e4), AngularImpulse(1e4,1e4,1e4), false, false ); pPhysicsObject->UpdateShadow( GetAbsOrigin(), GetAbsAngles(), false, 0 ); } int iIndex = 0;//LookupAttachment("Cargo"); /* Vector vecOrigin; QAngle vecAngles; GetAttachment( iIndex, vecOrigin, vecAngles ); m_hContainer->SetAbsOrigin( vecOrigin ); m_hContainer->SetAbsAngles( vec3_angle ); */ m_hContainer->SetAbsOrigin( GetAbsOrigin() ); m_hContainer->SetParent(this, iIndex); m_hContainer->SetMoveType( MOVETYPE_PUSH ); m_hContainer->RemoveFlag( FL_ONGROUND ); m_hContainer->Relink(); m_hContainer->SetAbsAngles( vec3_angle ); m_OnFinishedPickup.FireOutput( this, this ); m_iLandState = LANDING_NO; } } } DoRotorWash(); } break; } DoCombatStuff(); if ( GetActivity() != GetIdealActivity() ) { //Msg( "setactivity" ); SetActivity( GetIdealActivity() ); } }
void CASW_Weapon_Shotgun::PrimaryAttack( void ) { // If my clip is empty (and I use clips) start reload if ( UsesClipsForAmmo1() && !m_iClip1 ) { Reload(); return; } CASW_Player *pPlayer = GetCommander(); CASW_Marine *pMarine = GetMarine(); if (pMarine) // firing from a marine { // MUST call sound before removing a round from the clip of a CMachineGun WeaponSound(SINGLE); if (m_iClip1 <= AmmoClickPoint()) { LowAmmoSound(); } m_bIsFiring = true; // tell the marine to tell its weapon to draw the muzzle flash pMarine->DoMuzzleFlash(); // sets the animation on the weapon model iteself SendWeaponAnim( GetPrimaryAttackActivity() ); #ifdef GAME_DLL // check for turning on lag compensation if (pPlayer && pMarine->IsInhabited()) { CASW_Lag_Compensation::RequestLagCompensation( pPlayer, pPlayer->GetCurrentUserCommand() ); } #endif Vector vecSrc = pMarine->Weapon_ShootPosition( ); // hull trace out to this shoot position, so we can be sure we're not firing from over an alien's head trace_t tr; CTraceFilterSimple tracefilter(pMarine, COLLISION_GROUP_NONE); Vector vecMarineMiddle(pMarine->GetAbsOrigin()); vecMarineMiddle.z = vecSrc.z; AI_TraceHull( vecMarineMiddle, vecSrc, Vector( -10, -10, -20 ), Vector( 10, 10, 10 ), MASK_SHOT, &tracefilter, &tr ); vecSrc = tr.endpos; Vector vecAiming = vec3_origin; if ( pPlayer && pMarine->IsInhabited() ) { vecAiming = pPlayer->GetAutoaimVectorForMarine(pMarine, GetAutoAimAmount(), GetVerticalAdjustOnlyAutoAimAmount()); // 45 degrees = 0.707106781187 } else { #ifndef CLIENT_DLL vecAiming = pMarine->GetActualShootTrajectory( vecSrc ); #endif } if (true) // hitscan pellets { #ifndef CLIENT_DLL if (asw_DebugAutoAim.GetBool()) { NDebugOverlay::Line(vecSrc, vecSrc + vecAiming * asw_weapon_max_shooting_distance.GetFloat(), 64, 0, 64, false, 120.0); } #endif int iPellets = GetNumPellets(); for (int i=0;i<iPellets;i++) { FireBulletsInfo_t info( 1, vecSrc, vecAiming, GetAngularBulletSpread(), asw_weapon_max_shooting_distance.GetFloat(), m_iPrimaryAmmoType ); info.m_pAttacker = pMarine; info.m_iTracerFreq = 1; info.m_nFlags = FIRE_BULLETS_NO_PIERCING_SPARK | FIRE_BULLETS_HULL | FIRE_BULLETS_ANGULAR_SPREAD; info.m_flDamage = GetWeaponDamage(); info.m_flDamageForceScale = asw_weapon_force_scale.GetFloat(); #ifndef CLIENT_DLL if (asw_debug_marine_damage.GetBool()) Msg("Weapon dmg = %f\n", info.m_flDamage); info.m_flDamage *= pMarine->GetMarineResource()->OnFired_GetDamageScale(); #endif // shotgun bullets have a base 50% chance of piercing //float fPiercingChance = 0.5f; //if (pMarine->GetMarineResource() && pMarine->GetMarineProfile() && pMarine->GetMarineProfile()->GetMarineClass() == MARINE_CLASS_SPECIAL_WEAPONS) //fPiercingChance += MarineSkills()->GetSkillBasedValueByMarine(pMarine, ASW_MARINE_SKILL_PIERCING); //pMarine->FirePenetratingBullets(info, 5, fPiercingChance); //pMarine->FirePenetratingBullets(info, 5, 1.0f, i, false ); pMarine->FirePenetratingBullets(info, 0, 1.0f, i, false ); } } else // projectile pellets { #ifndef CLIENT_DLL CShotManipulator Manipulator( vecAiming ); int iPellets = GetNumPellets(); for (int i=0;i<iPellets;i++) { // create a pellet at some random spread direction //CASW_Shotgun_Pellet *pPellet = Vector newVel = Manipulator.ApplySpread(GetBulletSpread()); //Vector newVel = ApplySpread( vecAiming, GetBulletSpread() ); if ( pMarine->GetWaterLevel() == 3 ) newVel *= PELLET_WATER_VELOCITY; else newVel *= PELLET_AIR_VELOCITY; newVel *= (1.0 + (0.1 * random->RandomFloat(-1,1))); CreatePellet(vecSrc, newVel, pMarine); //CASW_Shotgun_Pellet_Predicted::CreatePellet(vecSrc, newVel, pPlayer, pMarine); } #endif } // increment shooting stats #ifndef CLIENT_DLL if (pMarine && pMarine->GetMarineResource()) { pMarine->GetMarineResource()->UsedWeapon(this, 1); pMarine->OnWeaponFired( this, GetNumPellets() ); } #endif // decrement ammo m_iClip1 -= 1; #ifdef GAME_DLL CASW_Marine *pMarine = GetMarine(); if (pMarine && m_iClip1 <= 0 && pMarine->GetAmmoCount(m_iPrimaryAmmoType) <= 0 ) { // check he doesn't have ammo in an ammo bay CASW_Weapon_Ammo_Bag* pAmmoBag = dynamic_cast<CASW_Weapon_Ammo_Bag*>(pMarine->GetASWWeapon(0)); if (!pAmmoBag) pAmmoBag = dynamic_cast<CASW_Weapon_Ammo_Bag*>(pMarine->GetASWWeapon(1)); if (!pAmmoBag || !pAmmoBag->CanGiveAmmoToWeapon(this)) pMarine->OnWeaponOutOfAmmo(true); } #endif } if (m_iClip1 > 0) // only force the fire wait time if we have ammo for another shot m_flNextPrimaryAttack = gpGlobals->curtime + GetFireRate(); else m_flNextPrimaryAttack = gpGlobals->curtime; m_fSlowTime = gpGlobals->curtime + 0.1f; }
//----------------------------------------------------------------------------- // Purpose: Return true if pTestHint passes the criteria specified in hintCriteria //----------------------------------------------------------------------------- bool CAI_Hint::HintMatchesCriteria( CAI_BaseNPC *pNPC, const CHintCriteria &hintCriteria, const Vector &position, float *flNearestDistance, bool bIgnoreLock, bool bIgnoreHintType ) { // Cannot be locked if ( !bIgnoreLock && IsLocked() ) { REPORTFAILURE( "Node is locked." ); return false; } if ( !bIgnoreHintType && !hintCriteria.MatchesHintType( HintType() ) ) { return false; } if ( GetMinState() > NPC_STATE_IDLE || GetMaxState() < NPC_STATE_COMBAT ) { if ( pNPC && ( pNPC->GetState() < GetMinState() || pNPC->GetState() > GetMaxState() ) ) { REPORTFAILURE( "NPC not in correct state." ); return false; } } // See if we're filtering by group name if ( hintCriteria.GetGroup() != NULL_STRING ) { AssertIsValidString( GetGroup() ); AssertIsValidString( hintCriteria.GetGroup() ); if ( GetGroup() == NULL_STRING || GetGroup() != hintCriteria.GetGroup() ) { Assert(GetGroup() == NULL_STRING || strcmp( STRING(GetGroup()), STRING(hintCriteria.GetGroup())) != 0 ); REPORTFAILURE( "Doesn't match NPC hint group." ); return false; } } // If we're watching for include zones, test it if ( ( hintCriteria.HasIncludeZones() ) && ( hintCriteria.InIncludedZone( GetAbsOrigin() ) == false ) ) { REPORTFAILURE( "Not inside include zones." ); return false; } // If we're watching for exclude zones, test it if ( ( hintCriteria.HasExcludeZones() ) && ( hintCriteria.InExcludedZone( GetAbsOrigin() ) ) ) { REPORTFAILURE( "Inside exclude zones." ); return false; } // See if the class handles this hint type if ( ( pNPC != NULL ) && ( pNPC->FValidateHintType( this ) == false ) ) { REPORTFAILURE( "NPC doesn't know how to handle that type." ); return false; } if ( hintCriteria.HasFlag(bits_HINT_NPC_IN_NODE_FOV) ) { if ( pNPC == NULL ) { AssertMsg(0,"Hint node attempted to verify NPC in node FOV without NPC!\n"); } else { if( !IsInNodeFOV(pNPC) ) { REPORTFAILURE( "NPC Not in hint's FOV" ); return false; } } } if ( hintCriteria.HasFlag( bits_HINT_NODE_IN_AIMCONE ) ) { if ( pNPC == NULL ) { AssertMsg( 0, "Hint node attempted to find node in aimcone without specifying NPC!\n" ); } else { if( !pNPC->FInAimCone( GetAbsOrigin() ) ) { REPORTFAILURE( "Hint isn't in NPC's aimcone" ); return false; } } } if ( hintCriteria.HasFlag( bits_HINT_NODE_IN_VIEWCONE ) ) { if ( pNPC == NULL ) { AssertMsg( 0, "Hint node attempted to find node in viewcone without specifying NPC!\n" ); } else { if( !pNPC->FInViewCone( this ) ) { REPORTFAILURE( "Hint isn't in NPC's viewcone" ); return false; } } } { AI_PROFILE_SCOPE( HINT_FVisible ); // See if we're requesting a visible node if ( hintCriteria.HasFlag( bits_HINT_NODE_VISIBLE ) ) { if ( pNPC == NULL ) { //NOTENOTE: If you're hitting this, you've asked for a visible node without specifing an NPC! AssertMsg( 0, "Hint node attempted to find visible node without specifying NPC!\n" ); } else { if( m_NodeData.nNodeID == NO_NODE ) { // This is just an info_hint, not a node. if( !pNPC->FVisible( this ) ) { REPORTFAILURE( "Hint isn't visible to NPC." ); return false; } } else { // This hint associated with a node. trace_t tr; Vector vHintPos; GetPosition(pNPC,&vHintPos); AI_TraceLine ( pNPC->EyePosition(), vHintPos + pNPC->GetViewOffset(), MASK_NPCSOLID_BRUSHONLY, pNPC, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction != 1.0f ) { REPORTFAILURE( "Node isn't visible to NPC." ); return false; } } } } } // Check for clear if requested if ( hintCriteria.HasFlag( bits_HINT_NODE_CLEAR ) ) { if ( pNPC == NULL ) { //NOTENOTE: If you're hitting this, you've asked for a clear node without specifing an NPC! AssertMsg( 0, "Hint node attempted to find clear node without specifying NPC!\n" ); } else { trace_t tr; // Can my bounding box fit there? AI_TraceHull ( GetAbsOrigin(), GetAbsOrigin(), pNPC->WorldAlignMins(), pNPC->WorldAlignMaxs(), MASK_SOLID, pNPC, COLLISION_GROUP_NONE, &tr ); if ( tr.fraction != 1.0 ) { REPORTFAILURE( "Node isn't clear." ); return false; } } } // See if this is our next, closest node if ( hintCriteria.HasFlag( bits_HINT_NODE_NEAREST ) ) { Assert( flNearestDistance ); // Calculate our distance float distance = (GetAbsOrigin() - position).Length(); // Must be closer than the current best if ( distance > *flNearestDistance ) { REPORTFAILURE( "Not the nearest node." ); return false; } // Remember the distance *flNearestDistance = distance; } // Must either be visible or not if requested if ( hintCriteria.HasFlag( bits_HINT_NODE_NOT_VISIBLE_TO_PLAYER|bits_HINT_NODE_VISIBLE_TO_PLAYER ) ) { bool bWasSeen = false; // Test all potential seers for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pPlayer = UTIL_PlayerByIndex(i); if ( pPlayer ) { // Only spawn if the player's looking away from me Vector vLookDir = pPlayer->EyeDirection3D(); Vector vTargetDir = GetAbsOrigin() - pPlayer->EyePosition(); VectorNormalize(vTargetDir); float fDotPr = DotProduct(vLookDir,vTargetDir); if ( fDotPr > 0 ) { trace_t tr; UTIL_TraceLine( pPlayer->EyePosition(), GetAbsOrigin(), MASK_SOLID_BRUSHONLY, pPlayer, COLLISION_GROUP_NONE, &tr); if ( tr.fraction == 1.0 ) { if ( hintCriteria.HasFlag( bits_HINT_NODE_NOT_VISIBLE_TO_PLAYER ) ) { REPORTFAILURE( "Node is visible to player." ); return false; } bWasSeen = true; } } } } if ( !bWasSeen && hintCriteria.HasFlag( bits_HINT_NODE_VISIBLE_TO_PLAYER ) ) { REPORTFAILURE( "Node isn't visible to player." ); return false; } } return true; }