2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
6 // $Header: r:/t2repos/thief2/src/sound/ambient.c,v 1.29 1999/04/26 15:12:00 mwhite Exp $
7 // ambient sound object initial cut
8 // actual system which do things sound things
9 // ambprop has the prop sys side
38 // @TODO: this is the only thing bound by MAX_AMB_OBJS
39 AmbientRunTime ambRTData
[MAX_AMB_OBJS
];
42 bool ambient_mono
=FALSE
, ambient_heartbeat
=FALSE
;
43 int amb_heartbeat_count
=0;
44 #define amb_info(x) do { if (ambient_mono) mprintf x ; } while (0)
45 #define amb_info2(x) do { if (ambient_mono) mprintf x ; } while (0)
46 #define amb_hbeat() if (ambient_heartbeat&&(((++amb_heartbeat_count)&0x7f)==0)) ShowHeartbeatStats()
47 #define _amb_xtra_clear(hnd) \
49 if (hnd!=NO_CUR_HANDLE) \
50 mprintf("Ambient: inloop Halt didnt null handle for %x\n",hnd); \
57 #define _amb_xtra_clear(h)
60 #define AMB_ENV_INACTIVE (-1)
62 // this is dorky - i really want to make entry 0 in our list always the current environment
63 // and not let any objects get assigned to it, but im not sure how to do that
64 int cur_env_idx
= AMB_ENV_INACTIVE
, last_env_idx
=AMB_ENV_INACTIVE
;
65 int hAuxSch1
, hAuxSch2
;
67 //AmbientSound curEnv; // track the current "environment"
69 // @HACK for beta milestone, so we don't play in edit mode
70 ObjID restore_obj
= OBJ_NULL
;
72 static int sgCurrSongThemeIdx
;
74 #define _ambsnd_handle(i) (ambRTData[i].schemaHandle)
75 #define _ambsnd_lastplay(i) (ambRTData[i].last_played)
77 #define AMB_ENV_PDATA (-1)
78 #define AMB_ENV_AUX_PDATA (-2)
80 #define NO_CUR_HANDLE SCH_HANDLE_NULL
81 #define MIN_REFIRE 1000
83 // setup the initial data 'n such...
84 void AmbientRunTimeInit(void)
88 for (i
=0; i
<MAX_AMB_OBJS
; i
++)
90 _ambsnd_handle(i
)=NO_CUR_HANDLE
;
91 _ambsnd_lastplay(i
)=0;
93 hAuxSch1
=hAuxSch2
=NO_CUR_HANDLE
;
94 } // no currently running handle either
100 for (i
=0; i
<MAX_AMB_OBJS
; i
++)
102 if (_ambsnd_handle(i
)!=NO_CUR_HANDLE
)
104 mprintf("Warning... currently slot %d has handle %x\n",i
,_ambsnd_handle(i
));
106 mprintf("Warning... this is the current environment schema\n");
107 else if (i
==last_env_idx
)
108 mprintf("Warning... this is the last environment schema\n");
110 _ambsnd_handle(i
)=NO_CUR_HANDLE
;
111 _ambsnd_lastplay(i
)=0;
113 if (hAuxSch1
!=NO_CUR_HANDLE
)
114 mprintf("Warning... currenty have active auxSch1 %x\n",hAuxSch1
);
115 if (hAuxSch2
!=NO_CUR_HANDLE
)
116 mprintf("Warning... currenty have active auxSch2 %x\n",hAuxSch2
);
118 AmbientRunTimeInit();
122 static void ShowHeartbeatStats(void)
125 if (cur_env_idx
!=AMB_ENV_INACTIVE
)
126 mprintf("CurEnv is %s\n",ambState(cur_env_idx
)->schema_name
.text
);
127 for (i
=0; i
<MAX_AMB_OBJS
; i
++)
129 if (_ambsnd_handle(i
)!=NO_CUR_HANDLE
)
131 AmbientSound
*state
=ambState(i
);
132 mprintf("Amb slot %d: currently %x [%s]",i
,_ambsnd_handle(i
),state
?state
->schema_name
.text
:"Unknown");
134 mprintf(" current env");
135 else if (i
==last_env_idx
)
136 mprintf(" last env");
140 if (hAuxSch1
!=NO_CUR_HANDLE
)
141 mprintf("Amb AuxSch1 currently %x\n",hAuxSch1
);
142 if (hAuxSch2
!=NO_CUR_HANDLE
)
143 mprintf("Amb AuxSch2 currently %x\n",hAuxSch2
);
147 // if we get kill callback
148 static void _AmbSchemaKillCallback(int hSchema
, ObjID schemaID
, void *pData
)
151 if (idx
==AMB_ENV_AUX_PDATA
) // if secret hacked aux thing, deal and get out
153 if (hSchema
==hAuxSch1
)
155 amb_info(("schema kill of aux env 1 (%x)\n",hSchema
));
156 hAuxSch1
=NO_CUR_HANDLE
;
158 else if (hSchema
==hAuxSch2
)
160 amb_info(("schema kill of aux env 2 (%x)\n",hSchema
));
161 hAuxSch2
=NO_CUR_HANDLE
;
164 Warning(("Ambient: Got kill event for aux sample im not using!!\n"));
167 if (idx
==AMB_ENV_PDATA
)
169 if (hSchema
==_ambsnd_handle(cur_env_idx
))
171 else if (hSchema
==_ambsnd_handle(last_env_idx
))
174 mprintf("Amb seeing ENV handle (%x) but isnt cur or last\n",hSchema
);
176 // if we make it here, it is primary environment or an object
179 if (hSchema
!=_ambsnd_handle(idx
))
180 mprintf("AmbSchemaKillCallback: idx v. handle mismatch (idx %d gave %x v. %x)\n",idx
,_ambsnd_handle(idx
),hSchema
);
184 AmbientSound
*state
=ambState(idx
);
186 if (idx
==cur_env_idx
)
187 amb_info(("schema kill active environment (idx %d) %s (%x)\n",idx
,ambState(cur_env_idx
)->schema_name
.text
,hSchema
));
188 else if (idx
==last_env_idx
)
189 amb_info(("schema kill last environment (idx %d) (%x)\n",idx
,hSchema
));
191 amb_info(("schema kill of object ambient (idx %d) %s (%x)\n",idx
,ambState(idx
)->schema_name
.text
,hSchema
));
193 _ambsnd_handle(idx
)=NO_CUR_HANDLE
; // look, im done playing
194 if (idx
==cur_env_idx
)
195 cur_env_idx
=AMB_ENV_INACTIVE
;
196 else if (idx
==last_env_idx
)
197 last_env_idx
=AMB_ENV_INACTIVE
;
201 if (state
->flags
&AMBFLG_S_KILLOBJ
)
203 IDamageModel
*pDmgModel
=AppGetObj(IDamageModel
);
204 IDamageModel_SlayObject(pDmgModel
,ambObjID(idx
),OBJ_NULL
);
205 SafeRelease(pDmgModel
);
207 else if (state
->flags
&AMBFLG_S_REMOVE
)
209 else if (state
->flags
&AMBFLG_S_AUTOOFF
)
210 ambSetFlags(idx
,state
->flags
|AMBFLG_S_TURNEDOFF
);
214 mprintf("AmbSchemaKillCallback: NULL state for idx %d\n",idx
);
219 amb_info(("Schema kill of nothing? (idx %d) (schema %x)\n", idx
, hSchema
));
224 static sSchemaCallParams schemaCallData
={0,0,OBJ_NULL
,NULL
,0,_AmbSchemaKillCallback
,0,NULL
};
226 int _AmbientRestore();
228 // for now, we boneheadly scan the whole area every frame
229 // really, we should cache for each object how far away it was
230 // things greater than 2x rad can update every couple seconds
231 // as opposed to checking every frame
232 void AmbientRunFrame(mxs_vector
*head_pos
) // where is the head?
234 ulong sim_time
=tm_get_millisec(); // should probably use sim-time, eh?
235 int i
, near_env_idx
=AMB_ENV_INACTIVE
;
236 float near_env_dist
=1e20
;
239 for (i
=0; i
< ambMax(); i
++)
240 if (ambObjID(i
)!=OBJ_NULL
)
241 { // go through and check ranges for each object from the head
242 ObjPos
* pos
= ObjPosGet(ambObjID(i
));
243 AmbientSound
* state
= ambState(i
);
248 if ( state
->flags
& AMBFLG_S_MUSIC
)
250 if (i
!= sgCurrSongThemeIdx
)
252 // Check to see if head is inside sphere.
253 rad
= (float)state
->rad
;
254 mx_sub_vec(&diff
,&pos
->loc
.vec
,head_pos
);
255 if ( mx_mag2_vec(&diff
) < (rad
* rad
) )
257 // Head is inside sphere... Change theme.
258 sgCurrSongThemeIdx
= i
;
259 SongUtilSetTheme ( state
->schema_name
.text
);
262 continue; // Sorry.. I never use these, but it's easier here than a big "else".
265 if (state
->flags
&AMBFLG_S_TURNEDOFF
)
266 { // if ive been turned off, make sure im not active
267 if (_ambsnd_handle(i
)!=NO_CUR_HANDLE
)
268 { // if we are currently playing
271 SchemaPlayHalt(_ambsnd_handle(i
));
272 if (hAuxSch1
!=NO_CUR_HANDLE
) SchemaPlayHalt(hAuxSch1
);
273 if (hAuxSch2
!=NO_CUR_HANDLE
) SchemaPlayHalt(hAuxSch2
);
276 SchemaPlayHalt(_ambsnd_handle(i
));
281 mx_sub_vec(&diff
,&pos
->loc
.vec
,head_pos
);
284 rad
= (float)state
->rad
;
286 if (((state
->flags
&AMBFLG_S_ENVIRON
)==0)&&(_ambsnd_handle(i
)!=NO_CUR_HANDLE
))
287 { // we are already playing - but have we gone too far away
288 if ( mx_mag2_vec(&diff
)*RAD_MUL
> ( rad
* rad
) )
290 SchemaPlayHalt(_ambsnd_handle(i
));
291 amb_info(("Killing local ambient (idx %d) %s (%x)\n",
292 i
,state
->schema_name
.text
,_ambsnd_handle(i
)));
293 _amb_xtra_clear(_ambsnd_handle(i
));
297 // This "quick test" is apparently a mistake: should be "abs(diff.x)+abs(diff.y)+abs(diff.z)",
298 // but abs is roughly as expensive as actually doing the squaring. The test happens to not
299 // actually break code, and shortcuts some cases, but in general is misleading and inefficient.
300 // (It's additional computation that generally becomes "if (true)").
301 //if ((diff.x+diff.y+diff.z)<(float)(state->rad<<1)) // may be worth considering
302 if ( ( realdist
=mx_mag2_vec(&diff
)*RAD_MUL
) < ( rad
* rad
) )
303 if (state
->flags
&AMBFLG_S_ENVIRON
)
304 { // within radius of an environmental ambient object
305 if (realdist
<near_env_dist
)
307 near_env_dist
=realdist
;
311 else if (sim_time
>_ambsnd_lastplay(i
)+MIN_REFIRE
)
312 { // check to make sure we dont refire within time x passed
313 schemaCallData
.flags
= SCH_SET_OBJ
|SCH_SET_CALLBACK
|SCH_RADIUS_VOLUME
;
314 if ((state
->flags
&AMBFLG_S_NOSHARPCURVE
)==0)
315 schemaCallData
.flags
|= SCH_SHARP_ATTEN
;
316 schemaCallData
.sourceID
= ambObjID(i
); // objID
317 schemaCallData
.pData
= (void *)i
; // handle
318 schemaCallData
.volume
= state
->rad
;
319 _ambsnd_handle(i
)=SchemaPlay(&state
->schema_name
,&schemaCallData
,NULL
);
320 if (_ambsnd_handle(i
)==SCH_HANDLE_NULL
)
321 Warning(("Ambient couldn't play %s\n",state
->schema_name
.text
));
322 _ambsnd_lastplay(i
) = sim_time
;
323 amb_info(("Fired ambient object (idx %d) %s (%x)\n",
324 i
,state
->schema_name
.text
,_ambsnd_handle(i
)));
325 } // not sure how to wire up the modify callback, sadly
328 if (restore_obj
!= OBJ_NULL
)
329 near_env_idx
= _AmbientRestore();
331 if (near_env_idx
!= AMB_ENV_INACTIVE
)
332 { // Bail if we're in the currently active schema
333 if (near_env_idx
== cur_env_idx
)
336 last_env_idx
= cur_env_idx
;
337 cur_env_idx
= near_env_idx
;
338 amb_info(("Switching into env %d from %d\n",cur_env_idx
,last_env_idx
));
340 if (last_env_idx
!=AMB_ENV_INACTIVE
&& ambState(last_env_idx
)==NULL
)
343 mprintf("Needing to use INACTIVE since state of %d is NULL\n",last_env_idx
);
345 amb_info(("Needing to use INACTIVE since state of %d is NULL\n",last_env_idx
));
346 last_env_idx
=AMB_ENV_INACTIVE
;
349 // Don't start main if it has the same name
350 if ((last_env_idx
==AMB_ENV_INACTIVE
)||
351 (stricmp(ambState(last_env_idx
)->schema_name
.text
,ambState(cur_env_idx
)->schema_name
.text
)))
352 { // it is an environmental of a different name
353 if ((last_env_idx
!=AMB_ENV_INACTIVE
)&&(_ambsnd_handle(last_env_idx
)!=NO_CUR_HANDLE
))
354 { // we are currently playing, want to stop it
355 amb_info(("Leaving environment schema (idx %d) %s (%x)\n",
356 last_env_idx
,ambState(last_env_idx
)->schema_name
.text
,_ambsnd_handle(last_env_idx
)));
357 SchemaPlayHalt(_ambsnd_handle(last_env_idx
)); // @TODO: Fade?
358 _amb_xtra_clear(_ambsnd_handle(last_env_idx
));
363 if (last_env_idx
==AMB_ENV_INACTIVE
)
365 if ((hAuxSch1
!=NO_CUR_HANDLE
)||(hAuxSch2
!=NO_CUR_HANDLE
))
366 Warning(("Had aux 1 or 2, no primary ambient bed\n"));
367 amb_info(("Leaving environment NULL (%x %x)\n",hAuxSch1
,hAuxSch2
));
370 amb_info(("Leaving environment %s main inactive (aux %x %x)\n",
371 ambState(last_env_idx
)->schema_name
.text
,hAuxSch1
,hAuxSch2
));
374 if (ambState(near_env_idx
)->schema_name
.text
[0]!='\0')
376 schemaCallData
.flags
= SCH_SET_CALLBACK
;
377 schemaCallData
.pData
= (void *)AMB_ENV_PDATA
; // cur_env_idx;
378 _ambsnd_handle(cur_env_idx
)=SchemaPlay(&ambState(cur_env_idx
)->schema_name
, &schemaCallData
, NULL
);
379 amb_info(("Entering environment schema (idx %d) %s (%x)\n",
380 cur_env_idx
,ambState(cur_env_idx
)->schema_name
.text
,_ambsnd_handle(cur_env_idx
)));
383 amb_info(("Entering environment schema NULL\n"));
385 else if (last_env_idx
!=AMB_ENV_INACTIVE
)
386 { // same base schema, maybe new aux stuff
387 _ambsnd_handle(cur_env_idx
)=_ambsnd_handle(last_env_idx
);
388 _ambsnd_handle(last_env_idx
)=NO_CUR_HANDLE
;
392 if (hAuxSch1
!=NO_CUR_HANDLE
)
394 amb_info2(("kill aux 1 %x\n",hAuxSch1
));
395 SchemaPlayHalt(hAuxSch1
);
396 _amb_xtra_clear(hAuxSch1
);
398 if (hAuxSch2
!=NO_CUR_HANDLE
)
400 amb_info2(("kill aux 2 %x\n",hAuxSch2
));
401 SchemaPlayHalt(hAuxSch2
);
402 _amb_xtra_clear(hAuxSch2
);
406 if (ambState(near_env_idx
)->aux_schema_1
.text
[0]!='\0')
408 schemaCallData
.flags
= SCH_SET_CALLBACK
;
409 schemaCallData
.pData
= (void *)AMB_ENV_AUX_PDATA
;
410 hAuxSch1
=SchemaPlay(&ambState(cur_env_idx
)->aux_schema_1
, &schemaCallData
,NULL
);
411 amb_info2(("start aux 1 %x\n",hAuxSch1
));
413 if (ambState(near_env_idx
)->aux_schema_2
.text
[0]!='\0')
415 schemaCallData
.flags
= SCH_SET_CALLBACK
;
416 schemaCallData
.pData
= (void *)AMB_ENV_AUX_PDATA
;
417 hAuxSch2
=SchemaPlay(&ambState(cur_env_idx
)->aux_schema_2
, &schemaCallData
, NULL
);
418 amb_info2(("start aux 2 %x\n",hAuxSch2
));
424 //////////////////////////
427 static TagVersion AmbientVersion
= {1, 2};
428 static TagFileTag AmbientTag
= {"AMBIENT"};
430 BOOL
AmbientSave(ITagFile
*file
)
432 HRESULT result
= ITagFile_OpenBlock(file
, &AmbientTag
, &AmbientVersion
);
437 ObjID obj
= ambObjID(cur_env_idx
);
438 ITagFile_Write(file
, (char *) &obj
, sizeof(obj
));
439 ITagFile_CloseBlock(file
);
441 if (cur_env_idx
!= -1)
442 { // @HACK for beta: make sound start again
443 restore_obj
= ambObjID(cur_env_idx
);
451 BOOL
AmbientLoad(ITagFile
*file
)
453 TagVersion found_version
= AmbientVersion
;
454 HRESULT result
= ITagFile_OpenBlock(file
, &AmbientTag
, &found_version
);
458 && found_version
.major
== AmbientVersion
.major
459 && found_version
.minor
== AmbientVersion
.minor
)
461 ITagFile_Read(file
, (char *) &restore_obj
, sizeof(restore_obj
));
462 ITagFile_CloseBlock(file
);
467 // attempt to restore on load
468 static int _AmbientRestore(void)
470 ObjID obj
= ObjRemapOnLoad(restore_obj
);
472 restore_obj
= OBJ_NULL
;
474 if (obj
!= OBJ_NULL
) {
477 for (i
= 0; i
< max
; ++i
)
478 if (ambObjID(i
) == obj
)
480 amb_info(("Restore idx %d from obj %d\n",i
,obj
));
484 Warning(("Could not restore old ambient state from obj %d.\n", obj
));
492 void AmbientDrawRadii(BOOL all
)
494 int ocol
= gr_get_fcolor();
499 for (i
=0; i
<ambMax(); i
++)
500 if (ambObjID(i
)!=OBJ_NULL
)
502 if (!all
&& (_ambsnd_handle(i
)==NO_CUR_HANDLE
))
505 r3_transform_block(1, &pt
, &ObjPosGet(ambObjID(i
))->loc
.vec
);
508 AmbientSound
*state
=ambState(i
);
509 int s
=(int)r3_get_hsize(pt
.p
.z
,(float)state
->rad
);
510 int rad
=fix_div(s
,grd_bm
.w
);
511 g2_circle(pt
.grp
.sx
, pt
.grp
.sy
, rad
);