You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
219 lines
5.7 KiB
219 lines
5.7 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
// $NoKeywords: $ |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "player.h" |
|
#include "mathlib/mathlib.h" |
|
#include "ai_speech.h" |
|
#include "stringregistry.h" |
|
#include "gamerules.h" |
|
#include "game.h" |
|
#include <ctype.h> |
|
#include "entitylist.h" |
|
#include "vstdlib/random.h" |
|
#include "engine/IEngineSound.h" |
|
#include "ndebugoverlay.h" |
|
#include "soundscape.h" |
|
|
|
#define SPEAKER_START_SILENT 1 // wait for trigger 'on' to start announcements |
|
|
|
// =================================================================================== |
|
// |
|
// Speaker class. Used for announcements per level, for door lock/unlock spoken voice. |
|
// |
|
|
|
class CSpeaker : public CPointEntity |
|
{ |
|
DECLARE_CLASS( CSpeaker, CPointEntity ); |
|
public: |
|
bool KeyValue( const char *szKeyName, const char *szValue ); |
|
void Spawn( void ); |
|
void Precache( void ); |
|
void ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ); |
|
void SpeakerThink( void ); |
|
|
|
virtual int ObjectCaps( void ) { return (CBaseEntity::ObjectCaps() & ~FCAP_ACROSS_TRANSITION); } |
|
|
|
int m_preset; // preset number |
|
string_t m_iszMessage; |
|
|
|
DECLARE_DATADESC(); |
|
}; |
|
|
|
LINK_ENTITY_TO_CLASS( speaker, CSpeaker ); |
|
|
|
BEGIN_DATADESC( CSpeaker ) |
|
DEFINE_FIELD( m_preset, FIELD_INTEGER ), |
|
DEFINE_KEYFIELD( m_iszMessage, FIELD_STRING, "message" ), |
|
DEFINE_THINKFUNC( SpeakerThink ), |
|
DEFINE_USEFUNC( ToggleUse ), |
|
END_DATADESC() |
|
|
|
// |
|
// ambient_generic - general-purpose user-defined static sound |
|
// |
|
void CSpeaker::Spawn( void ) |
|
{ |
|
char* szSoundFile = (char*) STRING( m_iszMessage ); |
|
|
|
if ( !m_preset && ( m_iszMessage == NULL_STRING || strlen( szSoundFile ) < 1 ) ) |
|
{ |
|
Msg( "SPEAKER with no Level/Sentence! at: %f, %f, %f\n", GetAbsOrigin().x, GetAbsOrigin().y, GetAbsOrigin().z ); |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
SetThink( &CSpeaker::SUB_Remove ); |
|
return; |
|
} |
|
SetSolid( SOLID_NONE ); |
|
SetMoveType( MOVETYPE_NONE ); |
|
|
|
|
|
SetThink(&CSpeaker::SpeakerThink); |
|
SetNextThink( TICK_NEVER_THINK ); |
|
|
|
// allow on/off switching via 'use' function. |
|
SetUse ( &CSpeaker::ToggleUse ); |
|
|
|
Precache( ); |
|
} |
|
|
|
#define ANNOUNCE_MINUTES_MIN 0.25 |
|
#define ANNOUNCE_MINUTES_MAX 2.25 |
|
|
|
void CSpeaker::Precache( void ) |
|
{ |
|
if ( !FBitSet ( GetSpawnFlags(), SPEAKER_START_SILENT ) ) |
|
// set first announcement time for random n second |
|
SetNextThink( gpGlobals->curtime + random->RandomFloat( 5.0, 15.0 ) ); |
|
} |
|
void CSpeaker::SpeakerThink( void ) |
|
{ |
|
char* szSoundFile = NULL; |
|
float flvolume = m_iHealth * 0.1; |
|
int flags = 0; |
|
int pitch = 100; |
|
|
|
|
|
// Wait for the talking characters to finish first. |
|
if ( !g_AIFriendliesTalkSemaphore.IsAvailable( this ) || !g_AIFoesTalkSemaphore.IsAvailable( this ) ) |
|
{ |
|
float releaseTime = MAX( g_AIFriendliesTalkSemaphore.GetReleaseTime(), g_AIFoesTalkSemaphore.GetReleaseTime() ); |
|
SetNextThink( gpGlobals->curtime + releaseTime + random->RandomFloat( 5, 10 ) ); |
|
return; |
|
} |
|
|
|
if (m_preset) |
|
{ |
|
// go lookup preset text, assign szSoundFile |
|
switch (m_preset) |
|
{ |
|
case 1: szSoundFile = "C1A0_"; break; |
|
case 2: szSoundFile = "C1A1_"; break; |
|
case 3: szSoundFile = "C1A2_"; break; |
|
case 4: szSoundFile = "C1A3_"; break; |
|
case 5: szSoundFile = "C1A4_"; break; |
|
case 6: szSoundFile = "C2A1_"; break; |
|
case 7: szSoundFile = "C2A2_"; break; |
|
case 8: szSoundFile = "C2A3_"; break; |
|
case 9: szSoundFile = "C2A4_"; break; |
|
case 10: szSoundFile = "C2A5_"; break; |
|
case 11: szSoundFile = "C3A1_"; break; |
|
case 12: szSoundFile = "C3A2_"; break; |
|
} |
|
} else |
|
szSoundFile = (char*) STRING( m_iszMessage ); |
|
|
|
if (szSoundFile[0] == '!') |
|
{ |
|
// play single sentence, one shot |
|
UTIL_EmitAmbientSound ( GetSoundSourceIndex(), GetAbsOrigin(), szSoundFile, |
|
flvolume, SNDLVL_120dB, flags, pitch); |
|
|
|
// shut off and reset |
|
SetNextThink( TICK_NEVER_THINK ); |
|
} |
|
else |
|
{ |
|
// make random announcement from sentence group |
|
|
|
if ( SENTENCEG_PlayRndSz( edict(), szSoundFile, flvolume, SNDLVL_120dB, flags, pitch) < 0 ) |
|
Msg( "Level Design Error!\nSPEAKER has bad sentence group name: %s\n",szSoundFile); |
|
|
|
// set next announcement time for random 5 to 10 minute delay |
|
SetNextThink ( gpGlobals->curtime + |
|
random->RandomFloat( ANNOUNCE_MINUTES_MIN * 60.0, ANNOUNCE_MINUTES_MAX * 60.0 ) ); |
|
|
|
// time delay until it's ok to speak: used so that two NPCs don't talk at once |
|
g_AIFriendliesTalkSemaphore.Acquire( 5, this ); |
|
g_AIFoesTalkSemaphore.Acquire( 5, this ); |
|
} |
|
|
|
return; |
|
} |
|
|
|
|
|
// |
|
// ToggleUse - if an announcement is pending, cancel it. If no announcement is pending, start one. |
|
// |
|
void CSpeaker::ToggleUse ( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) |
|
{ |
|
int fActive = (GetNextThink() > 0.0); |
|
|
|
// fActive is TRUE only if an announcement is pending |
|
|
|
if ( useType != USE_TOGGLE ) |
|
{ |
|
// ignore if we're just turning something on that's already on, or |
|
// turning something off that's already off. |
|
if ( (fActive && useType == USE_ON) || (!fActive && useType == USE_OFF) ) |
|
return; |
|
} |
|
|
|
if ( useType == USE_ON ) |
|
{ |
|
// turn on announcements |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
return; |
|
} |
|
|
|
if ( useType == USE_OFF ) |
|
{ |
|
// turn off announcements |
|
SetNextThink( TICK_NEVER_THINK ); |
|
return; |
|
|
|
} |
|
|
|
// Toggle announcements |
|
|
|
|
|
if ( fActive ) |
|
{ |
|
// turn off announcements |
|
SetNextThink( TICK_NEVER_THINK ); |
|
} |
|
else |
|
{ |
|
// turn on announcements |
|
SetNextThink( gpGlobals->curtime + 0.1 ); |
|
} |
|
} |
|
|
|
// KeyValue - load keyvalue pairs into member data |
|
// NOTE: called BEFORE spawn! |
|
|
|
bool CSpeaker::KeyValue( const char *szKeyName, const char *szValue ) |
|
{ |
|
// preset |
|
if (FStrEq(szKeyName, "preset")) |
|
{ |
|
m_preset = atoi(szValue); |
|
return true; |
|
} |
|
else |
|
return BaseClass::KeyValue( szKeyName, szValue ); |
|
}
|
|
|