//----------------------------------------------------------------------------- // Purpose: Return true if this bug should flee //----------------------------------------------------------------------------- bool CNPC_Bug_Warrior::ShouldFlee( void ) { // I don't flee if I'm fighting to the death if ( m_bFightingToDeath ) return false; // I don't flee if I'm not alone if ( !IsAlone() ) return false; // I don't flee if I'm within sight of a bughole if ( m_hMyBugHole.Get() && FVisible( m_hMyBugHole ) ) return false; return true; }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CObjectSentrygun::ValidTargetPlayer( CTFPlayer *pPlayer, const Vector &vecStart, const Vector &vecEnd ) { // Keep shooting at spies that go invisible after we acquire them as a target. if ( pPlayer->m_Shared.GetPercentInvisible() > 0.5 ) return false; // Keep shooting at spies that disguise after we acquire them as at a target. if ( pPlayer->m_Shared.InCond( TF_COND_DISGUISED ) && pPlayer->m_Shared.GetDisguiseTeam() == GetTeamNumber() && pPlayer != m_hEnemy ) return false; // Not across water boundary. if ( ( GetWaterLevel() == 0 && pPlayer->GetWaterLevel() >= 3 ) || ( GetWaterLevel() == 3 && pPlayer->GetWaterLevel() <= 0 ) ) return false; // Ray trace!!! return FVisible( pPlayer, MASK_SHOT | CONTENTS_GRATE ); }
//----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- bool CObjectSentrygun::ValidTargetObject( CBaseObject *pObject, const Vector &vecStart, const Vector &vecEnd ) { // Ignore objects being placed, they are not real objects yet. if ( pObject->IsPlacing() ) return false; // Ignore sappers. if ( pObject->MustBeBuiltOnAttachmentPoint() ) return false; // Not across water boundary. if ( ( GetWaterLevel() == 0 && pObject->GetWaterLevel() >= 3 ) || ( GetWaterLevel() == 3 && pObject->GetWaterLevel() <= 0 ) ) return false; // Ray trace. return FVisible( pObject, MASK_SHOT | CONTENTS_GRATE ); }
//------------------------------------------------------------------------------ // Updates the enemy //------------------------------------------------------------------------------ void CBaseHelicopter::UpdateEnemy() { if( HasCondition( COND_ENEMY_DEAD ) ) { SetEnemy( NULL ); } // Look for my best enemy. If I change enemies, // be sure and change my prevseen/lastseen timers. if( m_lifeState == LIFE_ALIVE ) { GetSenses()->Look( (int)EnemySearchDistance() ); GetEnemies()->RefreshMemories(); ChooseEnemy(); if( HasEnemy() ) { CBaseEntity *pEnemy = GetEnemy(); GatherEnemyConditions( pEnemy ); if ( FVisible( pEnemy ) ) { if (m_flLastSeen < gpGlobals->curtime - 2) { m_flPrevSeen = gpGlobals->curtime; } m_flLastSeen = gpGlobals->curtime; m_vecTargetPosition = pEnemy->WorldSpaceCenter(); } } else { // look at where we're going instead m_vecTargetPosition = GetDesiredPosition(); } } else { // If we're dead or dying, forget our enemy and don't look for new ones(sjb) SetEnemy( NULL ); } }
//----------------------------------------------------------------------------- // Purpose: Try to greet player first time he's seen // Output : int //----------------------------------------------------------------------------- int CNPCSimpleTalker::FIdleHello( void ) { // Filter might be preventing us from ever greeting the player if ( !CanSayHello() ) return false; // get a player CBaseEntity *pPlayer = FindNearestFriend(true); if (pPlayer) { if (FInViewCone(pPlayer) && FVisible(pPlayer)) { SayHelloToPlayer( pPlayer ); return true; } } return false; }
//----------------------------------------------------------------------------- // Purpose: Say hello to the specified player //----------------------------------------------------------------------------- void CNPCSimpleTalker::SayHelloToPlayer( CBaseEntity *pPlayer ) { Assert( !GetExpresser()->SpokeConcept(TLK_HELLO) ); SetSpeechTarget( pPlayer ); Speak( TLK_HELLO ); DeferAllIdleSpeech( random->RandomFloat( 5, 10 ) ); CAI_BaseNPC **ppAIs = g_AI_Manager.AccessAIs(); CAI_PlayerAlly *pTalker; for ( int i = 0; i < g_AI_Manager.NumAIs(); i++ ) { pTalker = dynamic_cast<CAI_PlayerAlly *>(ppAIs[i]); if( pTalker && FVisible( pTalker ) ) { // Tell this guy he's already said hello to the player, too. pTalker->GetExpresser()->SetSpokeConcept( TLK_HELLO, NULL ); } } }
//========================================================= // AutoSearchThink - //========================================================= void CNPC_BaseTurret::AutoSearchThink(void) { // ensure rethink StudioFrameAdvance( ); SetNextThink( gpGlobals->curtime + 0.3 ); // If we have a target and we're still healthy CBaseEntity *pEnemy = GetEnemy(); if (pEnemy != NULL) { if (!pEnemy->IsAlive() ) { pEnemy = NULL; } } // Acquire Target if (pEnemy == NULL) { GetSenses()->Look( TURRET_RANGE ); pEnemy = BestEnemy(); if ( pEnemy && !FVisible( pEnemy ) ) pEnemy = NULL; } if (pEnemy != NULL) { SetThink(&CNPC_BaseTurret::Deploy); CPASAttenuationFilter filter( this ); EmitSound( filter, entindex(), "Turret.Alert" ); } SetEnemy( pEnemy ); }
//========================================================= // Look - Base class monster function to find enemies or // food by sight. iDistance is distance ( in units ) that the // monster can see. // // Sets the sight bits of the m_afConditions mask to indicate // which types of entities were sighted. // Function also sets the Looker's m_pLink // to the head of a link list that contains all visible ents. // (linked via each ent's m_pLink field) // //========================================================= void CBaseMonster :: Look ( int iDistance ) { int iSighted = 0; // DON'T let visibility information from last frame sit around! ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); m_pLink = NULL; CBaseEntity *pSightEnt = NULL;// the current visible entity that we're dealing with CBaseEntity *pList[100]; Vector delta = Vector( iDistance, iDistance, iDistance ); // Find only monsters/clients in box, NOT limited to PVS int count = UTIL_EntitiesInBox( pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT|FL_MONSTER ); for ( int i = 0; i < count; i++ ) { pSightEnt = pList[i]; if ( pSightEnt != this && pSightEnt->pev->health > 0 ) { // the looker will want to consider this entity // don't check anything else about an entity that can't be seen, or an entity that you don't care about. if ( IRelationship( pSightEnt ) != R_NO && FInViewCone( pSightEnt ) && !FBitSet( pSightEnt->pev->flags, FL_NOTARGET ) && FVisible( pSightEnt ) ) { if ( pSightEnt->IsPlayer() ) { // if we see a client, remember that (mostly for scripted AI) iSighted |= bits_COND_SEE_CLIENT; } pSightEnt->m_pLink = m_pLink; m_pLink = pSightEnt; if ( pSightEnt == m_hEnemy ) { // we know this ent is visible, so if it also happens to be our enemy, store that now. iSighted |= bits_COND_SEE_ENEMY; } // don't add the Enemy's relationship to the conditions. We only want to worry about conditions when // we see monsters other than the Enemy. switch ( IRelationship ( pSightEnt ) ) { case R_NM: iSighted |= bits_COND_SEE_NEMESIS; break; case R_HT: iSighted |= bits_COND_SEE_HATE; break; case R_DL: iSighted |= bits_COND_SEE_DISLIKE; break; case R_FR: iSighted |= bits_COND_SEE_FEAR; break; case R_AL: break; default: ALERT ( at_aiconsole, "%s can't assess %s\n", STRING(pev->classname), STRING(pSightEnt->pev->classname ) ); break; } } } } SetConditions( iSighted ); }
//========================================================= // SonicAttack //========================================================= void CNPC_Houndeye::SonicAttack ( void ) { EmitSound( "NPC_Houndeye.SonicAttack" ); float flAdjustedDamage; float flDist; CBroadcastRecipientFilter filter2; te->BeamRingPoint(filter2, 0.0, GetAbsOrigin(), //origin 16, //start radius HOUNDEYE_MAX_ATTACK_RADIUS,//end radius m_iSpriteTexture, //texture 0, //halo index 0, //start frame 0, //framerate 0.2, //life 24, //width 16, //spread 0, //amplitude 188, //r 220, //g 255, //b 192, //a 0 //speed ); CBroadcastRecipientFilter filter3; te->BeamRingPoint(filter3, 0.0, GetAbsOrigin(), //origin 16, //start radius HOUNDEYE_MAX_ATTACK_RADIUS / 2, //end radius m_iSpriteTexture, //texture 0, //halo index 0, //start frame 0, //framerate 0.2, //life 24, //width 16, //spread 0, //amplitude 188, //r 220, //g 255, //b 192, //a 0 //speed ); CBaseEntity *pEntity = NULL; // iterate on all entities in the vicinity. while ((pEntity = gEntList.FindEntityInSphere(pEntity, GetAbsOrigin(), HOUNDEYE_MAX_ATTACK_RADIUS)) != NULL) { if (pEntity->m_takedamage != DAMAGE_NO) { if (!FClassnameIs(pEntity, "npc_houndeye")) {// houndeyes don't hurt other houndeyes with their attack // houndeyes do FULL damage if the ent in question is visible. Half damage otherwise. // This means that you must get out of the houndeye's attack range entirely to avoid damage. // Calculate full damage first flAdjustedDamage = sk_Houndeye_dmg_blast.GetFloat(); flDist = (pEntity->WorldSpaceCenter() - GetAbsOrigin()).Length(); flAdjustedDamage -= (flDist / HOUNDEYE_MAX_ATTACK_RADIUS) * flAdjustedDamage; if (!FVisible(pEntity)) { if (pEntity->IsPlayer()) { // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients // so that monsters in other parts of the level don't take the damage and get pissed. flAdjustedDamage *= 0.5; } else if (!FClassnameIs(pEntity, "func_breakable") && !FClassnameIs(pEntity, "func_pushable")) { // do not hurt nonclients through walls, but allow damage to be done to breakables flAdjustedDamage = 0; } } //ALERT ( at_aiconsole, "Damage: %f\n", flAdjustedDamage ); if (flAdjustedDamage > 0) { CTakeDamageInfo info(this, this, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB); CalculateExplosiveDamageForce(&info, (pEntity->GetAbsOrigin() - GetAbsOrigin()), pEntity->GetAbsOrigin()); pEntity->TakeDamage(info); if ((pEntity->GetAbsOrigin() - GetAbsOrigin()).Length2D() <= HOUNDEYE_MAX_ATTACK_RADIUS) { if (pEntity->GetMoveType() == MOVETYPE_VPHYSICS || (pEntity->VPhysicsGetObject() && !pEntity->IsPlayer())) { IPhysicsObject *pPhysObject = pEntity->VPhysicsGetObject(); if (pPhysObject) { float flMass = pPhysObject->GetMass(); if (flMass <= HOUNDEYE_TOP_MASS) { // Increase the vertical lift of the force Vector vecForce = info.GetDamageForce(); vecForce.z *= 2.0f; info.SetDamageForce(vecForce); pEntity->VPhysicsTakeDamage(info); } } } } } } } } }
void CSnark::HuntThink( void ) { if (!IsInWorld()) { SetTouch( NULL ); UTIL_Remove( this ); return; } StudioFrameAdvance( ); SetNextThink( gpGlobals->curtime + 0.1f ); // explode when ready if ( gpGlobals->curtime >= m_flDie ) { g_vecAttackDir = GetAbsVelocity(); VectorNormalize( g_vecAttackDir ); m_iHealth = -1; CTakeDamageInfo info( this, this, 1, DMG_GENERIC ); Event_Killed( info ); return; } // float if ( GetWaterLevel() != 0) { if ( GetMoveType() == MOVETYPE_FLYGRAVITY ) { SetMoveType( MOVETYPE_FLY, MOVECOLLIDE_FLY_BOUNCE ); } Vector vecVel = GetAbsVelocity(); vecVel *= 0.9; vecVel.z += 8.0; SetAbsVelocity( vecVel ); } else if ( GetMoveType() == MOVETYPE_FLY ) { SetMoveType( MOVETYPE_FLYGRAVITY, MOVECOLLIDE_FLY_BOUNCE ); } // return if not time to hunt if ( m_flNextHunt > gpGlobals->curtime ) return; m_flNextHunt = gpGlobals->curtime + 2.0; Vector vecFlat = GetAbsVelocity(); vecFlat.z = 0; VectorNormalize( vecFlat ); if ( GetEnemy() == NULL || !GetEnemy()->IsAlive() ) { // find target, bounce a bit towards it. GetSenses()->Look( 512 ); SetEnemy( BestEnemy() ); } // squeek if it's about time blow up if ( (m_flDie - gpGlobals->curtime <= 0.5) && (m_flDie - gpGlobals->curtime >= 0.3) ) { CPASAttenuationFilter filter( this ); enginesound->EmitSound( filter, entindex(), CHAN_VOICE, "squeek/sqk_die1.wav", 1, ATTN_NORM, 0, 100 + random->RandomInt( 0, 0x3F ) ); CSoundEnt::InsertSound( SOUND_COMBAT, GetAbsOrigin(), 256, 0.25 ); } // higher pitch as squeeker gets closer to detonation time float flpitch = 155.0 - 60.0 * ( (m_flDie - gpGlobals->curtime) / SQUEEK_DETONATE_DELAY ); if ( flpitch < 80 ) flpitch = 80; if ( GetEnemy() != NULL ) { if ( FVisible( GetEnemy() ) ) { m_vecTarget = GetEnemy()->EyePosition() - GetAbsOrigin(); VectorNormalize( m_vecTarget ); } float flVel = GetAbsVelocity().Length(); float flAdj = 50.0 / ( flVel + 10.0 ); if ( flAdj > 1.2 ) flAdj = 1.2; // ALERT( at_console, "think : enemy\n"); // ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z ); SetAbsVelocity( GetAbsVelocity() * flAdj + (m_vecTarget * 300) ); } if ( GetFlags() & FL_ONGROUND ) { SetLocalAngularVelocity( QAngle( 0, 0, 0 ) ); } else { QAngle angVel = GetLocalAngularVelocity(); if ( angVel == QAngle( 0, 0, 0 ) ) { angVel.x = random->RandomFloat( -100, 100 ); angVel.z = random->RandomFloat( -100, 100 ); SetLocalAngularVelocity( angVel ); } } if ( ( GetAbsOrigin() - m_posPrev ).Length() < 1.0 ) { Vector vecVel = GetAbsVelocity(); vecVel.x = random->RandomFloat( -100, 100 ); vecVel.y = random->RandomFloat( -100, 100 ); SetAbsVelocity( vecVel ); } m_posPrev = GetAbsOrigin(); QAngle angles; VectorAngles( GetAbsVelocity(), angles ); angles.z = 0; angles.x = 0; SetAbsAngles( angles ); }
//========================================================= // SonicAttack //========================================================= void CHoundeye :: SonicAttack ( void ) { float flAdjustedDamage; float flDist; switch ( RANDOM_LONG( 0, 2 ) ) { case 0: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast1.wav", 1, ATTN_NORM); break; case 1: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast2.wav", 1, ATTN_NORM); break; case 2: EMIT_SOUND(ENT(pev), CHAN_WEAPON, "houndeye/he_blast3.wav", 1, ATTN_NORM); break; } // blast circles MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); WRITE_COORD( pev->origin.z + 16); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); WRITE_COORD( pev->origin.z + 16 + HOUNDEYE_MAX_ATTACK_RADIUS / .2); // reach damage radius over .3 seconds WRITE_SHORT( m_iSpriteTexture ); WRITE_BYTE( 0 ); // startframe WRITE_BYTE( 0 ); // framerate WRITE_BYTE( 2 ); // life WRITE_BYTE( 16 ); // width WRITE_BYTE( 0 ); // noise WriteBeamColor(); WRITE_BYTE( 255 ); //brightness WRITE_BYTE( 0 ); // speed MESSAGE_END(); MESSAGE_BEGIN( MSG_PAS, SVC_TEMPENTITY, pev->origin ); WRITE_BYTE( TE_BEAMCYLINDER ); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); WRITE_COORD( pev->origin.z + 16); WRITE_COORD( pev->origin.x); WRITE_COORD( pev->origin.y); WRITE_COORD( pev->origin.z + 16 + ( HOUNDEYE_MAX_ATTACK_RADIUS / 2 ) / .2); // reach damage radius over .3 seconds WRITE_SHORT( m_iSpriteTexture ); WRITE_BYTE( 0 ); // startframe WRITE_BYTE( 0 ); // framerate WRITE_BYTE( 2 ); // life WRITE_BYTE( 16 ); // width WRITE_BYTE( 0 ); // noise WriteBeamColor(); WRITE_BYTE( 255 ); //brightness WRITE_BYTE( 0 ); // speed MESSAGE_END(); CBaseEntity *pEntity = NULL; // iterate on all entities in the vicinity. while ((pEntity = UTIL_FindEntityInSphere( pEntity, pev->origin, HOUNDEYE_MAX_ATTACK_RADIUS )) != NULL) { if ( pEntity->pev->takedamage != DAMAGE_NO ) { if ( !FClassnameIs(pEntity->pev, "monster_houndeye") ) {// houndeyes don't hurt other houndeyes with their attack // houndeyes do FULL damage if the ent in question is visible. Half damage otherwise. // This means that you must get out of the houndeye's attack range entirely to avoid damage. // Calculate full damage first if ( SquadCount() > 1 ) { // squad gets attack bonus. flAdjustedDamage = gSkillData.houndeyeDmgBlast + gSkillData.houndeyeDmgBlast * ( HOUNDEYE_SQUAD_BONUS * ( SquadCount() - 1 ) ); } else { // solo flAdjustedDamage = gSkillData.houndeyeDmgBlast; } flDist = (pEntity->Center() - pev->origin).Length(); flAdjustedDamage -= ( flDist / HOUNDEYE_MAX_ATTACK_RADIUS ) * flAdjustedDamage; if ( !FVisible( pEntity ) ) { if ( pEntity->IsPlayer() ) { // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients // so that monsters in other parts of the level don't take the damage and get pissed. flAdjustedDamage *= 0.5; } else if ( !FClassnameIs( pEntity->pev, "func_breakable" ) && !FClassnameIs( pEntity->pev, "func_pushable" ) ) { // do not hurt nonclients through walls, but allow damage to be done to breakables flAdjustedDamage = 0; } } //ALERT ( at_aiconsole, "Damage: %f\n", flAdjustedDamage ); if (flAdjustedDamage > 0 ) { pEntity->TakeDamage ( pev, pev, flAdjustedDamage, DMG_SONIC | DMG_ALWAYSGIB ); } } } } }
void CBaseMonster::Look(int iDistance) { int iSighted = 0; ClearConditions(bits_COND_SEE_HATE | bits_COND_SEE_DISLIKE | bits_COND_SEE_ENEMY | bits_COND_SEE_FEAR | bits_COND_SEE_NEMESIS | bits_COND_SEE_CLIENT); m_pLink = NULL; CBaseEntity *pSightEnt = NULL; CBaseEntity *pList[100]; Vector delta = Vector(iDistance, iDistance, iDistance); int count = UTIL_EntitiesInBox(pList, 100, pev->origin - delta, pev->origin + delta, FL_CLIENT | FL_MONSTER); for (int i = 0; i < count; i++) { pSightEnt = pList[i]; if (pSightEnt != this && pSightEnt->pev->health > 0) { if (IRelationship(pSightEnt) != R_NO && FInViewCone(pSightEnt) && !FBitSet(pSightEnt->pev->flags, FL_NOTARGET) && FVisible(pSightEnt)) { if (pSightEnt->IsPlayer()) iSighted |= bits_COND_SEE_CLIENT; pSightEnt->m_pLink = m_pLink; m_pLink = pSightEnt; if (pSightEnt == m_hEnemy) iSighted |= bits_COND_SEE_ENEMY; switch (IRelationship(pSightEnt)) { case R_NM: iSighted |= bits_COND_SEE_NEMESIS; break; case R_HT: iSighted |= bits_COND_SEE_HATE; break; case R_DL: iSighted |= bits_COND_SEE_DISLIKE; break; case R_FR: iSighted |= bits_COND_SEE_FEAR; break; case R_AL: break; } } } } SetConditions(iSighted); }
void CSqueakGrenade::HuntThink( void ) { // ALERT( at_console, "think\n" ); if (!IsInWorld()) { SetTouch( NULL ); UTIL_Remove( this ); return; } StudioFrameAdvance( ); pev->nextthink = gpGlobals->time + 0.1; // explode when ready if (gpGlobals->time >= m_flDie) { g_vecAttackDir = pev->velocity.Normalize( ); pev->health = -1; Killed( pev, 0 ); return; } // float if (pev->waterlevel != 0) { if (pev->movetype == MOVETYPE_BOUNCE) { pev->movetype = MOVETYPE_FLY; } pev->velocity = pev->velocity * 0.9; pev->velocity.z += 8.0; } else if (pev->movetype = MOVETYPE_FLY) { pev->movetype = MOVETYPE_BOUNCE; } // return if not time to hunt if (m_flNextHunt > gpGlobals->time) return; m_flNextHunt = gpGlobals->time + 2.0; CBaseEntity *pOther = NULL; Vector vecDir; TraceResult tr; Vector vecFlat = pev->velocity; vecFlat.z = 0; vecFlat = vecFlat.Normalize( ); UTIL_MakeVectors( pev->angles ); if (m_hEnemy == NULL || !m_hEnemy->IsAlive()) { // find target, bounce a bit towards it. Look( 512 ); m_hEnemy = BestVisibleEnemy( ); } // squeek if it's about time blow up if ((m_flDie - gpGlobals->time <= 0.5) && (m_flDie - gpGlobals->time >= 0.3)) { EMIT_SOUND_DYN(ENT(pev), CHAN_VOICE, "squeek/sqk_die1.wav", 1, ATTN_NORM, 0, 100 + RANDOM_LONG(0,0x3F)); CSoundEnt::InsertSound ( bits_SOUND_COMBAT, pev->origin, 256, 0.25 ); } // higher pitch as squeeker gets closer to detonation time float flpitch = 155.0 - 60.0 * ((m_flDie - gpGlobals->time) / SQUEEK_DETONATE_DELAY); if (flpitch < 80) flpitch = 80; if (m_hEnemy != NULL) { if (FVisible( m_hEnemy )) { vecDir = m_hEnemy->EyePosition() - pev->origin; m_vecTarget = vecDir.Normalize( ); } float flVel = pev->velocity.Length(); float flAdj = 50.0 / (flVel + 10.0); if (flAdj > 1.2) flAdj = 1.2; // ALERT( at_console, "think : enemy\n"); // ALERT( at_console, "%.0f %.2f %.2f %.2f\n", flVel, m_vecTarget.x, m_vecTarget.y, m_vecTarget.z ); pev->velocity = pev->velocity * flAdj + m_vecTarget * 300; } if (pev->flags & FL_ONGROUND) { pev->avelocity = Vector( 0, 0, 0 ); } else { if (pev->avelocity == Vector( 0, 0, 0)) { pev->avelocity.x = RANDOM_FLOAT( -100, 100 ); pev->avelocity.z = RANDOM_FLOAT( -100, 100 ); } } if ((pev->origin - m_posPrev).Length() < 1.0) { pev->velocity.x = RANDOM_FLOAT( -100, 100 ); pev->velocity.y = RANDOM_FLOAT( -100, 100 ); } m_posPrev = pev->origin; pev->angles = UTIL_VecToAngles( pev->velocity ); pev->angles.z = 0; pev->angles.x = 0; }
bool CNPC_Dog::FindPhysicsObject( const char *pPickupName, CBaseEntity *pIgnore ) { CBaseEntity *pEnt = NULL; CBaseEntity *pNearest = NULL; float flDist; IPhysicsObject *pPhysObj = NULL; float flNearestDist = 99999; if ( pPickupName != NULL && strlen( pPickupName ) > 0 ) { pEnt = gEntList.FindEntityByName( NULL, pPickupName ); if ( m_hUnreachableObjects.Find( pEnt ) == -1 ) { m_bHasObject = false; m_hPhysicsEnt = pEnt; return true; } } while ( ( pEnt = gEntList.FindEntityByClassname( pEnt, "prop_physics" ) ) != NULL ) { //We don't want this one. if ( pEnt == pIgnore ) continue; if ( m_hUnreachableObjects.Find( pEnt ) != -1 ) continue; pPhysObj = pEnt->VPhysicsGetObject(); if( pPhysObj == NULL ) continue; if ( pPhysObj->GetMass() > DOG_MAX_THROW_MASS ) continue; Vector center = pEnt->WorldSpaceCenter(); flDist = UTIL_DistApprox2D( GetAbsOrigin(), center ); vcollide_t *pCollide = modelinfo->GetVCollide( pEnt->GetModelIndex() ); if ( pCollide == NULL ) continue; if ( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) continue; if ( pPhysObj->IsMoveable() == false ) continue; if ( pEnt->GetCollisionGroup() == COLLISION_GROUP_DEBRIS || pEnt->GetCollisionGroup() == COLLISION_GROUP_INTERACTIVE_DEBRIS ) continue; if ( center.z > EyePosition().z ) continue; if ( flDist >= flNearestDist ) continue; if ( FVisible( pEnt ) == false ) continue; pNearest = pEnt; flNearestDist = flDist; } m_bHasObject = false; m_hPhysicsEnt = pNearest; if ( dog_debug.GetBool() == true ) { if ( pNearest ) NDebugOverlay::Box( pNearest->WorldSpaceCenter(), pNearest->CollisionProp()->OBBMins(), pNearest->CollisionProp()->OBBMaxs(), 255, 0, 255, true, 3 ); } if( m_hPhysicsEnt == NULL ) { return false; } else { return true; } }
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 ); } } } }
void CTFBaseRocket::FlyThink( void ) { if ( gpGlobals->curtime > m_flCollideWithTeammatesTime && m_bCollideWithTeammates == false ) { m_bCollideWithTeammates = true; } if ( tf2c_homing_rockets.GetBool() || ( tf2c_homing_deflected_rockets.GetBool() && m_iDeflected ) ) { // Find the closest visible enemy player. CUtlVector<CTFPlayer *> vecPlayers; int count = CollectPlayers( &vecPlayers, TEAM_ANY, true ); float flClosest = FLT_MAX; Vector vecClosestTarget = vec3_origin; for ( int i = 0; i < count; i++ ) { CTFPlayer *pPlayer = vecPlayers[i]; if ( pPlayer == GetOwnerEntity() ) continue; if ( pPlayer->GetTeamNumber() == GetTeamNumber() && !TFGameRules()->IsDeathmatch() ) continue; Vector vecTarget; QAngle angTarget; if ( GetWeaponID() == TF_WEAPON_COMPOUND_BOW ) { int iBone = pPlayer->LookupBone( "bip_head" ); pPlayer->GetBonePosition( iBone, vecTarget, angTarget );; } else { vecTarget = pPlayer->EyePosition(); } if ( FVisible( vecTarget ) ) { float flDistSqr = ( vecTarget - GetAbsOrigin() ).LengthSqr(); if ( flDistSqr < flClosest ) { flClosest = flDistSqr; vecClosestTarget = vecTarget; } } } // Head towards him. if ( vecClosestTarget != vec3_origin ) { Vector vecTarget = vecClosestTarget; Vector vecDir = vecTarget - GetAbsOrigin(); VectorNormalize( vecDir ); float flSpeed = GetAbsVelocity().Length(); QAngle angForward; VectorAngles( vecDir, angForward ); SetAbsAngles( angForward ); SetAbsVelocity( vecDir * flSpeed ); } } SetNextThink( gpGlobals->curtime + 0.1f ); }
//--------------------------------------------------------- // 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 ); }
//========================================================= // GetSchedule //========================================================= int CNPC_Bullsquid::SelectSchedule( void ) { switch ( m_NPCState ) { case NPC_STATE_ALERT: { if ( HasCondition( COND_LIGHT_DAMAGE ) || HasCondition( COND_HEAVY_DAMAGE ) ) { return SCHED_SQUID_HURTHOP; } if ( HasCondition( COND_SQUID_SMELL_FOOD ) ) { CSound *pSound; pSound = GetBestScent(); if ( pSound && (!FInViewCone ( pSound->GetSoundOrigin() ) || !FVisible ( pSound->GetSoundOrigin() )) ) { // scent is behind or occluded return SCHED_SQUID_SNIFF_AND_EAT; } // food is right out in the open. Just go get it. return SCHED_SQUID_EAT; } if ( HasCondition( COND_SMELL ) ) { // there's something stinky. CSound *pSound; pSound = GetBestScent(); if ( pSound ) return SCHED_SQUID_WALLOW; } 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 ( HasCondition( COND_NEW_ENEMY ) ) { if ( m_fCanThreatDisplay && IRelationType( GetEnemy() ) == D_HT && FClassnameIs( GetEnemy(), "monster_headcrab" ) ) { // this means squid sees a headcrab! m_fCanThreatDisplay = FALSE;// only do the headcrab dance once per lifetime. return SCHED_SQUID_SEECRAB; } else { return SCHED_WAKE_ANGRY; } } if ( HasCondition( COND_SQUID_SMELL_FOOD ) ) { CSound *pSound; pSound = GetBestScent(); if ( pSound && (!FInViewCone ( pSound->GetSoundOrigin() ) || !FVisible ( pSound->GetSoundOrigin() )) ) { // scent is behind or occluded return SCHED_SQUID_SNIFF_AND_EAT; } // food is right out in the open. Just go get it. return SCHED_SQUID_EAT; } if ( HasCondition( COND_CAN_RANGE_ATTACK1 ) ) { return SCHED_RANGE_ATTACK1; } if ( HasCondition( COND_CAN_MELEE_ATTACK1 ) ) { return SCHED_MELEE_ATTACK1; } if ( HasCondition( COND_CAN_MELEE_ATTACK2 ) ) { return SCHED_MELEE_ATTACK2; } return SCHED_CHASE_ENEMY; break; } } return BaseClass::SelectSchedule(); }
//========================================================= // GetSchedule //========================================================= Schedule_t *CBullsquid :: GetSchedule( void ) { switch ( m_MonsterState ) { case MONSTERSTATE_ALERT: { if ( HasConditions(bits_COND_LIGHT_DAMAGE | bits_COND_HEAVY_DAMAGE) ) { return GetScheduleOfType ( SCHED_SQUID_HURTHOP ); } if ( HasConditions(bits_COND_SMELL_FOOD) ) { CSound *pSound; pSound = PBestScent(); if ( pSound && (!FInViewCone ( &pSound->m_vecOrigin ) || !FVisible ( pSound->m_vecOrigin )) ) { // scent is behind or occluded return GetScheduleOfType( SCHED_SQUID_SNIFF_AND_EAT ); } // food is right out in the open. Just go get it. return GetScheduleOfType( SCHED_SQUID_EAT ); } if ( HasConditions(bits_COND_SMELL) ) { // there's something stinky. CSound *pSound; pSound = PBestScent(); if ( pSound ) return GetScheduleOfType( SCHED_SQUID_WALLOW); } break; } case MONSTERSTATE_COMBAT: { // dead enemy if ( HasConditions( bits_COND_ENEMY_DEAD ) ) { // call base class, all code to handle dead enemies is centralized there. return CBaseMonster :: GetSchedule(); } if ( HasConditions(bits_COND_NEW_ENEMY) ) { if ( m_fCanThreatDisplay && IRelationship( m_hEnemy ) == R_HT ) { // this means squid sees a headcrab! m_fCanThreatDisplay = FALSE;// only do the headcrab dance once per lifetime. return GetScheduleOfType ( SCHED_SQUID_SEECRAB ); } else { return GetScheduleOfType ( SCHED_WAKE_ANGRY ); } } if ( HasConditions(bits_COND_SMELL_FOOD) ) { CSound *pSound; pSound = PBestScent(); if ( pSound && (!FInViewCone ( &pSound->m_vecOrigin ) || !FVisible ( pSound->m_vecOrigin )) ) { // scent is behind or occluded return GetScheduleOfType( SCHED_SQUID_SNIFF_AND_EAT ); } // food is right out in the open. Just go get it. return GetScheduleOfType( SCHED_SQUID_EAT ); } if ( HasConditions( bits_COND_CAN_RANGE_ATTACK1 ) ) { return GetScheduleOfType ( SCHED_RANGE_ATTACK1 ); } if ( HasConditions( bits_COND_CAN_MELEE_ATTACK1 ) ) { return GetScheduleOfType ( SCHED_MELEE_ATTACK1 ); } if ( HasConditions( bits_COND_CAN_MELEE_ATTACK2 ) ) { return GetScheduleOfType ( SCHED_MELEE_ATTACK2 ); } return GetScheduleOfType ( SCHED_CHASE_ENEMY ); break; } } return CBaseMonster :: GetSchedule(); }
//========================================================= // Hornet is flying, gently tracking target //========================================================= void CNPC_Hornet::TrackTarget ( void ) { Vector vecFlightDir; Vector vecDirToEnemy; float flDelta; StudioFrameAdvance( ); if (gpGlobals->curtime > m_flStopAttack) { SetTouch( NULL ); SetThink( &CBaseEntity::SUB_Remove ); SetNextThink( gpGlobals->curtime + 0.1f ); return; } // UNDONE: The player pointer should come back after returning from another level if ( GetEnemy() == NULL ) {// enemy is dead. GetSenses()->Look( 1024 ); SetEnemy( BestEnemy() ); } if ( GetEnemy() != NULL && FVisible( GetEnemy() )) { m_vecEnemyLKP = GetEnemy()->BodyTarget( GetAbsOrigin() ); } else { m_vecEnemyLKP = m_vecEnemyLKP + GetAbsVelocity() * m_flFlySpeed * 0.1; } vecDirToEnemy = m_vecEnemyLKP - GetAbsOrigin(); VectorNormalize( vecDirToEnemy ); if ( GetAbsVelocity().Length() < 0.1 ) vecFlightDir = vecDirToEnemy; else { vecFlightDir = GetAbsVelocity(); VectorNormalize( vecFlightDir ); } SetAbsVelocity( vecFlightDir + vecDirToEnemy ); // measure how far the turn is, the wider the turn, the slow we'll go this time. flDelta = DotProduct ( vecFlightDir, vecDirToEnemy ); if ( flDelta < 0.5 ) {// hafta turn wide again. play sound CPASAttenuationFilter filter( this ); EmitSound( filter, entindex(), "Hornet.Buzz" ); } if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED ) {// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far. flDelta = 0.25; } Vector vecVel = vecFlightDir + vecDirToEnemy; VectorNormalize( vecVel ); if ( GetOwnerEntity() && (GetOwnerEntity()->GetFlags() & FL_NPC) ) { // random pattern only applies to hornets fired by monsters, not players. vecVel.x += random->RandomFloat ( -0.10, 0.10 );// scramble the flight dir a bit. vecVel.y += random->RandomFloat ( -0.10, 0.10 ); vecVel.z += random->RandomFloat ( -0.10, 0.10 ); } switch ( m_iHornetType ) { case HORNET_TYPE_RED: SetAbsVelocity( vecVel * ( m_flFlySpeed * flDelta ) );// scale the dir by the ( speed * width of turn ) SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.1, 0.3 ) ); break; default: Assert( false ); //fall through if release case HORNET_TYPE_ORANGE: SetAbsVelocity( vecVel * m_flFlySpeed );// do not have to slow down to turn. SetNextThink( gpGlobals->curtime + 0.1f );// fixed think time break; } QAngle angNewAngles; VectorAngles( GetAbsVelocity(), angNewAngles ); SetAbsAngles( angNewAngles ); SetSolid( SOLID_BBOX ); // if hornet is close to the enemy, jet in a straight line for a half second. // (only in the single player game) if ( GetEnemy() != NULL && !g_pGameRules->IsMultiplayer() ) { if ( flDelta >= 0.4 && ( GetAbsOrigin() - m_vecEnemyLKP ).Length() <= 300 ) { CPVSFilter filter( GetAbsOrigin() ); te->Sprite( filter, 0.0, &GetAbsOrigin(), // pos iHornetPuff, // model 0.2, //size 128 // brightness ); CPASAttenuationFilter filter2( this ); EmitSound( filter2, entindex(), "Hornet.Buzz" ); SetAbsVelocity( GetAbsVelocity() * 2 ); SetNextThink( gpGlobals->curtime + 1.0f ); // don't attack again m_flStopAttack = gpGlobals->curtime; } } }
void CIchthyosaur :: RunTask ( Task_t *pTask ) { switch ( pTask->iTask ) { case TASK_ICHTHYOSAUR_CIRCLE_ENEMY: if (m_hEnemy == NULL) { TaskComplete( ); } else if (FVisible( m_hEnemy )) { Vector vecFrom = m_hEnemy->EyePosition( ); Vector vecDelta = (pev->origin - vecFrom).Normalize( ); Vector vecSwim = CrossProduct( vecDelta, Vector( 0, 0, 1 ) ).Normalize( ); if (DotProduct( vecSwim, m_SaveVelocity ) < 0) vecSwim = vecSwim * -1.0; Vector vecPos = vecFrom + vecDelta * m_idealDist + vecSwim * 32; // ALERT( at_console, "vecPos %.0f %.0f %.0f\n", vecPos.x, vecPos.y, vecPos.z ); TraceResult tr; UTIL_TraceHull( vecFrom, vecPos, ignore_monsters, large_hull, m_hEnemy->edict(), &tr ); if (tr.flFraction > 0.5) vecPos = tr.vecEndPos; m_SaveVelocity = m_SaveVelocity * 0.8 + 0.2 * (vecPos - pev->origin).Normalize() * m_flightSpeed; // ALERT( at_console, "m_SaveVelocity %.2f %.2f %.2f\n", m_SaveVelocity.x, m_SaveVelocity.y, m_SaveVelocity.z ); if (HasConditions( bits_COND_ENEMY_FACING_ME ) && m_hEnemy->FVisible( this )) { m_flNextAlert -= 0.1; if (m_idealDist < m_flMaxDist) { m_idealDist += 4; } if (m_flightSpeed > m_flMinSpeed) { m_flightSpeed -= 2; } else if (m_flightSpeed < m_flMinSpeed) { m_flightSpeed += 2; } if (m_flMinSpeed < m_flMaxSpeed) { m_flMinSpeed += 0.5; } } else { m_flNextAlert += 0.1; if (m_idealDist > 128) { m_idealDist -= 4; } if (m_flightSpeed < m_flMaxSpeed) { m_flightSpeed += 4; } } // ALERT( at_console, "%.0f\n", m_idealDist ); } else { m_flNextAlert = gpGlobals->time + 0.2; } if (m_flNextAlert < gpGlobals->time) { // ALERT( at_console, "AlertSound()\n"); AlertSound( ); m_flNextAlert = gpGlobals->time + RANDOM_FLOAT( 3, 5 ); } break; case TASK_ICHTHYOSAUR_SWIM: if (m_fSequenceFinished) { TaskComplete( ); } break; case TASK_DIE: if ( m_fSequenceFinished ) { pev->deadflag = DEAD_DEAD; TaskComplete( ); } break; case TASK_ICHTHYOSAUR_FLOAT: pev->angles.x = UTIL_ApproachAngle( 0, pev->angles.x, 20 ); pev->velocity = pev->velocity * 0.8; if (pev->waterlevel > 1 && pev->velocity.z < 64) { pev->velocity.z += 8; } else { pev->velocity.z -= 8; } // ALERT( at_console, "%f\n", pev->velocity.z ); break; default: CFlyingMonster :: RunTask ( pTask ); break; } }
//--------------------------------------------------------- //--------------------------------------------------------- void CNPC_Dog::RunTask( const Task_t *pTask ) { switch( pTask->iTask ) { case TASK_DOG_PICKUP_ITEM: { PullObject( false ); } break; case TASK_DOG_GET_PATH_TO_PHYSOBJ: { //Check this cause our object might have been deleted. if ( m_hPhysicsEnt == NULL ) FindPhysicsObject( NULL ); //And if we still can't find anything, then just go away. if ( m_hPhysicsEnt == NULL ) { TaskFail( "Can't find an object I like!" ); return; } IPhysicsObject *pPhysicsObject = m_hPhysicsEnt->VPhysicsGetObject(); Vector vecGoalPos; Vector vecDir; vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter(); VectorNormalize(vecDir); vecDir.z = 0; if ( m_hPhysicsEnt->GetOwnerEntity() == NULL ) m_hPhysicsEnt->SetOwnerEntity( this ); if ( pPhysicsObject ) pPhysicsObject->RecheckCollisionFilter(); vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST ); bool bBuiltRoute = false; //If I'm near my goal, then just walk to it. Activity aActivity = ACT_RUN; if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 ) aActivity = ACT_WALK; bBuiltRoute = GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL ); if ( bBuiltRoute == true ) TaskComplete(); else { m_flTimeToCatch = gpGlobals->curtime + 0.1; m_flNextRouteTime = gpGlobals->curtime + 0.3; m_flNextSwat = gpGlobals->curtime + 0.1; if ( m_hUnreachableObjects.Find( m_hPhysicsEnt ) == -1 ) m_hUnreachableObjects.AddToTail( m_hPhysicsEnt ); m_hPhysicsEnt = NULL; GetNavigator()->ClearGoal(); } } break; case TASK_WAIT: { if ( IsWaitFinished() ) { TaskComplete(); } if ( m_hPhysicsEnt ) { if ( m_bHasObject == false ) { GetMotor()->SetIdealYawToTarget( m_hPhysicsEnt->GetAbsOrigin() ); GetMotor()->UpdateYaw(); } } break; } case TASK_DOG_LAUNCH_ITEM: if( IsActivityFinished() ) { if ( m_hPhysicsEnt ) { m_hPhysicsEnt->SetOwnerEntity( NULL ); } TaskComplete(); } break; case TASK_DOG_WAIT_FOR_TARGET_TO_FACE: { if ( CanTargetSeeMe() ) TaskComplete(); } break; case TASK_WAIT_FOR_MOVEMENT: { if ( GetState() == NPC_STATE_SCRIPT || IsInAScript() ) { BaseClass::RunTask( pTask ); return; } if ( m_hPhysicsEnt != NULL ) { IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject(); if ( !pPhysObj ) { Warning( "npc_dog TASK_WAIT_FOR_MOVEMENT with NULL m_hPhysicsEnt->VPhysicsGetObject\n" ); } if ( pPhysObj && pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) TaskFail( "Player picked it up!" ); //If the object is moving then my old goal might not be valid //cancel the schedule and make it restart again in a bit. if ( pPhysObj && pPhysObj->IsAsleep() == false && GetNavigator()->IsGoalActive() == false ) { Vector vecGoalPos; Vector vecDir; vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter(); VectorNormalize(vecDir); vecDir.z = 0; vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST ); GetNavigator()->ClearGoal(); float flDistance = (vecGoalPos - GetLocalOrigin()).Length(); //If I'm near my goal, then just walk to it. Activity aActivity = ACT_RUN; if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 ) aActivity = ACT_WALK; GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL ); if ( flDistance <= DOG_PHYSOBJ_MOVE_TO_DIST ) { TaskComplete(); GetNavigator()->StopMoving(); } } } BaseClass::RunTask( pTask ); } break; case TASK_DOG_WAIT_FOR_OBJECT: { if ( m_hPhysicsEnt != NULL ) { if ( FVisible( m_hPhysicsEnt ) == false ) { m_flTimeToCatch = 0.0f; ClearBeams(); TaskFail( "Lost sight of the object!" ); m_hPhysicsEnt->SetOwnerEntity( NULL ); return; } m_hPhysicsEnt->SetOwnerEntity( this ); Vector vForward; AngleVectors( GetAbsAngles(), &vForward ); Vector vGunPos; GetAttachment( m_iPhysGunAttachment, vGunPos ); Vector vToObject = m_hPhysicsEnt->WorldSpaceCenter() - vGunPos; float flDistance = vToObject.Length(); VectorNormalize( vToObject ); SetAim( m_hPhysicsEnt->WorldSpaceCenter() - GetAbsOrigin() ); #ifdef SecobMod__Enable_Fixed_Multiplayer_AI CBasePlayer *pPlayer = UTIL_GetNearestVisiblePlayer(this); #else CBasePlayer *pPlayer = AI_GetSinglePlayer(); #endif //SecobMod__Enable_Fixed_Multiplayer_AI float flDistanceToPlayer = flDistance; if ( pPlayer ) { flDistanceToPlayer = (pPlayer->GetAbsOrigin() - m_hPhysicsEnt->WorldSpaceCenter()).Length(); } IPhysicsObject *pPhysObj = m_hPhysicsEnt->VPhysicsGetObject(); if ( !pPhysObj ) { Warning( "npc_dog: TASK_DOG_WAIT_FOR_OBJECT with m_hPhysicsEnt->VPhysicsGetObject == NULL\n" ); } if ( pPhysObj && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) && flDistanceToPlayer > ( flDistance * 2 ) ) { if ( m_flTimeToPull <= gpGlobals->curtime ) { Vector vCurrentVel; float flCurrentVel; AngularImpulse vCurrentAI; pPhysObj->GetVelocity( &vCurrentVel, &vCurrentAI ); flCurrentVel = vCurrentVel.Length(); VectorNormalize( vCurrentVel ); if ( pPhysObj && flDistance <= DOG_PULL_DISTANCE ) { Vector vDir = ( vGunPos - m_hPhysicsEnt->WorldSpaceCenter() ); VectorNormalize( vDir ); vCurrentVel = vCurrentVel * ( flCurrentVel * DOG_PULL_VELOCITY_MOD ); vCurrentAI = vCurrentAI * DOG_PULL_ANGULARIMP_MOD; pPhysObj->SetVelocity( &vCurrentVel, &vCurrentAI ); vDir = vDir * flDistance * DOG_PULL_TO_GUN_VEL_MOD; Vector vAngle( 0, 0, 0 ); pPhysObj->AddVelocity( &vDir, &vAngle ); CreateBeams(); } float flDot = DotProduct( vCurrentVel, vForward ); if ( flDistance >= DOG_PULL_DISTANCE && flDistance <= ( DOG_PULL_DISTANCE * 2 ) && flDot > -0.3 ) { if ( pPhysObj->IsAsleep() == false && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) ) { Vector vecGoalPos; Vector vecDir; vecDir = GetLocalOrigin() - m_hPhysicsEnt->WorldSpaceCenter(); VectorNormalize(vecDir); vecDir.z = 0; vecGoalPos = m_hPhysicsEnt->WorldSpaceCenter() + (vecDir * DOG_PHYSOBJ_MOVE_TO_DIST ); GetNavigator()->ClearGoal(); //If I'm near my goal, then just walk to it. Activity aActivity = ACT_RUN; if ( ( vecGoalPos - GetLocalOrigin() ).Length() <= 128 ) aActivity = ACT_WALK; GetNavigator()->SetGoal( AI_NavGoal_t( vecGoalPos, aActivity ), AIN_NO_PATH_TASK_FAIL ); } } } } float flDirDot = DotProduct( vToObject, vForward ); if ( flDirDot < 0.2 ) { GetMotor()->SetIdealYawToTarget( m_hPhysicsEnt->GetAbsOrigin() ); GetMotor()->UpdateYaw(); } if ( m_flTimeToCatch < gpGlobals->curtime && m_bDoWaitforObjectBehavior == false ) { m_hPhysicsEnt->SetOwnerEntity( NULL ); m_flTimeToCatch = 0.0f; ClearBeams(); TaskFail( "Done waiting!" ); } else if ( pPhysObj && ( flDistance <= DOG_CATCH_DISTANCE && !( pPhysObj->GetGameFlags() & FVPHYSICS_PLAYER_HELD ) ) ) { AngularImpulse vZero( 0, 0, 0 ); pPhysObj->SetVelocity( &vec3_origin, &vZero ); GetNavigator()->StopMoving(); //Fire Output! m_OnCatch.FireOutput( this, this ); m_bHasObject = true; ClearBeams(); TaskComplete(); } } else { GetNavigator()->StopMoving(); ClearBeams(); TaskFail("No Physics Object!"); } } break; case TASK_DOG_CATCH_OBJECT: if( IsActivityFinished() ) { m_flTimeToCatch = 0.0f; TaskComplete(); } break; default: BaseClass::RunTask( pTask ); break; } }
void CNihilanth :: HuntThink( void ) { pev->nextthink = gpGlobals->time + 0.1; DispatchAnimEvents( ); StudioFrameAdvance( ); ShootBalls( ); // if dead, force cancelation of current animation if (pev->health <= 0) { SetThink( &CNihilanth::DyingThink ); m_fSequenceFinished = TRUE; return; } // ALERT( at_console, "health %.0f\n", pev->health ); // if damaged, try to abosorb some spheres if (pev->health < gSkillData.nihilanthHealth && AbsorbSphere( )) { pev->health += gSkillData.nihilanthHealth / N_SPHERES; } // get new sequence if (m_fSequenceFinished) { // if (!m_fSequenceLoops) pev->frame = 0; NextActivity( ); ResetSequenceInfo( ); pev->framerate = 2.0 - 1.0 * (pev->health / gSkillData.nihilanthHealth); } // look for current enemy if (m_hEnemy != NULL && m_hRecharger == NULL) { if (FVisible( m_hEnemy )) { if (m_flLastSeen < gpGlobals->time - 5) m_flPrevSeen = gpGlobals->time; m_flLastSeen = gpGlobals->time; m_posTarget = m_hEnemy->pev->origin; m_vecTarget = (m_posTarget - pev->origin).Normalize(); m_vecDesired = m_vecTarget; m_posDesired = Vector( pev->origin.x, pev->origin.y, m_posTarget.z + m_flAdj ); } else { m_flAdj = min( m_flAdj + 10, 1000 ); } } // don't go too high if (m_posDesired.z > m_flMaxZ) m_posDesired.z = m_flMaxZ; // don't go too low if (m_posDesired.z < m_flMinZ) m_posDesired.z = m_flMinZ; Flight( ); }
void CBaseTurret::ActiveThink(void) { int fAttack = 0; Vector vecDirToEnemy; SetNextThink( gpGlobals->curtime + 0.1f ); StudioFrameAdvance( ); if ((!m_iOn) || (GetEnemy() == NULL)) { SetEnemy( NULL ); m_flLastSight = gpGlobals->curtime + m_flMaxWait; SetThink(SearchThink); return; } // if it's dead, look for something new if ( !GetEnemy()->IsAlive() ) { if (!m_flLastSight) { m_flLastSight = gpGlobals->curtime + 0.5; // continue-shooting timeout } else { if (gpGlobals->curtime > m_flLastSight) { SetEnemy( NULL ); m_flLastSight = gpGlobals->curtime + m_flMaxWait; SetThink(SearchThink); return; } } } Vector vecMid = EyePosition( ); Vector vecMidEnemy = GetEnemy()->BodyTarget(vecMid); // g_pEffects->Sparks( vecMid ); // g_pEffects->Sparks( vecMidEnemy ); // Look for our current enemy //int fEnemyVisible = FBoxVisible( this, GetEnemy(), vecMidEnemy ); int fEnemyVisible = FInViewCone( GetEnemy() ) && FVisible( GetEnemy() ); vecDirToEnemy = vecMidEnemy - vecMid; // calculate dir and dist to enemy // NDebugOverlay::Line( vecMid, vecMidEnemy, 0, 255, 0, false, 0.1 ); float flDistToEnemy = vecDirToEnemy.Length(); QAngle vecAnglesToEnemy; VectorNormalize( vecDirToEnemy ); VectorAngles( vecDirToEnemy, vecAnglesToEnemy ); // Current enmey is not visible. if (!fEnemyVisible || (flDistToEnemy > TURRET_RANGE)) { // DevMsg( "lost you\n" ); if (!m_flLastSight) { m_flLastSight = gpGlobals->curtime + 0.5; } else { // Should we look for a new target? if (gpGlobals->curtime > m_flLastSight) { ClearEnemyMemory(); SetEnemy( NULL ); m_flLastSight = gpGlobals->curtime + m_flMaxWait; SetThink(SearchThink); return; } } fEnemyVisible = 0; } else { m_vecLastSight = vecMidEnemy; } Vector vecLOS = vecDirToEnemy; //vecMid - m_vecLastSight; VectorNormalize( vecLOS ); Vector vecMuzzle, vecMuzzleDir; QAngle vecMuzzleAng; GetAttachment( "eyes", vecMuzzle, vecMuzzleAng ); AngleVectors( vecMuzzleAng, &vecMuzzleDir ); // Is the Gun looking at the target if (DotProduct(vecLOS, vecMuzzleDir) <= 0.9848) // 10 degree slop { fAttack = FALSE; } else { fAttack = TRUE; } // fire the gun if (fAttack || m_fBeserk) { m_Activity = ACT_RESET; SetActivity( (Activity)ACT_TURRET_FIRE ); Shoot(vecMuzzle, vecMuzzleDir ); } else { SetActivity( (Activity)ACT_TURRET_OPEN_IDLE ); } //move the gun if (m_fBeserk) { // DevMsg( "berserk" ); if (random->RandomInt(0,9) == 0) { m_vecGoalAngles.y = random->RandomFloat(-180,180); m_vecGoalAngles.x = random->RandomFloat(-90,90); OnTakeDamage( CTakeDamageInfo( this, this, 1, DMG_GENERIC ) ); // don't beserk forever return; } } else if (fEnemyVisible) { // DevMsg( "->[%.2f]\n", vec.x); m_vecGoalAngles.y = vecAnglesToEnemy.y; m_vecGoalAngles.x = vecAnglesToEnemy.x; } MoveTurret(); }
//----------------------------------------------------------------------------- // Purpose: Target doesn't exist or has eluded us, so search for one //----------------------------------------------------------------------------- void CNPC_Portal_FloorTurret::SearchThink( void ) { //Allow descended classes a chance to do something before the think function if ( PreThink( TURRET_SEARCHING ) ) return; SetNextThink( gpGlobals->curtime + 0.05f ); SetActivity( (Activity) ACT_FLOOR_TURRET_OPEN_IDLE ); //If our enemy has died, pick a new enemy if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) ) { SetEnemy( NULL ); } //Acquire the target if ( GetEnemy() == NULL ) { HackFindEnemy(); } LaserOn(); CBaseEntity *pEnemy = GetEnemy(); //If we've found a target, spin up the barrel and start to attack if ( pEnemy != NULL ) { //Get our shot positions Vector vecMid = EyePosition(); Vector vecMidEnemy = pEnemy->BodyTarget( vecMid ); //Look for our current enemy bool bEnemyInFOV = FInViewCone( pEnemy ); bool bEnemyVisible = FVisible( pEnemy ); //Calculate dir and dist to enemy Vector vecDirToEnemy = vecMidEnemy - vecMid; m_flDistToEnemy = VectorNormalize( vecDirToEnemy ); // If the enemy isn't in the normal fov, check the fov through portals CProp_Portal *pPortal = NULL; pPortal = FInViewConeThroughPortal( pEnemy ); if ( pPortal && FVisibleThroughPortal( pPortal, pEnemy ) ) { // Translate our target across the portal Vector vecMidEnemyTransformed; UTIL_Portal_PointTransform( pPortal->m_hLinkedPortal->MatrixThisToLinked(), vecMidEnemy, vecMidEnemyTransformed ); //Calculate dir and dist to enemy Vector vecDirToEnemyTransformed = vecMidEnemyTransformed - vecMid; float flDistToEnemyTransformed = VectorNormalize( vecDirToEnemyTransformed ); // If it's not visible through normal means or the enemy is closer through the portal, use the translated info if ( !bEnemyInFOV || !bEnemyVisible || flDistToEnemyTransformed < m_flDistToEnemy ) { bEnemyInFOV = true; bEnemyVisible = true; vecMidEnemy = vecMidEnemyTransformed; vecDirToEnemy = vecDirToEnemyTransformed; m_flDistToEnemy = flDistToEnemyTransformed; } } // Give enemies that are farther away a longer grace period float fDistanceRatio = m_flDistToEnemy / PORTAL_FLOOR_TURRET_RANGE; m_flShotTime = gpGlobals->curtime + fDistanceRatio * fDistanceRatio * PORTAL_FLOOR_TURRET_MAX_SHOT_DELAY; m_flLastSight = 0; SetThink( &CNPC_FloorTurret::ActiveThink ); SetEyeState( TURRET_EYE_SEE_TARGET ); SpinUp(); if ( gpGlobals->curtime > m_flNextActivateSoundTime ) { EmitSound( "NPC_FloorTurret.Activate" ); m_flNextActivateSoundTime = gpGlobals->curtime + 3.0; } return; } //Are we out of time and need to retract? if ( gpGlobals->curtime > m_flLastSight ) { //Before we retrace, make sure that we are spun down. m_flLastSight = 0; SetThink( &CNPC_FloorTurret::Retire ); return; } //Display that we're scanning m_vecGoalAngles.x = GetAbsAngles().x + ( sin( ( m_flLastSight + gpGlobals->curtime * m_fSearchSpeed ) * 1.5f ) * 20.0f ); m_vecGoalAngles.y = GetAbsAngles().y + ( sin( ( m_flLastSight + gpGlobals->curtime * m_fSearchSpeed ) * 2.5f ) * 20.0f ); //Turn and ping UpdateFacing(); Ping(); // Update rope positions for ( int iRope = 0; iRope < PORTAL_FLOOR_TURRET_NUM_ROPES; ++iRope ) { if ( m_hRopes[ iRope ] ) { m_hRopes[ iRope ]->EndpointsChanged(); } } }
void CApache :: HuntThink( void ) { StudioFrameAdvance( ); SetNextThink( 0.1 ); ShowDamage( ); if ( m_pGoalEnt == NULL && !FStringNull(pev->target) )// this monster has a target { m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( pev->target ) ); if (m_pGoalEnt) { m_posDesired = m_pGoalEnt->pev->origin; UTIL_MakeAimVectors( m_pGoalEnt->pev->angles ); m_vecGoal = gpGlobals->v_forward; } } // if (m_hEnemy == NULL) { Look( 4092 ); m_hEnemy = BestVisibleEnemy( ); } // generic speed up if (m_flGoalSpeed < 800) m_flGoalSpeed += 5; if (m_hEnemy != NULL) { // ALERT( at_console, "%s\n", STRING( m_hEnemy->pev->classname ) ); if (FVisible( m_hEnemy )) { if (m_flLastSeen < gpGlobals->time - 5) m_flPrevSeen = gpGlobals->time; m_flLastSeen = gpGlobals->time; m_posTarget = m_hEnemy->Center( ); } else { m_hEnemy = NULL; } } m_vecTarget = (m_posTarget - pev->origin).Normalize(); float flLength = (pev->origin - m_posDesired).Length(); if (m_pGoalEnt) { // ALERT( at_console, "%.0f\n", flLength ); if (flLength < 128) { m_pGoalEnt = UTIL_FindEntityByTargetname( NULL, STRING( m_pGoalEnt->pev->target ) ); if (m_pGoalEnt) { m_posDesired = m_pGoalEnt->pev->origin; UTIL_MakeAimVectors( m_pGoalEnt->pev->angles ); m_vecGoal = gpGlobals->v_forward; flLength = (pev->origin - m_posDesired).Length(); } } } else { m_posDesired = pev->origin; } if (flLength > 250) // 500 { // float flLength2 = (m_posTarget - pev->origin).Length() * (1.5 - DotProduct((m_posTarget - pev->origin).Normalize(), pev->velocity.Normalize() )); // if (flLength2 < flLength) if (m_flLastSeen + 90 > gpGlobals->time && DotProduct( (m_posTarget - pev->origin).Normalize(), (m_posDesired - pev->origin).Normalize( )) > 0.25) { m_vecDesired = (m_posTarget - pev->origin).Normalize( ); } else { m_vecDesired = (m_posDesired - pev->origin).Normalize( ); } } else { m_vecDesired = m_vecGoal; } Flight( ); // ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->time, m_flLastSeen, m_flPrevSeen ); if ((m_flLastSeen + 1 > gpGlobals->time) && (m_flPrevSeen + 2 < gpGlobals->time)) { if (FireGun( )) { // slow down if we're fireing if (m_flGoalSpeed > 400) m_flGoalSpeed = 400; } // don't fire rockets and gun on easy mode if (g_iSkillLevel == SKILL_EASY) m_flNextRocket = gpGlobals->time + 10.0; } UTIL_MakeAimVectors( pev->angles ); Vector vecEst = (gpGlobals->v_forward * 800 + pev->velocity).Normalize( ); // ALERT( at_console, "%d %d %d %4.2f\n", pev->angles.x < 0, DotProduct( pev->velocity, gpGlobals->v_forward ) > -100, m_flNextRocket < gpGlobals->time, DotProduct( m_vecTarget, vecEst ) ); if ((m_iRockets % 2) == 1) { FireRocket( ); m_flNextRocket = gpGlobals->time + 0.5; if (m_iRockets <= 0) { m_flNextRocket = gpGlobals->time + 10; m_iRockets = 10; } } else if (pev->angles.x < 0 && DotProduct( pev->velocity, gpGlobals->v_forward ) > -100 && m_flNextRocket < gpGlobals->time) { if (m_flLastSeen + 60 > gpGlobals->time) { if (m_hEnemy != NULL) { // make sure it's a good shot if (DotProduct( m_vecTarget, vecEst) > .965) { TraceResult tr; UTIL_TraceLine( pev->origin, pev->origin + vecEst * 4096, ignore_monsters, edict(), &tr ); if ((tr.vecEndPos - m_posTarget).Length() < 512) FireRocket( ); } } else { TraceResult tr; UTIL_TraceLine( pev->origin, pev->origin + vecEst * 4096, dont_ignore_monsters, edict(), &tr ); // just fire when close if ((tr.vecEndPos - m_posTarget).Length() < 512) FireRocket( ); } } } }
//========================================================= // SonicAttack //========================================================= void CNPC_Houndeye::SonicAttack ( void ) { EmitSound( "NPC_Houndeye.SonicAttack" ); if (m_pEnergyWave) { UTIL_Remove(m_pEnergyWave); } Vector vFacingDir = EyeDirection3D( ); m_pEnergyWave = (CEnergyWave*)Create( "energy_wave", EyePosition(), GetLocalAngles() ); m_flEndEnergyWaveTime = gpGlobals->curtime + 1; //<<TEMP>> magic m_pEnergyWave->SetAbsVelocity( 100*vFacingDir ); CBaseEntity *pEntity = NULL; // iterate on all entities in the vicinity. for ( CEntitySphereQuery sphere( GetAbsOrigin(), HOUNDEYE_MAX_ATTACK_RADIUS ); pEntity = sphere.GetCurrentEntity(); sphere.NextEntity() ) { if (pEntity->Classify() == CLASS_HOUNDEYE) { continue; } if (pEntity->GetFlags() & FL_NOTARGET) { continue; } IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject(); if ( pEntity->m_takedamage != DAMAGE_NO || pPhysicsObject) { // -------------------------- // Adjust damage by distance // -------------------------- float flDist = (pEntity->WorldSpaceCenter() - GetAbsOrigin()).Length(); float flDamageAdjuster = 1-( flDist / HOUNDEYE_MAX_ATTACK_RADIUS ); // -------------------------- // Adjust damage by direction // -------------------------- Vector forward; AngleVectors( GetAbsAngles(), &forward ); Vector vEntDir = (pEntity->GetAbsOrigin() - GetAbsOrigin()); VectorNormalize(vEntDir); float flDotPr = DotProduct(forward,vEntDir); flDamageAdjuster *= flDotPr; if (flDamageAdjuster < 0) { continue; } // -------------------------- // Adjust damage by visibility // -------------------------- if ( !FVisible( pEntity ) ) { if ( pEntity->IsPlayer() ) { // if this entity is a client, and is not in full view, inflict half damage. We do this so that players still // take the residual damage if they don't totally leave the houndeye's effective radius. We restrict it to clients // so that monsters in other parts of the level don't take the damage and get pissed. flDamageAdjuster *= 0.5; } else if ( !FClassnameIs( pEntity, "func_breakable" ) && !FClassnameIs( pEntity, "func_pushable" ) ) { // do not hurt nonclients through walls, but allow damage to be done to breakables continue; } } // ------------------------------ // Apply the damage // ------------------------------ if (pEntity->m_takedamage != DAMAGE_NO) { CTakeDamageInfo info( this, this, flDamageAdjuster * sk_Houndeye_dmg_blast.GetFloat(), DMG_SONIC | DMG_ALWAYSGIB ); CalculateExplosiveDamageForce( &info, (pEntity->GetAbsOrigin() - GetAbsOrigin()), pEntity->GetAbsOrigin() ); pEntity->TakeDamage( info ); // Throw the player if ( pEntity->IsPlayer() ) { Vector forward; AngleVectors( GetLocalAngles(), &forward ); Vector vecVelocity = pEntity->GetAbsVelocity(); vecVelocity += forward * 250 * flDamageAdjuster; vecVelocity.z = 300 * flDamageAdjuster; pEntity->SetAbsVelocity( vecVelocity ); pEntity->ViewPunch( QAngle(random->RandomInt(-20,20), 0, random->RandomInt(-20,20)) ); } } // ------------------------------ // Apply physics foces // ------------------------------ IPhysicsObject *pPhysicsObject = pEntity->VPhysicsGetObject(); if (pPhysicsObject) { float flForce = flDamageAdjuster * 8000; pPhysicsObject->ApplyForceCenter( (vEntDir+Vector(0,0,0.2)) * flForce ); pPhysicsObject->ApplyTorqueCenter( vEntDir * flForce ); } } } }
//========================================================= // SearchThink // This search function will sit with the turret deployed and look for a new target. // After a set amount of time, the barrel will spin down. After m_flMaxWait, the turret will // retact. //========================================================= void CNPC_BaseTurret::SearchThink(void) { // ensure rethink SetTurretAnim(TURRET_ANIM_SPIN); StudioFrameAdvance( ); SetNextThink( gpGlobals->curtime + 0.1 ); if (m_flSpinUpTime == 0 && m_flMaxSpin) m_flSpinUpTime = gpGlobals->curtime + m_flMaxSpin; Ping( ); CBaseEntity *pEnemy = GetEnemy(); // If we have a target and we're still healthy if (pEnemy != NULL) { if (!pEnemy->IsAlive() ) pEnemy = NULL;// Dead enemy forces a search for new one } // Acquire Target if (pEnemy == NULL) { GetSenses()->Look(TURRET_RANGE); pEnemy = BestEnemy(); if ( pEnemy && !FVisible( pEnemy ) ) pEnemy = NULL; } // If we've found a target, spin up the barrel and start to attack if (pEnemy != NULL) { m_flLastSight = 0; m_flSpinUpTime = 0; SetThink(&CNPC_BaseTurret::ActiveThink); } else { // Are we out of time, do we need to retract? if (gpGlobals->curtime > m_flLastSight) { //Before we retrace, make sure that we are spun down. m_flLastSight = 0; m_flSpinUpTime = 0; SetThink(&CNPC_BaseTurret::Retire); } // should we stop the spin? else if ((m_flSpinUpTime) && (gpGlobals->curtime > m_flSpinUpTime)) { SpinDownCall(); } // generic hunt for new victims m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_fTurnRate); if (m_vecGoalAngles.y >= 360) m_vecGoalAngles.y -= 360; MoveTurret(); } SetEnemy( pEnemy ); }
//========================================================= // Hornet is flying, gently tracking target //========================================================= void CHornet :: TrackTarget ( void ) { Vector vecFlightDir; Vector vecDirToEnemy; float flDelta; StudioFrameAdvance( ); if (gpGlobals->time > m_flStopAttack) { SetTouch( NULL ); SetThink( SUB_Remove ); pev->nextthink = gpGlobals->time + 0.1; return; } // UNDONE: The player pointer should come back after returning from another level if ( m_hEnemy == NULL ) {// enemy is dead. Look( 512 ); m_hEnemy = BestVisibleEnemy( ); } if ( m_hEnemy != NULL && FVisible( m_hEnemy )) { m_vecEnemyLKP = m_hEnemy->BodyTarget( GetAbsOrigin() ); } else { m_vecEnemyLKP = m_vecEnemyLKP + GetAbsVelocity() * m_flFlySpeed * 0.1; } vecDirToEnemy = ( m_vecEnemyLKP - GetAbsOrigin() ).Normalize(); if (GetAbsVelocity().Length() < 0.1) vecFlightDir = vecDirToEnemy; else vecFlightDir = GetAbsVelocity().Normalize(); // measure how far the turn is, the wider the turn, the slow we'll go this time. flDelta = DotProduct ( vecFlightDir, vecDirToEnemy ); if ( flDelta < 0.5 ) {// hafta turn wide again. play sound switch (RANDOM_LONG(0,2)) { case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM); break; } } if ( flDelta <= 0 && m_iHornetType == HORNET_TYPE_RED ) {// no flying backwards, but we don't want to invert this, cause we'd go fast when we have to turn REAL far. flDelta = 0.25; } SetAbsVelocity(( vecFlightDir + vecDirToEnemy).Normalize() ); if( pev->owner && ( pev->owner->v.flags & FL_MONSTER )) { // random pattern only applies to hornets fired by monsters, not players. Vector vecVelocity = GetAbsVelocity(); vecVelocity.x += RANDOM_FLOAT ( -0.10, 0.10 );// scramble the flight dir a bit. vecVelocity.y += RANDOM_FLOAT ( -0.10, 0.10 ); vecVelocity.z += RANDOM_FLOAT ( -0.10, 0.10 ); SetAbsVelocity( vecVelocity ); } switch ( m_iHornetType ) { case HORNET_TYPE_RED: SetAbsVelocity( GetAbsVelocity() * ( m_flFlySpeed * flDelta ));// scale the dir by the ( speed * width of turn ) pev->nextthink = gpGlobals->time + RANDOM_FLOAT( 0.1, 0.3 ); break; case HORNET_TYPE_ORANGE: SetAbsVelocity( GetAbsVelocity() * m_flFlySpeed ); // do not have to slow down to turn. pev->nextthink = gpGlobals->time + 0.1;// fixed think time break; } SetAbsAngles( UTIL_VecToAngles( GetAbsVelocity())); pev->solid = SOLID_BBOX; // if hornet is close to the enemy, jet in a straight line for a half second. // (only in the single player game) if ( m_hEnemy != NULL && !g_pGameRules->IsMultiplayer() ) { if ( flDelta >= 0.4 && ( GetAbsOrigin() - m_vecEnemyLKP ).Length() <= 300 ) { Vector vecOrigin = GetAbsOrigin(); MESSAGE_BEGIN( MSG_PVS, SVC_TEMPENTITY, vecOrigin ); WRITE_BYTE( TE_SPRITE ); WRITE_COORD( vecOrigin.x ); // pos WRITE_COORD( vecOrigin.y ); WRITE_COORD( vecOrigin.z ); WRITE_SHORT( iHornetPuff ); // model // WRITE_BYTE( 0 ); // life * 10 WRITE_BYTE( 2 ); // size * 10 WRITE_BYTE( 128 ); // brightness MESSAGE_END(); switch( RANDOM_LONG( 0, 2 )) { case 0: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz1.wav", HORNET_BUZZ_VOLUME, ATTN_NORM ); break; case 1: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz2.wav", HORNET_BUZZ_VOLUME, ATTN_NORM ); break; case 2: EMIT_SOUND( ENT(pev), CHAN_VOICE, "hornet/ag_buzz3.wav", HORNET_BUZZ_VOLUME, ATTN_NORM ); break; } SetAbsVelocity( GetAbsVelocity() * 2 ); pev->nextthink = gpGlobals->time + 1.0; // don't attack again m_flStopAttack = gpGlobals->time; } } }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CBaseHelicopter::Hunt( void ) { UpdateTrackNavigation( ); if( HasCondition( COND_ENEMY_DEAD ) ) { SetEnemy( NULL ); } // Look for my best enemy. If I change enemies, // be sure and change my prevseen/lastseen timers. if( m_lifeState == LIFE_ALIVE ) { GetSenses()->Look( 4092 ); GetEnemies()->RefreshMemories(); ChooseEnemy(); if( HasEnemy() ) { GatherEnemyConditions( GetEnemy() ); if (FVisible( GetEnemy() )) { if (m_flLastSeen < gpGlobals->curtime - 2) { m_flPrevSeen = gpGlobals->curtime; } m_flLastSeen = gpGlobals->curtime; m_vecTargetPosition = GetEnemy()->WorldSpaceCenter(); } } else { // look at where we're going instead m_vecTargetPosition = GetDesiredPosition(); } } else { // If we're dead or dying, forget our enemy and don't look for new ones(sjb) SetEnemy( NULL ); } if ( 1 ) { Vector targetDir = m_vecTargetPosition - GetAbsOrigin(); Vector desiredDir = GetDesiredPosition() - GetAbsOrigin(); VectorNormalize( targetDir ); VectorNormalize( desiredDir ); if ( !IsCrashing() && m_flLastSeen + 5 > gpGlobals->curtime ) //&& DotProduct( targetDir, desiredDir) > 0.25) { // If we've seen the target recently, face the target. //Msg( "Facing Target \n" ); m_vecDesiredFaceDir = targetDir; } else { // Face our desired position. // Msg( "Facing Position\n" ); m_vecDesiredFaceDir = desiredDir; } } else { // Face the way the path corner tells us to. //Msg( "Facing my path corner\n" ); m_vecDesiredFaceDir = GetGoalOrientation(); } Flight(); UpdatePlayerDopplerShift( ); // ALERT( at_console, "%.0f %.0f %.0f\n", gpGlobals->curtime, m_flLastSeen, m_flPrevSeen ); if (m_fHelicopterFlags & BITS_HELICOPTER_GUN_ON) { //if ( (m_flLastSeen + 1 > gpGlobals->curtime) && (m_flPrevSeen + 2 < gpGlobals->curtime) ) { if (FireGun( )) { // slow down if we're firing if (m_flGoalSpeed > m_flMaxSpeedFiring ) { m_flGoalSpeed = m_flMaxSpeedFiring; } } } } if (m_fHelicopterFlags & BITS_HELICOPTER_MISSILE_ON) { AimRocketGun(); } // Finally, forget dead enemies. if( GetEnemy() != NULL && (!GetEnemy()->IsAlive() || GetEnemy()->GetFlags() & FL_NOTARGET) ) { SetEnemy( NULL ); } }