CSound *CAI_Senses::GetClosestSound( bool fScent, int validTypes, bool bUsePriority ) { float flBestDist = MAX_COORD_RANGE*MAX_COORD_RANGE;// so first nearby sound will become best so far. float flDist; int iBestPriority = SOUND_PRIORITY_VERY_LOW; AISoundIter_t iter; CSound *pResult = NULL; CSound *pCurrent = GetFirstHeardSound( &iter ); Vector earPosition = GetOuter()->EarPosition(); while ( pCurrent ) { if ( ( !fScent && pCurrent->FIsSound() ) || ( fScent && pCurrent->FIsScent() ) ) { if( pCurrent->IsSoundType( validTypes ) && !GetOuter()->ShouldIgnoreSound( pCurrent ) ) { if( !bUsePriority || GetOuter()->GetSoundPriority(pCurrent) >= iBestPriority ) { flDist = ( pCurrent->GetSoundOrigin() - earPosition ).LengthSqr(); if ( flDist < flBestDist ) { pResult = pCurrent; flBestDist = flDist; iBestPriority = GetOuter()->GetSoundPriority(pCurrent); } } } } pCurrent = GetNextHeardSound( &iter ); } return pResult; }
//----------------------------------------------------------------------------- // Purpose: Return the loudest sound of the specified type at "earposition" //----------------------------------------------------------------------------- CSound* CSoundEnt::GetLoudestSoundOfType( int iType, const Vector &vecEarPosition ) { CSound *pLoudestSound = NULL; int iThisSound; int iBestSound = SOUNDLIST_EMPTY; float flBestDist = MAX_COORD_RANGE*MAX_COORD_RANGE;// so first nearby sound will become best so far. float flDist; CSound *pSound; iThisSound = ActiveList(); while ( iThisSound != SOUNDLIST_EMPTY ) { pSound = SoundPointerForIndex( iThisSound ); if ( pSound && pSound->m_iType == iType && pSound->ValidateOwner() ) { flDist = ( pSound->GetSoundOrigin() - vecEarPosition ).Length(); //FIXME: This doesn't match what's in Listen() //flDist = UTIL_DistApprox( pSound->GetSoundOrigin(), vecEarPosition ); if ( flDist <= pSound->m_iVolume && flDist < flBestDist ) { pLoudestSound = pSound; iBestSound = iThisSound; flBestDist = flDist; } } iThisSound = pSound->m_iNext; } return pLoudestSound; }
//========================================================= // 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(); }
//========================================================= // Think - at interval, the entire active sound list is checked // for sounds that have ExpireTimes less than or equal // to the current world time, and these sounds are deallocated. //========================================================= void CSoundEnt::Think ( void ) { int iSound; int iPreviousSound; SetNextThink( gpGlobals->curtime + 0.1 );// how often to check the sound list. iPreviousSound = SOUNDLIST_EMPTY; iSound = m_iActiveSound; while ( iSound != SOUNDLIST_EMPTY ) { if ( (m_SoundPool[ iSound ].m_flExpireTime <= gpGlobals->curtime && (!m_SoundPool[ iSound ].m_bNoExpirationTime)) || !m_SoundPool[iSound].ValidateOwner() ) { int iNext = m_SoundPool[ iSound ].m_iNext; if( displaysoundlist.GetInt() == 1 ) { Msg(" Removed Sound: %d (Time:%f)\n", m_SoundPool[ iSound ].SoundType(), gpGlobals->curtime ); } if( displaysoundlist.GetInt() == 2 && m_SoundPool[ iSound ].IsSoundType( SOUND_DANGER ) ) { Msg(" Removed Danger Sound: %d (time:%f)\n", m_SoundPool[ iSound ].SoundType(), gpGlobals->curtime ); } // move this sound back into the free list FreeSound( iSound, iPreviousSound ); iSound = iNext; } else { if( displaysoundlist.GetBool() ) { Vector forward, right, up; GetVectors( &forward, &right, &up ); byte r, g, b; // Default to yellow. r = 255; g = 255; b = 0; CSound *pSound = &m_SoundPool[ iSound ]; if( pSound->IsSoundType( SOUND_DANGER ) ) { r = 255; g = 0; b = 0; } if( displaysoundlist.GetInt() == 1 || (displaysoundlist.GetInt() == 2 && pSound->IsSoundType( SOUND_DANGER ) ) ) { NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + forward * pSound->Volume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - forward * pSound->Volume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + right * pSound->Volume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - right * pSound->Volume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + up * pSound->Volume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - up * pSound->Volume(), r,g,b, false, 0.1 ); if( pSound->m_flOcclusionScale != 1.0 ) { // Draw the occluded radius, too. r = 0; g = 150; b = 255; NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + forward * pSound->OccludedVolume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - forward * pSound->OccludedVolume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + right * pSound->OccludedVolume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - right * pSound->OccludedVolume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() + up * pSound->OccludedVolume(), r,g,b, false, 0.1 ); NDebugOverlay::Line( pSound->GetSoundOrigin(), pSound->GetSoundOrigin() - up * pSound->OccludedVolume(), r,g,b, false, 0.1 ); } } DevMsg( 2, "Soundlist: %d / %d (%d)\n", ISoundsInList( SOUNDLISTTYPE_ACTIVE ),ISoundsInList( SOUNDLISTTYPE_FREE ), ISoundsInList( SOUNDLISTTYPE_ACTIVE ) - m_cLastActiveSounds ); m_cLastActiveSounds = ISoundsInList ( SOUNDLISTTYPE_ACTIVE ); } iPreviousSound = iSound; iSound = m_SoundPool[ iSound ].m_iNext; } } }
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; } }
//========================================================= // InsertSound - Allocates a free sound and fills it with // sound info. //========================================================= int CSoundEnt::InsertSound ( int iType, const Vector &vecOrigin, int iVolume, float flDuration, CBaseEntity *pOwner, int soundChannelIndex, CBaseEntity *pSoundTarget ) { int iThisSound; if ( !g_pSoundEnt ) return SOUNDLIST_EMPTY; if( soundChannelIndex == SOUNDENT_CHANNEL_UNSPECIFIED ) { // No sound channel specified. So just make a new sound. iThisSound = g_pSoundEnt->IAllocSound(); } else { // If this entity has already got a sound in the soundlist that's on this // channel, update that sound. Otherwise add a new one. iThisSound = g_pSoundEnt->FindOrAllocateSound( pOwner, soundChannelIndex ); } if ( iThisSound == SOUNDLIST_EMPTY ) { DevMsg( "Could not AllocSound() for InsertSound() (Game DLL)\n" ); return SOUNDLIST_EMPTY; } CSound *pSound; pSound = &g_pSoundEnt->m_SoundPool[ iThisSound ]; pSound->SetSoundOrigin( vecOrigin ); pSound->m_iType = iType; pSound->m_iVolume = iVolume; pSound->m_flOcclusionScale = 0.5; if ( flDuration != FLT_MAX ) { pSound->m_flExpireTime = gpGlobals->curtime + flDuration; pSound->m_bNoExpirationTime = false; } else { pSound->m_flExpireTime = FLT_MAX; pSound->m_bNoExpirationTime = true; } pSound->m_hOwner.Set( pOwner ); pSound->m_hTarget.Set( pSoundTarget ); pSound->m_ownerChannelIndex = soundChannelIndex; // Keep track of whether this sound had an owner when it was made. If the sound has a long duration, // the owner could disappear by the time someone hears this sound, so we have to look at this boolean // and throw out sounds who have a NULL owner but this field set to true. (sjb) 12/2/2005 if( pOwner ) { pSound->m_bHasOwner = true; } else { pSound->m_bHasOwner = false; } if( displaysoundlist.GetInt() == 1 ) { Msg(" Added Sound! Type:%d Duration:%f (Time:%f)\n", pSound->SoundType(), flDuration, gpGlobals->curtime ); NDebugOverlay::Text( pSound->GetSoundOrigin(), UTIL_VarArgs( "Sound: %d", pSound->SoundType() ), false, flDuration ); } if( displaysoundlist.GetInt() == 2 && (iType & SOUND_DANGER) ) { Msg(" Added Danger Sound! Duration:%f (Time:%f)\n", flDuration, gpGlobals->curtime ); } return iThisSound; }