1 /*
2 ===========================================================================
3 
4 Doom 3 GPL Source Code
5 Copyright (C) 1999-2011 id Software LLC, a ZeniMax Media company.
6 
7 This file is part of the Doom 3 GPL Source Code ("Doom 3 Source Code").
8 
9 Doom 3 Source Code is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
13 
14 Doom 3 Source Code is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17 GNU General Public License for more details.
18 
19 You should have received a copy of the GNU General Public License
20 along with Doom 3 Source Code.  If not, see <http://www.gnu.org/licenses/>.
21 
22 In addition, the Doom 3 Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 Source Code.  If not, please request a copy in writing from id Software at the address below.
23 
24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
25 
26 ===========================================================================
27 */
28 
29 #include "sys/platform.h"
30 #include "Entity.h"
31 
32 #include "Sound.h"
33 
34 /*
35 ===============================================================================
36 
37   SOUND
38 
39 ===============================================================================
40 */
41 
42 const idEventDef EV_Speaker_On( "On", NULL );
43 const idEventDef EV_Speaker_Off( "Off", NULL );
44 const idEventDef EV_Speaker_Timer( "<timer>", NULL );
45 
CLASS_DECLARATION(idEntity,idSound)46 CLASS_DECLARATION( idEntity, idSound )
47 	EVENT( EV_Activate,				idSound::Event_Trigger )
48 	EVENT( EV_Speaker_On,			idSound::Event_On )
49 	EVENT( EV_Speaker_Off,			idSound::Event_Off )
50 	EVENT( EV_Speaker_Timer,		idSound::Event_Timer )
51 END_CLASS
52 
53 
54 /*
55 ================
56 idSound::idSound
57 ================
58 */
59 idSound::idSound( void ) {
60 	lastSoundVol = 0.0f;
61 	soundVol = 0.0f;
62 	shakeTranslate.Zero();
63 	shakeRotate.Zero();
64 	random = 0.0f;
65 	wait = 0.0f;
66 	timerOn = false;
67 	playingUntilTime = 0;
68 }
69 
70 /*
71 ================
72 idSound::Save
73 ================
74 */
Save(idSaveGame * savefile) const75 void idSound::Save( idSaveGame *savefile ) const {
76 	savefile->WriteFloat( lastSoundVol );
77 	savefile->WriteFloat( soundVol );
78 	savefile->WriteFloat( random );
79 	savefile->WriteFloat( wait );
80 	savefile->WriteBool( timerOn );
81 	savefile->WriteVec3( shakeTranslate );
82 	savefile->WriteAngles( shakeRotate );
83 	savefile->WriteInt( playingUntilTime );
84 }
85 
86 /*
87 ================
88 idSound::Restore
89 ================
90 */
Restore(idRestoreGame * savefile)91 void idSound::Restore( idRestoreGame *savefile ) {
92 	savefile->ReadFloat( lastSoundVol );
93 	savefile->ReadFloat( soundVol );
94 	savefile->ReadFloat( random );
95 	savefile->ReadFloat( wait );
96 	savefile->ReadBool( timerOn );
97 	savefile->ReadVec3( shakeTranslate );
98 	savefile->ReadAngles( shakeRotate );
99 	savefile->ReadInt( playingUntilTime );
100 }
101 
102 /*
103 ================
104 idSound::Spawn
105 ================
106 */
Spawn(void)107 void idSound::Spawn( void ) {
108 	spawnArgs.GetVector( "move", "0 0 0", shakeTranslate );
109 	spawnArgs.GetAngles( "rotate", "0 0 0", shakeRotate );
110 	spawnArgs.GetFloat( "random", "0", random );
111 	spawnArgs.GetFloat( "wait", "0", wait );
112 
113 	if ( ( wait > 0.0f ) && ( random >= wait ) ) {
114 		random = wait - 0.001;
115 		gameLocal.Warning( "speaker '%s' at (%s) has random >= wait", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
116 	}
117 
118 	soundVol		= 0.0f;
119 	lastSoundVol	= 0.0f;
120 
121 	if ( ( shakeRotate != ang_zero ) || ( shakeTranslate != vec3_zero ) ) {
122 		BecomeActive( TH_THINK );
123 	}
124 
125 	if ( !refSound.waitfortrigger && ( wait > 0.0f ) ) {
126 		timerOn = true;
127 		PostEventSec( &EV_Speaker_Timer, wait + gameLocal.random.CRandomFloat() * random );
128 	} else {
129 		timerOn = false;
130 	}
131 }
132 
133 /*
134 ================
135 idSound::Event_Trigger
136 
137 this will toggle the idle idSound on and off
138 ================
139 */
Event_Trigger(idEntity * activator)140 void idSound::Event_Trigger( idEntity *activator ) {
141 	if ( wait > 0.0f ) {
142 		if ( timerOn ) {
143 			timerOn = false;
144 			CancelEvents( &EV_Speaker_Timer );
145 		} else {
146 			timerOn = true;
147 			DoSound( true );
148 			PostEventSec( &EV_Speaker_Timer, wait + gameLocal.random.CRandomFloat() * random );
149 		}
150 	} else {
151 		if ( gameLocal.isMultiplayer ) {
152 			if ( refSound.referenceSound && ( gameLocal.time < playingUntilTime ) ) {
153 				DoSound( false );
154 			} else {
155 				DoSound( true );
156 			}
157 		} else {
158 			if ( refSound.referenceSound && refSound.referenceSound->CurrentlyPlaying() ) {
159 				DoSound( false );
160 			} else {
161 				DoSound( true );
162 			}
163 		}
164 	}
165 }
166 
167 /*
168 ================
169 idSound::Event_Timer
170 ================
171 */
Event_Timer(void)172 void idSound::Event_Timer( void ) {
173 	DoSound( true );
174 	PostEventSec( &EV_Speaker_Timer, wait + gameLocal.random.CRandomFloat() * random );
175 }
176 
177 /*
178 ================
179 idSound::Think
180 ================
181 */
Think(void)182 void idSound::Think( void ) {
183 	idAngles	ang;
184 
185 	// run physics
186 	RunPhysics();
187 
188 	// clear out our update visuals think flag since we never call Present
189 	BecomeInactive( TH_UPDATEVISUALS );
190 }
191 
192 /*
193 ===============
194 idSound::UpdateChangableSpawnArgs
195 ===============
196 */
UpdateChangeableSpawnArgs(const idDict * source)197 void idSound::UpdateChangeableSpawnArgs( const idDict *source ) {
198 
199 	idEntity::UpdateChangeableSpawnArgs( source );
200 
201 	if ( source ) {
202 		FreeSoundEmitter( true );
203 		spawnArgs.Copy( *source );
204 		idSoundEmitter *saveRef = refSound.referenceSound;
205 		gameEdit->ParseSpawnArgsToRefSound( &spawnArgs, &refSound );
206 		refSound.referenceSound = saveRef;
207 
208 		idVec3 origin;
209 		idMat3 axis;
210 
211 		if ( GetPhysicsToSoundTransform( origin, axis ) ) {
212 			refSound.origin = GetPhysics()->GetOrigin() + origin * axis;
213 		} else {
214 			refSound.origin = GetPhysics()->GetOrigin();
215 		}
216 
217 		spawnArgs.GetFloat( "random", "0", random );
218 		spawnArgs.GetFloat( "wait", "0", wait );
219 
220 		if ( ( wait > 0.0f ) && ( random >= wait ) ) {
221 			random = wait - 0.001;
222 			gameLocal.Warning( "speaker '%s' at (%s) has random >= wait", name.c_str(), GetPhysics()->GetOrigin().ToString(0) );
223 		}
224 
225 		if ( !refSound.waitfortrigger && ( wait > 0.0f ) ) {
226 			timerOn = true;
227 			DoSound( false );
228 			CancelEvents( &EV_Speaker_Timer );
229 			PostEventSec( &EV_Speaker_Timer, wait + gameLocal.random.CRandomFloat() * random );
230 		} else  if ( !refSound.waitfortrigger && !(refSound.referenceSound && refSound.referenceSound->CurrentlyPlaying() ) ) {
231 			// start it if it isn't already playing, and we aren't waitForTrigger
232 			DoSound( true );
233 			timerOn = false;
234 		}
235 	}
236 }
237 
238 /*
239 ===============
240 idSound::SetSound
241 ===============
242 */
SetSound(const char * sound,int channel)243 void idSound::SetSound( const char *sound, int channel ) {
244 	const idSoundShader *shader = declManager->FindSound( sound );
245 	if ( shader != refSound.shader ) {
246 		FreeSoundEmitter( true );
247 	}
248 	gameEdit->ParseSpawnArgsToRefSound(&spawnArgs, &refSound);
249 	refSound.shader = shader;
250 	// start it if it isn't already playing, and we aren't waitForTrigger
251 	if ( !refSound.waitfortrigger && !(refSound.referenceSound && refSound.referenceSound->CurrentlyPlaying() ) ) {
252 		DoSound( true );
253 	}
254 }
255 
256 /*
257 ================
258 idSound::DoSound
259 ================
260 */
DoSound(bool play)261 void idSound::DoSound( bool play ) {
262 	if ( play ) {
263 		StartSoundShader( refSound.shader, SND_CHANNEL_ANY, refSound.parms.soundShaderFlags, true, &playingUntilTime );
264 		playingUntilTime += gameLocal.time;
265 	} else {
266 		StopSound( SND_CHANNEL_ANY, true );
267 		playingUntilTime = 0;
268 	}
269 }
270 
271 /*
272 ================
273 idSound::Event_On
274 ================
275 */
Event_On(void)276 void idSound::Event_On( void ) {
277 	if ( wait > 0.0f ) {
278 		timerOn = true;
279 		PostEventSec( &EV_Speaker_Timer, wait + gameLocal.random.CRandomFloat() * random );
280 	}
281 	DoSound( true );
282 }
283 
284 /*
285 ================
286 idSound::Event_Off
287 ================
288 */
Event_Off(void)289 void idSound::Event_Off( void ) {
290 	if ( timerOn ) {
291 		timerOn = false;
292 		CancelEvents( &EV_Speaker_Timer );
293 	}
294 	DoSound( false );
295 }
296 
297 /*
298 ===============
299 idSound::ShowEditingDialog
300 ===============
301 */
ShowEditingDialog(void)302 void idSound::ShowEditingDialog( void ) {
303 	common->InitTool( EDITOR_SOUND, &spawnArgs );
304 }
305