//----------------------------------------------------------------------------- // Purpose: Tell the driver to start firing at targets //----------------------------------------------------------------------------- void CNPC_VehicleDriver::InputStartFiring( inputdata_t &inputdata ) { CLEARBITS( m_spawnflags, SF_VEHICLEDRIVER_INACTIVE ); SetCondition( COND_PROVOKED ); float flMinRange, flMaxRange; // If the vehicle has a weapon, set our capability if ( m_pVehicleInterface->NPC_HasPrimaryWeapon() ) { CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 ); m_pVehicleInterface->Weapon_PrimaryRanges( &flMinRange, &flMaxRange ); // Ensure the look distances is long enough if ( m_flDistTooFar < flMaxRange || GetSenses()->GetDistLook() < flMaxRange ) { m_flDistTooFar = flMaxRange; SetDistLook( flMaxRange ); } } if ( m_pVehicleInterface->NPC_HasSecondaryWeapon() ) { CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK2 ); m_pVehicleInterface->Weapon_SecondaryRanges( &flMinRange, &flMaxRange ); // Ensure the look distances is long enough if ( m_flDistTooFar < flMaxRange || GetSenses()->GetDistLook() < flMaxRange ) { m_flDistTooFar = flMaxRange; SetDistLook( flMaxRange ); } } }
//----------------------------------------------------------------------------- // Purpose: The turret doesn't run base AI properly, which is a bad decision. // As a result, it has to manually find enemies. //----------------------------------------------------------------------------- void CNPC_Portal_FloorTurret::HackFindEnemy( void ) { // We have to refresh our memories before finding enemies, so // dead enemies are cleared out before new ones are added. GetEnemies()->RefreshMemories(); GetSenses()->Look( PORTAL_FLOOR_TURRET_RANGE ); SetEnemy( BestEnemy() ); if ( GetEnemy() == NULL ) { // Look through the list of sensed objects for possible targets AISightIter_t iter; CBaseEntity *pObject; CBaseEntity *pNearest = NULL; float flClosestDistSqr = PORTAL_FLOOR_TURRET_RANGE * PORTAL_FLOOR_TURRET_RANGE; for ( pObject = GetSenses()->GetFirstSeenEntity( &iter, SEEN_MISC ); pObject; pObject = GetSenses()->GetNextSeenEntity( &iter ) ) { Vector vVelocity; pObject->GetVelocity( &vVelocity ); // Ignore objects going too slowly if ( vVelocity.LengthSqr() < m_fMovingTargetThreashold ) continue; float flDistSqr = pObject->WorldSpaceCenter().DistToSqr( GetAbsOrigin() ); if ( flDistSqr < flClosestDistSqr ) { flClosestDistSqr = flDistSqr; pNearest = pObject; } } if ( pNearest ) { SetEnemy( pNearest ); m_fMovingTargetThreashold += gpGlobals->curtime * 15.0f; if ( m_fMovingTargetThreashold > 800.0f ) { m_fMovingTargetThreashold = 800.0f; } } } else { m_fMovingTargetThreashold = 20.0f; } }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CNPC_CombineDropship::Hunt( void ) { // If we have a pickup target, fly to it if ( m_hPickupTarget ) { UpdatePickupNavigation(); } else if ( m_iLandState == LANDING_NO ) { UpdateTrackNavigation(); } // look for enemy GetSenses()->Look( 4092 ); ChooseEnemy(); // don't face player ever, only face nav points Vector desiredDir = GetDesiredPosition() - GetAbsOrigin(); VectorNormalize( desiredDir ); // Face our desired position. m_vecDesiredFaceDir = desiredDir; Flight(); UpdatePlayerDopplerShift( ); }
//----------------------------------------------------------------------------- // Purpose: Watch for a target to wander into our view //----------------------------------------------------------------------------- void CNPC_CeilingTurret::AutoSearchThink( void ) { //Allow descended classes a chance to do something before the think function if ( PreThink( TURRET_AUTO_SEARCHING ) ) return; //Spread out our thinking SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2f, 0.4f ) ); //If the enemy is dead, find a new one if ( ( GetEnemy() != NULL ) && ( GetEnemy()->IsAlive() == false ) ) { SetEnemy( NULL ); } //Acquire Target if ( GetEnemy() == NULL ) { GetSenses()->Look( CEILING_TURRET_RANGE ); SetEnemy( BestEnemy() ); } //Deploy if we've got an active target if ( GetEnemy() != NULL ) { SetThink( &CNPC_CeilingTurret::Deploy ); EmitSound( "NPC_CeilingTurret.Alert" ); } }
//------------------------------------------------------------------------------ // Purpose : //------------------------------------------------------------------------------ void CNPC_Launcher::LauncherThink( void ) { if (gpGlobals->curtime > m_flNextAttack) { // If enemy was set, fire at enemy if (GetEnemy()) { LaunchGrenade(GetEnemy()); m_OnLaunch.FireOutput(GetEnemy(), this); m_flNextAttack = gpGlobals->curtime + m_nLaunchDelay; } // Otherwise look for enemy to fire at else { GetSenses()->Look((int)m_flMaxAttackDist); CBaseEntity* pBestEnemy = BestEnemy(); if (pBestEnemy) { LaunchGrenade(pBestEnemy); m_OnLaunch.FireOutput(pBestEnemy, this); m_flNextAttack = gpGlobals->curtime + m_nLaunchDelay; } } } SetNextThink( gpGlobals->curtime + 0.1f ); }
//----------------------------------------------------------------------------- // Purpose: Called when we have no scripted target. Looks for new enemies to track. //----------------------------------------------------------------------------- CBaseEntity *CNPC_CombineCamera::MaintainEnemy() { if (HasSpawnFlags(SF_COMBINE_CAMERA_IGNOREENEMIES)) return NULL; GetSenses()->Look(m_nOuterRadius); CBaseEntity *pEnemy = BestEnemy(); if (pEnemy) { // See if our best enemy is too far away to care about. Vector vecDelta = pEnemy->GetAbsOrigin() - GetAbsOrigin(); float flDist = vecDelta.Length(); if (flDist < m_nOuterRadius) { if (FInViewCone(pEnemy)) { // dvs: HACK: for checking multiple view cones float flSaveFieldOfView = m_flFieldOfView; m_flFieldOfView = CAMERA_FOV_NARROW; // Is the target visible? bool bVisible = FVisible(pEnemy); m_flFieldOfView = flSaveFieldOfView; if ( bVisible ) return pEnemy; } } } return NULL; }
//------------------------------------------------------------------------------ // Purpose : // Input : // Output : //------------------------------------------------------------------------------ void CNPC_MissileDefense::RunAI( void ) { // If my enemy is dead clear the memory and reset m_hEnemy if (GetEnemy() != NULL && !GetEnemy()->IsAlive()) { ClearEnemyMemory(); SetEnemy( NULL ); } if (GetEnemy() == NULL ) { GetSenses()->Look( 4092 ); SetEnemy( BestEnemy( ) ); if (GetEnemy() != NULL) { m_iAmmoLoaded = MD_FULLAMMO; m_flReloadedTime = gpGlobals->curtime; } } if( m_iAmmoLoaded < 1 && gpGlobals->curtime > m_flReloadedTime ) { m_iAmmoLoaded = MD_FULLAMMO; } AimGun(); FireCannons(); SetNextThink( gpGlobals->curtime + 0.05 ); }
// // This think function will deploy the turret when something comes into range. This is for // automatically activated turrets. // void CBaseTurret::AutoSearchThink(void) { // ensure rethink StudioFrameAdvance( ); SetNextThink( gpGlobals->curtime + random->RandomFloat( 0.2, 0.3 ) ); // If we have a target and we're still healthy if (GetEnemy() != NULL) { if (!GetEnemy()->IsAlive() ) SetEnemy( NULL );// Dead enemy forces a search for new one } // Acquire Target if (GetEnemy() == NULL) { GetSenses()->Look( TURRET_RANGE ); SetEnemy( BestEnemy() ); } if (GetEnemy() != NULL) { SetThink(Deploy); EmitSound( "NPC_Turret.Alert" ); } }
//========================================================= // OnListened - monsters dig through the active sound list for // any sounds that may interest them. (smells, too!) //========================================================= void CNPC_Bullsquid::OnListened( void ) { AISoundIter_t iter; CSound *pCurrentSound; static int conditionsToClear[] = { COND_SQUID_SMELL_FOOD, }; ClearConditions( conditionsToClear, ARRAYSIZE( conditionsToClear ) ); pCurrentSound = GetSenses()->GetFirstHeardSound( &iter ); while ( pCurrentSound ) { // the npc cares about this sound, and it's close enough to hear. int condition = COND_NONE; if ( !pCurrentSound->FIsSound() ) { // if not a sound, must be a smell - determine if it's just a scent, or if it's a food scent if ( pCurrentSound->IsSoundType( SOUND_MEAT | SOUND_CARCASS ) ) { // the detected scent is a food item condition = COND_SQUID_SMELL_FOOD; } } if ( condition != COND_NONE ) SetCondition( condition ); pCurrentSound = GetSenses()->GetNextHeardSound( &iter ); } BaseClass::OnListened(); }
// // 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 CBaseTurret::SearchThink(void) { // ensure rethink SetActivity( (Activity)ACT_TURRET_OPEN_IDLE ); StudioFrameAdvance( ); SetNextThink( gpGlobals->curtime + 0.1f ); Ping( ); // If we have a target and we're still healthy if (GetEnemy() != NULL) { if (!GetEnemy()->IsAlive() ) SetEnemy( NULL );// Dead enemy forces a search for new one } // Acquire Target if (GetEnemy() == NULL) { GetSenses()->Look(TURRET_RANGE); SetEnemy( BestEnemy() ); } // If we've found a target, spin up the barrel and start to attack if (GetEnemy() != NULL) { m_flLastSight = 0; SetThink(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; SetThink(Retire); } // generic hunt for new victims m_vecGoalAngles.y = (m_vecGoalAngles.y + 0.1 * m_iBaseTurnRate); if (m_vecGoalAngles.y >= 360) m_vecGoalAngles.y -= 360; MoveTurret(); } }
void CNPC_Cremator::Spawn() { Precache(); SetModel( "models/cremator_test2.mdl" ); // a model with a bit of a tesselation (tesselated head and collar) SetHullType(HULL_HUMAN); // отключено, т.к. это новый введенный тип хулла, и вряд ли есть смысл возиться с его введением. //SetHullType( HULL_MEDIUM_TALL ); // данный стандартный тип подходит для большинства ситуаций, однако крематор не сможет проходить в двери, если они ненамного выше его /*that hull type is made special for a cremator since HULL_MEDIUM is not high enough but HULL_LARGE is too big and a cremator cannot fit in Borealis maps with it. The hull type is enumarated in ai_hull.h under the HULL_MEDIUM_TALL and then MUST be enumeraten in Hull_Bits_t, again, UNDER HULL_MEDIUM_TALL with the adress of 0x00000400, so that the definitions in ai_hull.cpp will then find the corresponding hull type.*/ SetHullSizeNormal(); SetBodygroup( 1, 0 ); // the gun SetBodygroup( 2, 0 ); // the head SetSolid( SOLID_BBOX ); AddSolidFlags( FSOLID_NOT_STANDABLE ); SetMoveType( MOVETYPE_STEP ); m_bloodColor = DONT_BLEED;//BLOOD_COLOR_YELLOW; m_iHealth = sk_cremator_health.GetFloat(); m_flFieldOfView = VIEW_FIELD_WIDE;// indicates the width of this NPC's forward view cone ( as a dotproduct result ) m_NPCState = NPC_STATE_NONE; m_nSkin = CREMATOR_SKIN_ALERT; // original yellow-eyes skin // Если надо спаунить крематора с иным цветом глаз, подставь значение из npc_cremator_h. m_iAmmo = m_iMaxAmmo = 54; NPCInit(); m_flDistTooFar = 6000.0; GetSenses()->SetDistLook( 6000.0 -1 ); m_flNextIdleSoundTime = gpGlobals->curtime; // + random->RandomFloat( 14.0f, 28.0f ); m_flNextRangeAttack2Time = gpGlobals->curtime; // + random->RandomFloat( 10.0f, 20.0f ); m_MuzzleAttachment = LookupAttachment( "muzzle" ); m_HeadAttachment = LookupAttachment( "headattachment" ); CapabilitiesAdd( bits_CAP_MOVE_GROUND | bits_CAP_TURN_HEAD ); CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1 ); // flamethrower CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK2 ); CapabilitiesAdd( bits_CAP_MOVE_SHOOT ); // TODO: Melee? }
//------------------------------------------------------------------------------ // 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 : Override base class to check range and visibility // Input : // Output : //------------------------------------------------------------------------------ bool CNPC_EnemyFinder::IsValidEnemy( CBaseEntity *pTarget ) { float flTargetDist = GetAbsOrigin().DistTo( pTarget->GetAbsOrigin() ); if (flTargetDist < m_flMinSearchDist) return false; if ( m_flMaxSearchDist && flTargetDist > m_flMaxSearchDist) return false; if ( !FBitSet( m_spawnflags, SF_ENEMY_FINDER_CHECK_VIS) ) return true; if ( GetSenses()->DidSeeEntity( pTarget ) ) return true; // Trace from launch position to target position. // Use position above actual barral based on vertical launch speed Vector vStartPos = GetAbsOrigin(); Vector vEndPos = pTarget->EyePosition(); // Test our line of sight to the target trace_t tr; AI_TraceLOS( vStartPos, vEndPos, this, &tr ); // If the player is in a vehicle, see if we can see that instead if ( pTarget->IsPlayer() ) { CBasePlayer *pPlayer = assert_cast<CBasePlayer*>(pTarget); if ( tr.m_pEnt == pPlayer->GetVehicleEntity() ) return true; } // Line must be clear if ( tr.fraction == 1.0f || tr.m_pEnt == pTarget ) return true; // Otherwise we can't see anything return false; }
//========================================================= // 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 ); }
//----------------------------------------------------------------------------- // Purpose: // // //----------------------------------------------------------------------------- void CNPC_Stalker::Spawn( void ) { Precache( ); SetModel( "models/stalker.mdl" ); SetHullType(HULL_HUMAN); SetHullSizeNormal(); SetSolid( SOLID_BBOX ); AddSolidFlags( FSOLID_NOT_STANDABLE ); SetMoveType( MOVETYPE_STEP ); m_bloodColor = DONT_BLEED; m_iHealth = sk_stalker_health.GetFloat(); m_flFieldOfView = 0.1;// indicates the width of this NPC's forward view cone ( as a dotproduct result ) m_NPCState = NPC_STATE_NONE; CapabilitiesAdd( bits_CAP_SQUAD | bits_CAP_MOVE_GROUND ); CapabilitiesAdd( bits_CAP_INNATE_RANGE_ATTACK1); m_flNextAttackSoundTime = 0; m_flNextBreatheSoundTime = gpGlobals->curtime + random->RandomFloat( 0.0, 10.0 ); m_flNextScrambleSoundTime = 0; m_nextSmokeTime = 0; m_bPlayingHitWall = false; m_bPlayingHitFlesh = false; m_fBeamEndTime = 0; m_fBeamRechargeTime = 0; m_fNextDamageTime = 0; NPCInit(); m_flDistTooFar = MAX_STALKER_FIRE_RANGE; m_iPlayerAggression = 0; GetSenses()->SetDistLook(MAX_STALKER_FIRE_RANGE - 1); }
//----------------------------------------------------------------------------- // Purpose: This used to have something to do with bees flying, but // now it only initializes moving furniture in scripted sequences //----------------------------------------------------------------------------- void CNPC_Furniture::Spawn( ) { Precache(); SetModel( STRING(GetModelName()) ); SetMoveType( MOVETYPE_STEP ); SetSolid( SOLID_BBOX ); // Our collision, if needed, will be done through bone followers AddSolidFlags( FSOLID_NOT_SOLID ); SetBloodColor( DONT_BLEED ); m_iHealth = TOO_MUCH_HEALTH_TO_DIE; //wow m_takedamage = DAMAGE_AIM; SetSequence( 0 ); SetCycle( 0 ); SetNavType( NAV_FLY ); AddFlag( FL_FLY ); CapabilitiesAdd( bits_CAP_MOVE_FLY | bits_CAP_TURN_HEAD | bits_CAP_ANIMATEDFACE ); AddEFlags( EFL_NO_MEGAPHYSCANNON_RAGDOLL ); // pev->nextthink += 1.0; // SetThink (WalkMonsterDelay); ResetSequenceInfo( ); SetCycle( 0 ); NPCInit(); // Furniture needs to block LOS SetBlocksLOS( true ); // Furniture just wastes CPU doing sensing code, since all they do is idle and play scripts GetSenses()->AddSensingFlags( SENSING_FLAGS_DONT_LOOK | SENSING_FLAGS_DONT_LISTEN ); }
//========================================================= // 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 ); }
//----------------------------------------------------------------------------- //----------------------------------------------------------------------------- void CNPC_Monk::GatherConditions() { BaseClass::GatherConditions(); // Build my zombie danger index! m_iNumZombies = 0; m_iDangerousZombies = 0; AISightIter_t iter; CBaseEntity *pSightEnt; pSightEnt = GetSenses()->GetFirstSeenEntity( &iter ); while( pSightEnt ) { if( pSightEnt->Classify() == CLASS_ZOMBIE && pSightEnt->IsAlive() ) { // Is this zombie coming for me? CAI_BaseNPC *pZombie = dynamic_cast<CAI_BaseNPC*>(pSightEnt); if( pZombie && pZombie->GetEnemy() == this ) { m_iNumZombies++; // if this zombie is close enough to attack, add him to the zombie danger! float flDist; flDist = (pZombie->GetAbsOrigin() - GetAbsOrigin()).Length2DSqr(); if( flDist <= 128.0f * 128.0f ) { m_iDangerousZombies++; } } } pSightEnt = GetSenses()->GetNextSeenEntity( &iter ); } if( m_iDangerousZombies >= 3 || (GetEnemy() && GetHealth() < 25) ) { // I see many zombies, or I'm quite injured. SpeakIfAllowed( TLK_HELP_ME ); } // NOTE!!!!!! This code assumes grigori is using annabelle! ClearCondition(COND_LOW_PRIMARY_AMMO); if ( GetActiveWeapon() ) { if ( GetActiveWeapon()->UsesPrimaryAmmo() ) { if (!GetActiveWeapon()->HasPrimaryAmmo() ) { SetCondition(COND_NO_PRIMARY_AMMO); } else if ( m_NPCState != NPC_STATE_COMBAT && GetActiveWeapon()->UsesClipsForAmmo1() && GetActiveWeapon()->Clip1() < 2 ) { // Don't send a low ammo message unless we're not in combat. SetCondition(COND_LOW_PRIMARY_AMMO); } } } }
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 ); }
void CNPC_Tentacle::Cycle( void ) { //NDebugOverlay::Cross3D( EarPosition(), 32, 255, 0, 0, false, 0.1 ); // ALERT( at_console, "%s %.2f %d %d\n", STRING( pev->targetname ), pev->origin.z, m_MonsterState, m_IdealMonsterState ); SetNextThink( gpGlobals->curtime + 0.1 ); // ALERT( at_console, "%s %d %d %d %f %f\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim, m_iDir, pev->framerate, pev->health ); if ( m_NPCState == NPC_STATE_SCRIPT || GetIdealState() == NPC_STATE_SCRIPT) { SetAbsAngles( QAngle( GetAbsAngles().x, m_flInitialYaw, GetAbsAngles().z ) ); GetMotor()->SetIdealYaw( m_flInitialYaw ); RemoveIgnoredConditions(); NPCThink( ); m_iGoalAnim = TENTACLE_ANIM_Pit_Idle; return; } StudioFrameAdvance(); DispatchAnimEvents( this ); GetMotor()->UpdateYaw( MaxYawSpeed() ); CSound *pSound = NULL; GetSenses()->Listen(); m_BoneFollowerManager.UpdateBoneFollowers(this); // Listen will set this if there's something in my sound list if ( HeardAnything() ) pSound = GetSenses()->GetClosestSound( false, (SOUND_DANGER|SOUND_COMBAT|SOUND_WORLD|SOUND_PLAYER) ); else pSound = NULL; if ( pSound ) { //NDebugOverlay::Line( EarPosition(), pSound->GetSoundOrigin(), 0, 255, 0, false, 0.2 ); Vector vecDir; if ( gpGlobals->curtime - m_flPrevSoundTime < 0.5 ) { float dt = gpGlobals->curtime - m_flPrevSoundTime; vecDir = pSound->GetSoundOrigin() + (pSound->GetSoundOrigin() - m_vecPrevSound) / dt - GetAbsOrigin(); } else { vecDir = pSound->GetSoundOrigin() - GetAbsOrigin(); } m_flPrevSoundTime = gpGlobals->curtime; m_vecPrevSound = pSound->GetSoundOrigin(); m_flSoundYaw = VecToYaw ( vecDir ) - m_flInitialYaw; m_iSoundLevel = Level( vecDir.z ); if (m_flSoundYaw < -180) m_flSoundYaw += 360; if (m_flSoundYaw > 180) m_flSoundYaw -= 360; // ALERT( at_console, "sound %d %.0f\n", m_iSoundLevel, m_flSoundYaw ); if (m_flSoundTime < gpGlobals->curtime) { // play "I hear new something" sound UTIL_EmitAmbientSound( GetSoundSourceIndex(), GetAbsOrigin() + Vector( 0, 0, MyHeight()), "Tentacle.Alert", 1.0, SNDLVL_GUNFIRE, 0, 100); } m_flSoundTime = gpGlobals->curtime + random->RandomFloat( 5.0, 10.0 ); } // clip ideal_yaw float dy = m_flSoundYaw; switch( GetSequence() ) { case TENTACLE_ANIM_Floor_Rear: case TENTACLE_ANIM_Floor_Rear_Idle: case TENTACLE_ANIM_Lev1_Rear: case TENTACLE_ANIM_Lev1_Rear_Idle: case TENTACLE_ANIM_Lev2_Rear: case TENTACLE_ANIM_Lev2_Rear_Idle: case TENTACLE_ANIM_Lev3_Rear: case TENTACLE_ANIM_Lev3_Rear_Idle: if (dy < 0 && dy > -m_flMaxYaw) dy = -m_flMaxYaw; if (dy > 0 && dy < m_flMaxYaw) dy = m_flMaxYaw; break; default: if (dy < -m_flMaxYaw) dy = -m_flMaxYaw; if (dy > m_flMaxYaw) dy = m_flMaxYaw; } GetMotor()->SetIdealYaw( m_flInitialYaw + dy ); if ( IsSequenceFinished() ) { // ALERT( at_console, "%s done %d %d\n", STRING( pev->targetname ), pev->sequence, m_iGoalAnim ); if ( m_iHealth <= 1) { m_iGoalAnim = TENTACLE_ANIM_Pit_Idle; if ( GetSequence() == TENTACLE_ANIM_Pit_Idle) { m_iHealth = 75; } } else if ( m_flSoundTime > gpGlobals->curtime ) { if (m_flSoundYaw >= -(m_flMaxYaw + 30) && m_flSoundYaw <= (m_flMaxYaw + 30)) { // strike switch ( m_iSoundLevel ) { case 0: m_iGoalAnim = SelectWeightedSequence ( ACT_1030 ); break; case 1: m_iGoalAnim = SelectWeightedSequence ( ACT_1031 ); break; case 2: m_iGoalAnim = SelectWeightedSequence ( ACT_1032 ); break; case 3: m_iGoalAnim = SelectWeightedSequence ( ACT_1033 ); break; } } else if (m_flSoundYaw >= -m_flMaxYaw * 2 && m_flSoundYaw <= m_flMaxYaw * 2) { // tap switch ( m_iSoundLevel ) { case 0: m_iGoalAnim = SelectWeightedSequence ( ACT_1020 ); break; case 1: m_iGoalAnim = SelectWeightedSequence ( ACT_1021 ); break; case 2: m_iGoalAnim = SelectWeightedSequence ( ACT_1022 ); break; case 3: m_iGoalAnim = SelectWeightedSequence ( ACT_1023 ); break; } } else { // go into rear idle switch ( m_iSoundLevel ) { case 0: m_iGoalAnim = SelectWeightedSequence ( ACT_1040 ); break; case 1: m_iGoalAnim = SelectWeightedSequence ( ACT_1041 ); break; case 2: m_iGoalAnim = SelectWeightedSequence ( ACT_1042 ); break; case 3: m_iGoalAnim = SelectWeightedSequence ( ACT_1043 ); break; case 4: m_iGoalAnim = SelectWeightedSequence ( ACT_1044 ); break; } } } else if ( GetSequence() == TENTACLE_ANIM_Pit_Idle) { // stay in pit until hear noise m_iGoalAnim = TENTACLE_ANIM_Pit_Idle; } else if ( GetSequence() == m_iGoalAnim) { if ( MyLevel() >= 0 && gpGlobals->curtime < m_flSoundTime) { if ( random->RandomInt(0,9) < m_flSoundTime - gpGlobals->curtime ) { // continue stike switch ( m_iSoundLevel ) { case 0: m_iGoalAnim = SelectWeightedSequence ( ACT_1030 ); break; case 1: m_iGoalAnim = SelectWeightedSequence ( ACT_1031 ); break; case 2: m_iGoalAnim = SelectWeightedSequence ( ACT_1032 ); break; case 3: m_iGoalAnim = SelectWeightedSequence ( ACT_1033 ); break; } } else { // tap switch ( m_iSoundLevel ) { case 0: m_iGoalAnim = SelectWeightedSequence ( ACT_1020 ); break; case 1: m_iGoalAnim = SelectWeightedSequence ( ACT_1021 ); break; case 2: m_iGoalAnim = SelectWeightedSequence ( ACT_1022 ); break; case 3: m_iGoalAnim = SelectWeightedSequence ( ACT_1023 ); break; } } } else if ( MyLevel( ) < 0 ) { m_iGoalAnim = SelectWeightedSequence( ACT_1010 ); } else { if (m_flNextSong < gpGlobals->curtime) { // play "I hear new something" sound CPASAttenuationFilter filter( this ); EmitSound( filter, entindex(), "Tentacle.Sing" ); m_flNextSong = gpGlobals->curtime + random->RandomFloat( 10, 20 ); } if (random->RandomInt(0,15) == 0) { // idle on new level switch ( random->RandomInt( 0, 3 ) ) { case 0: m_iGoalAnim = SelectWeightedSequence ( ACT_1010 ); break; case 1: m_iGoalAnim = SelectWeightedSequence ( ACT_1011 ); break; case 2: m_iGoalAnim = SelectWeightedSequence ( ACT_1012 ); break; case 3: m_iGoalAnim = SelectWeightedSequence ( ACT_1013 ); break; } } else if ( random->RandomInt( 0, 3 ) == 0 ) { // tap switch ( MyLevel() ) { case 0: m_iGoalAnim = SelectWeightedSequence ( ACT_1020 ); break; case 1: m_iGoalAnim = SelectWeightedSequence ( ACT_1021 ); break; case 2: m_iGoalAnim = SelectWeightedSequence ( ACT_1022 ); break; case 3: m_iGoalAnim = SelectWeightedSequence ( ACT_1023 ); break; } } else { // idle switch ( MyLevel() ) { case 0: m_iGoalAnim = SelectWeightedSequence ( ACT_1010 ); break; case 1: m_iGoalAnim = SelectWeightedSequence ( ACT_1011 ); break; case 2: m_iGoalAnim = SelectWeightedSequence ( ACT_1012 ); break; case 3: m_iGoalAnim = SelectWeightedSequence ( ACT_1013 ); break; } } } if (m_flSoundYaw < 0) m_flSoundYaw += random->RandomFloat( 2, 8 ); else m_flSoundYaw -= random->RandomFloat( 2, 8 ); } SetSequence( FindTransitionSequence( GetSequence(), m_iGoalAnim, &m_iDir ) ); if (m_iDir > 0) { SetCycle( 0 ); } else { m_iDir = -1; // just to safe SetCycle( 1.0f ); } ResetSequenceInfo( ); m_flFramerateAdj = random->RandomFloat( -0.2, 0.2 ); m_flPlaybackRate = m_iDir * 1.0 + m_flFramerateAdj; switch( GetSequence() ) { case TENTACLE_ANIM_Floor_Tap: case TENTACLE_ANIM_Lev1_Tap: case TENTACLE_ANIM_Lev2_Tap: case TENTACLE_ANIM_Lev3_Tap: { Vector vecSrc, v_forward; AngleVectors( GetAbsAngles(), &v_forward ); trace_t tr1, tr2; vecSrc = GetAbsOrigin() + Vector( 0, 0, MyHeight() - 4); UTIL_TraceLine( vecSrc, vecSrc + v_forward * 512, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr1 ); vecSrc = GetAbsOrigin() + Vector( 0, 0, MyHeight() + 8); UTIL_TraceLine( vecSrc, vecSrc + v_forward * 512, MASK_NPCSOLID_BRUSHONLY, this, COLLISION_GROUP_NONE, &tr2 ); // ALERT( at_console, "%f %f\n", tr1.flFraction * 512, tr2.flFraction * 512 ); m_flTapRadius = SetPoseParameter( 0, random->RandomFloat( tr1.fraction * 512, tr2.fraction * 512 ) ); } break; default: m_flTapRadius = 336; // 400 - 64 break; } SetViewOffset( Vector( 0, 0, MyHeight() ) ); // ALERT( at_console, "seq %d\n", pev->sequence ); } if (m_flPrevSoundTime + 2.0 > gpGlobals->curtime) { // 1.5 normal speed if hears sounds m_flPlaybackRate = m_iDir * 1.5 + m_flFramerateAdj; } else if (m_flPrevSoundTime + 5.0 > gpGlobals->curtime) { // slowdown to normal m_flPlaybackRate = m_iDir + m_iDir * (5 - (gpGlobals->curtime - m_flPrevSoundTime)) / 2 + m_flFramerateAdj; } }
//----------------------------------------------------------------------------- // Purpose: Target doesn't exist or has eluded us, so search for one //----------------------------------------------------------------------------- void CNPC_CeilingTurret::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_CEILING_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 ) { GetSenses()->Look( CEILING_TURRET_RANGE ); CBaseEntity *pEnemy = BestEnemy(); if ( pEnemy ) { SetEnemy( pEnemy ); } } //If we've found a target, spin up the barrel and start to attack if ( GetEnemy() != NULL ) { //Give players a grace period if ( GetEnemy()->IsPlayer() ) { m_flShotTime = gpGlobals->curtime + 0.5f; } else { m_flShotTime = gpGlobals->curtime + 0.1f; } m_flLastSight = 0; SetThink( &CNPC_CeilingTurret::ActiveThink ); SetEyeState( TURRET_EYE_SEE_TARGET ); SpinUp(); EmitSound( "NPC_CeilingTurret.Active" ); 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_CeilingTurret::Retire ); return; } //Display that we're scanning m_vecGoalAngles.x = 15.0f; m_vecGoalAngles.y = GetAbsAngles().y + ( sin( gpGlobals->curtime * 2.0f ) * 45.0f ); //Turn and ping UpdateFacing(); Ping(); }
//========================================================= // 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; } } }
//------------------------------------------------------------------------------ // 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 ); } }