convert line ends
[canaan.git] / prj / cam / src / sound / ambient.c
blob6e5fdccbc1386fadd928d9a88ca788d0d5b2ee3b
1 /*
2 @Copyright Looking Glass Studios, Inc.
3 1996,1997,1998,1999,2000 Unpublished Work.
4 */
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
11 #include <stdlib.h>
12 #include <string.h>
14 #include <mprintf.h>
15 #include <timer.h>
16 #include <config.h>
17 #include <matrix.h>
18 #include <appagg.h>
20 #include <objremap.h>
21 #include <schbase.h>
22 #include <schema.h>
23 #include <objpos.h>
24 #include <tagfile.h>
25 #include <vernum.h>
26 #include <wrtype.h>
27 #include <simtime.h>
28 #include <dmgmodel.h>
30 #include <ambbase.h>
31 #include <ambient_.h>
32 #include <rand.h>
34 #include <dbmem.h>
36 #include <songutil.h>
38 // @TODO: this is the only thing bound by MAX_AMB_OBJS
39 AmbientRunTime ambRTData[MAX_AMB_OBJS];
41 #ifdef PLAYTEST
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) \
48 do { \
49 if (hnd!=NO_CUR_HANDLE) \
50 mprintf("Ambient: inloop Halt didnt null handle for %x\n",hnd); \
51 hnd=NO_CUR_HANDLE; \
52 } while (0)
53 #else
54 #define amb_info(x)
55 #define amb_info2(x)
56 #define amb_hbeat()
57 #define _amb_xtra_clear(h)
58 #endif
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)
86 int i;
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
96 void AmbientReset()
98 #ifndef SHIP
99 int i;
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));
105 if (i==cur_env_idx)
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);
117 #endif
118 AmbientRunTimeInit();
121 #ifdef PLAYTEST
122 static void ShowHeartbeatStats(void)
124 int i;
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");
133 if (i==cur_env_idx)
134 mprintf(" current env");
135 else if (i==last_env_idx)
136 mprintf(" last env");
137 mprintf("\n");
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);
145 #endif
147 // if we get kill callback
148 static void _AmbSchemaKillCallback(int hSchema, ObjID schemaID, void *pData)
150 int idx=(int)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;
163 else
164 Warning(("Ambient: Got kill event for aux sample im not using!!\n"));
165 return;
167 if (idx==AMB_ENV_PDATA)
169 if (hSchema==_ambsnd_handle(cur_env_idx))
170 idx=cur_env_idx;
171 else if (hSchema==_ambsnd_handle(last_env_idx))
172 idx=last_env_idx;
173 else
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
177 #ifdef PLAYTEST
178 else if (idx>=0)
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);
181 #endif
182 if (idx>=0)
184 AmbientSound *state=ambState(idx);
185 #ifdef PLAYTEST
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));
190 else
191 amb_info(("schema kill of object ambient (idx %d) %s (%x)\n",idx,ambState(idx)->schema_name.text,hSchema));
192 #endif
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;
199 if (state)
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)
208 ambRemove(idx);
209 else if (state->flags&AMBFLG_S_AUTOOFF)
210 ambSetFlags(idx,state->flags|AMBFLG_S_TURNEDOFF);
212 #ifdef PLAYTEST
213 else
214 mprintf("AmbSchemaKillCallback: NULL state for idx %d\n",idx);
215 #endif
217 #ifdef PLAYTEST
218 else
219 amb_info(("Schema kill of nothing? (idx %d) (schema %x)\n", idx, hSchema));
220 #endif
223 // default callback
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;
237 float rad;
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);
244 float realdist;
245 mxs_vector diff;
247 // Song hack.
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
269 if (i==cur_env_idx)
271 SchemaPlayHalt(_ambsnd_handle(i));
272 if (hAuxSch1!=NO_CUR_HANDLE) SchemaPlayHalt(hAuxSch1);
273 if (hAuxSch2!=NO_CUR_HANDLE) SchemaPlayHalt(hAuxSch2);
275 else
276 SchemaPlayHalt(_ambsnd_handle(i));
278 continue;
281 mx_sub_vec(&diff,&pos->loc.vec,head_pos);
283 #define RAD_MUL 1.05
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));
296 else
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;
308 near_env_idx=i;
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)
334 return;
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)
342 #ifdef PLAYTEST
343 mprintf("Needing to use INACTIVE since state of %d is NULL\n",last_env_idx);
344 #endif
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));
360 #ifdef PLAYTEST
361 else
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));
369 else
370 amb_info(("Leaving environment %s main inactive (aux %x %x)\n",
371 ambState(last_env_idx)->schema_name.text,hAuxSch1,hAuxSch2));
373 #endif
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)));
382 else
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;
391 // Stop auxiliaries
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);
405 // Start auxiliaries
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));
421 amb_hbeat();
424 //////////////////////////
425 // save/load support
427 static TagVersion AmbientVersion = {1, 2};
428 static TagFileTag AmbientTag = {"AMBIENT"};
430 BOOL AmbientSave(ITagFile *file)
432 HRESULT result = ITagFile_OpenBlock(file, &AmbientTag, &AmbientVersion);
433 BOOL rv = FALSE;
435 if (result == S_OK)
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);
444 // cur_env_idx = -1;
447 return rv;
451 BOOL AmbientLoad(ITagFile *file)
453 TagVersion found_version = AmbientVersion;
454 HRESULT result = ITagFile_OpenBlock(file, &AmbientTag, &found_version);
455 BOOL rv = FALSE;
457 if (result == S_OK
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);
464 return rv;
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) {
475 int max = ambMax();
476 int i;
477 for (i = 0; i < max; ++i)
478 if (ambObjID(i) == obj)
480 amb_info(("Restore idx %d from obj %d\n",i,obj));
481 return i;
484 Warning(("Could not restore old ambient state from obj %d.\n", obj));
485 return -1;
488 #ifdef PLAYTEST
489 #include <r3d.h>
490 #include <g2.h>
491 #include <fix.h>
492 void AmbientDrawRadii(BOOL all)
494 int ocol = gr_get_fcolor();
495 r3s_point pt;
496 int i;
498 gr_set_fcolor(241);
499 for (i=0; i<ambMax(); i++)
500 if (ambObjID(i)!=OBJ_NULL)
502 if (!all && (_ambsnd_handle(i)==NO_CUR_HANDLE))
503 continue;
504 r3_start_block();
505 r3_transform_block(1, &pt, &ObjPosGet(ambObjID(i))->loc.vec);
506 if (pt.ccodes == 0)
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);
513 r3_end_block();
515 gr_set_fcolor(ocol);
517 #endif