1 // audio interface of the engine
2
3 #include "cube.h"
4
5 #define DEBUGCOND (audiodebug==1)
6
7 VARF(audio, 0, 1, 1, initwarning("sound configuration", INIT_RESET, CHANGE_SOUND));
8 VARP(audiodebug, 0, 0, 1);
9 char *musicdonecmd = NULL;
10
11 VARFP(musicvol, 0, 128, 255, audiomgr.setmusicvol(musicvol));
12
13 // audio manager
14
audiomanager()15 audiomanager::audiomanager()
16 {
17 nosound = true;
18 currentpitch = 1.0f;
19 device = NULL;
20 context = NULL;
21 }
22
initsound()23 void audiomanager::initsound()
24 {
25 if(!audio)
26 {
27 conoutf("audio is disabled");
28 return;
29 }
30
31 nosound = true;
32 device = NULL;
33 context = NULL;
34
35 // list available devices
36 if(alcIsExtensionPresent(NULL, "ALC_ENUMERATION_EXT"))
37 {
38 const ALCchar *devices = alcGetString(NULL, ALC_DEVICE_SPECIFIER);
39 if(devices)
40 {
41 string d;
42 copystring(d, "Audio devices: ");
43
44 // null separated device string
45 for(const ALchar *c = devices; c[strlen(c)+1]; c += strlen(c)+1)
46 {
47 if(c!=devices) concatstring(d, ", ");
48 concatstring(d, c);
49 }
50 conoutf("%s", d);
51 }
52 }
53
54 // open device
55 const char *devicename = getalias("openaldevice");
56 device = alcOpenDevice(devicename && devicename[0] ? devicename : NULL);
57
58 if(device)
59 {
60 context = alcCreateContext(device, NULL);
61 if(context)
62 {
63 alcMakeContextCurrent(context);
64
65 alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
66
67 // backend infos
68 conoutf("Sound: %s / %s (%s)", alcGetString(device, ALC_DEVICE_SPECIFIER), alGetString(AL_RENDERER), alGetString(AL_VENDOR));
69 conoutf("Driver: %s", alGetString(AL_VERSION));
70
71 // allocate OpenAL resources
72 sourcescheduler::instance().init(16);
73
74 // let the stream get the first source from the scheduler
75 gamemusic = new oggstream();
76 if(!gamemusic->valid) DELETEP(gamemusic);
77 setmusicvol(musicvol);
78
79 nosound = false;
80 }
81 }
82
83 if(nosound)
84 {
85 ALERR;
86 if(context) alcDestroyContext(context);
87 if(device) alcCloseDevice(device);
88 conoutf("sound initialization failed!");
89 }
90 }
91
music(char * name,int millis,char * cmd)92 void audiomanager::music(char *name, int millis, char *cmd)
93 {
94 if(nosound) return;
95 stopsound();
96 if(musicvol && *name)
97 {
98 if(cmd[0]) musicdonecmd = newstring(cmd);
99
100 if(gamemusic->open(name))
101 {
102 // fade
103 if(millis > 0)
104 {
105 const int fadetime = 1000;
106 gamemusic->fadein(lastmillis, fadetime);
107 gamemusic->fadeout(lastmillis+millis, fadetime);
108 }
109
110 // play
111 bool loop = cmd && cmd[0];
112 if(!gamemusic->playback(loop))
113 {
114 conoutf("could not play music: %s", name);
115 return;
116 }
117 setmusicvol(musicvol);
118 }
119 else conoutf("could not open music: %s", name);
120 }
121 }
122
musicpreload(int id)123 void audiomanager::musicpreload(int id)
124 {
125 if(nosound) return;
126 stopsound();
127 if(musicvol && (id>=M_FLAGGRAB && id<=M_LASTMINUTE2))
128 {
129 char *name = musics[id];
130 conoutf("preloading music #%d : %s", id, name);
131 if(gamemusic->open(name))
132 {
133 defformatstring(whendone)("musicvol %d", musicvol);
134 musicdonecmd = newstring(whendone);
135 //conoutf("when done: %s", musicdonecmd);
136 const int preloadfadetime = 3;
137 gamemusic->fadein(lastmillis, preloadfadetime);
138 gamemusic->fadeout(lastmillis+2*preloadfadetime, preloadfadetime);
139 if(!gamemusic->playback(false))
140 {
141 conoutf("could not play music: %s", name);
142 return;
143 }
144 setmusicvol(1); // not 0 !
145 }
146 else conoutf("could not open music: %s", name);
147 }
148 else setmusicvol(musicvol); // call "musicpreload -1" to ensure musicdonecmd runs - but it should w/o that
149 }
150
musicsuggest(int id,int millis,bool rndofs)151 void audiomanager::musicsuggest(int id, int millis, bool rndofs) // play bg music if nothing else is playing
152 {
153 if(nosound || !gamemusic) return;
154 if(gamemusic->playing()) return;
155 if(!musicvol) return;
156 if(!musics.inrange(id))
157 {
158 conoutf("\f3music %d not registered", id);
159 return;
160 }
161 char *name = musics[id];
162 if(gamemusic->open(name))
163 {
164 gamemusic->fadein(lastmillis, 1000);
165 gamemusic->fadeout(millis ? lastmillis+millis : 0, 1000);
166 if(rndofs) gamemusic->seek(millis ? (double)rnd(millis)/2.0f : (double)lastmillis);
167 if(!gamemusic->playback(rndofs)) conoutf("could not play music: %s", name);
168 }
169 else conoutf("could not open music: %s", name);
170 }
171
musicfadeout(int id)172 void audiomanager::musicfadeout(int id)
173 {
174 if(nosound || !gamemusic) return;
175 if(!gamemusic->playing() || !musics.inrange(id)) return;
176 if(!strcmp(musics[id], gamemusic->name)) gamemusic->fadeout(lastmillis+1000, 1000);
177 }
178
setmusicvol(int musicvol)179 void audiomanager::setmusicvol(int musicvol)
180 {
181 if(gamemusic) gamemusic->setvolume(musicvol > 0 ? musicvol/255.0f : 0);
182 }
183
setlistenervol(int vol)184 void audiomanager::setlistenervol(int vol)
185 {
186 if(!nosound) alListenerf(AL_GAIN, vol/255.0f);
187 }
188
registermusic(char * name)189 void audiomanager::registermusic(char *name)
190 {
191 if(nosound||!musicvol) return;
192 if(!name || !name[0]) return;
193 musics.add(newstring(name));
194 }
195
findsound(char * name,int vol,vector<soundconfig> & sounds)196 int audiomanager::findsound(char *name, int vol, vector<soundconfig> &sounds)
197 {
198 loopv(sounds)
199 {
200 if(!strcmp(sounds[i].buf->name, name) && (!vol || sounds[i].vol==vol)) return i;
201 }
202 return -1;
203 }
204
addsound(char * name,int vol,int maxuses,bool loop,vector<soundconfig> & sounds,bool load,int audibleradius)205 int audiomanager::addsound(char *name, int vol, int maxuses, bool loop, vector<soundconfig> &sounds, bool load, int audibleradius)
206 {
207 if(nosound) return -1;
208 if(!soundvol) return -1;
209
210 // check if the sound was already registered
211 int index = findsound(name, vol, sounds);
212 if(index > -1) return index;
213
214 sbuffer *b = bufferpool.find(name);
215 if(!b)
216 {
217 conoutf("\f3failed to allocate sample %s", name);
218 return -1;
219 }
220
221 if(load && !b->load()) conoutf("\f3failed to load sample %s", name);
222
223 soundconfig s(b, vol, maxuses, loop, audibleradius);
224 sounds.add(s);
225 return sounds.length()-1;
226 }
227
preloadmapsound(entity & e,bool trydl)228 void audiomanager::preloadmapsound(entity &e, bool trydl)
229 {
230 if(e.type!=SOUND || !mapsounds.inrange(e.attr1)) return;
231 sbuffer *buf = mapsounds[e.attr1].buf;
232 if(!buf->load(trydl) && !trydl) conoutf("\f3failed to load sample %s", buf->name);
233 }
234
preloadmapsounds(bool trydl)235 void audiomanager::preloadmapsounds(bool trydl)
236 {
237 loopv(ents)
238 {
239 entity &e = ents[i];
240 if(e.type==SOUND) preloadmapsound(e, trydl);
241 }
242 }
243
applymapsoundchanges()244 void audiomanager::applymapsoundchanges() // during map editing, drop all mapsounds so they can be re-added
245 {
246 loopv(locations)
247 {
248 location *l = locations[i];
249 if(l && l->ref && l->ref->type==worldobjreference::WR_ENTITY) l->drop();
250 }
251 }
252
setchannels(int num)253 void audiomanager::setchannels(int num)
254 {
255 if(!nosound) sourcescheduler::instance().init(num);
256 };
257
258
259
260 // called at game exit
soundcleanup()261 void audiomanager::soundcleanup()
262 {
263 if(nosound) return;
264
265 // destroy consuming code
266 stopsound();
267 DELETEP(gamemusic);
268 mapsounds.shrink(0);
269 locations.deletecontents();
270 gamesounds.shrink(0);
271 bufferpool.clear();
272
273 // kill scheduler
274 sourcescheduler::instance().reset();
275
276 // shutdown openal
277 alcMakeContextCurrent(NULL);
278 if(context) alcDestroyContext(context);
279 if(device) alcCloseDevice(device);
280 }
281
282 // clear world-related sounds, called on mapchange
clearworldsounds(bool fullclean)283 void audiomanager::clearworldsounds(bool fullclean)
284 {
285 stopsound();
286 if(fullclean) mapsounds.shrink(0);
287 locations.deleteworldobjsounds();
288 }
289
mapsoundreset()290 void audiomanager::mapsoundreset()
291 {
292 mapsounds.shrink(0);
293 locations.deleteworldobjsounds();
294 }
295
296 VARP(footsteps, 0, 1, 1);
297 VARP(localfootsteps, 0, 1, 1);
298
updateplayerfootsteps(playerent * p)299 void audiomanager::updateplayerfootsteps(playerent *p)
300 {
301 if(!p) return;
302
303 const int footstepradius = 20;
304
305 // find existing footstep sounds
306 physentreference ref(p);
307 location *locs[] =
308 {
309 locations.find(S_FOOTSTEPS, &ref, gamesounds),
310 locations.find(S_FOOTSTEPSCROUCH, &ref, gamesounds),
311 locations.find(S_WATERFOOTSTEPS, &ref, gamesounds),
312 locations.find(S_WATERFOOTSTEPSCROUCH, &ref, gamesounds)
313 };
314
315 bool local = (p == camera1);
316 bool inrange = footsteps && (local || (camera1->o.dist(p->o) < footstepradius));
317
318 if(!footsteps || (local && !localfootsteps) || !inrange || p->state != CS_ALIVE || lastmillis-p->lastpain < 300 || (!p->onfloor && p->timeinair>50) || (!p->move && !p->strafe) || p->inwater)
319 {
320 const int minplaytime = 200;
321 loopi(sizeof(locs)/sizeof(locs[0]))
322 {
323 location *l = locs[i];
324 if(!l) continue;
325 if(l->playmillis+minplaytime>totalmillis) continue; // tolerate short interruptions by enforcing a minimal playtime
326 l->drop();
327 }
328 }
329 else
330 {
331 // play footsteps
332
333 int grounddist;
334 if( ((int)p->o.x) >= 0 && ((int)p->o.y) >= 0 && ((int)p->o.x) < ssize && ((int)p->o.y) < ssize) { // sam's fix to the sound crash
335 grounddist = hdr.waterlevel-S((int)p->o.x, (int)p->o.y)->floor;
336 }else{
337 grounddist = 0;
338 }
339 // int grounddist = hdr.waterlevel-S((int)p->o.x, (int)p->o.y)->floor;
340 bool water = p->o.z-p->eyeheight+0.25f<hdr.waterlevel;
341 if(water && grounddist>p->eyeheight) return; // don't play step sound when jumping into water
342
343 int stepsound;
344 if(p->crouching) stepsound = water ? S_WATERFOOTSTEPSCROUCH : S_FOOTSTEPSCROUCH; // crouch
345 else stepsound = water ? S_WATERFOOTSTEPS : S_FOOTSTEPS; // normal
346
347 // proc existing sounds
348 bool isplaying = false;
349 loopi(sizeof(locs)/sizeof(locs[0]))
350 {
351 location *l = locs[i];
352 if(!l) continue;
353 if(i+S_FOOTSTEPS==stepsound) isplaying = true; // already playing
354 else l->drop(); // different footstep sound, drop it
355 }
356
357 if(!isplaying)
358 {
359 // play
360 float rndoffset = float(rnd(500))/500.0f;
361 audiomgr._playsound(stepsound, ref, local ? SP_HIGH : SP_LOW, rndoffset);
362 }
363 }
364 }
365
366 // manage looping sounds
updateloopsound(int sound,bool active,float vol)367 location *audiomanager::updateloopsound(int sound, bool active, float vol)
368 {
369 location *l = locations.find(sound, NULL, gamesounds);
370 if(!l && active) l = _playsound(sound, camerareference(), SP_HIGH, 0.0f, true);
371 else if(l && !active) l->drop();
372 if(l && vol != 1.0f) l->src->gain(vol);
373 return l;
374 }
375
376 VARP(mapsoundrefresh, 0, 10, 1000);
377
mutesound(int n,int off)378 void audiomanager::mutesound(int n, int off)
379 {
380 bool mute = (off == 0);
381 if(!gamesounds.inrange(n))
382 {
383 conoutf("\f3could not %s sound #%d", mute ? "silence" : "unmute", n);
384 return;
385 }
386 gamesounds[n].muted = mute;
387 }
388
unmuteallsounds()389 void audiomanager::unmuteallsounds()
390 {
391 loopv(gamesounds) gamesounds[i].muted = false;
392 }
393
soundmuted(int n)394 int audiomanager::soundmuted(int n)
395 {
396 return gamesounds.inrange(n) && !gamesounds[n].muted ? 0 : 1;
397 }
398
writesoundconfig(stream * f)399 void audiomanager::writesoundconfig(stream *f)
400 {
401 bool wrotesound = false;
402 loopv(gamesounds)
403 {
404 if(gamesounds[i].muted)
405 {
406 if(!wrotesound)
407 {
408 f->printf("// sound settings\n\n");
409 wrotesound = true;
410 }
411 f->printf("mutesound %d\n", i);
412 }
413 }
414 }
415
voicecom(char * sound,char * text)416 void voicecom(char *sound, char *text)
417 {
418 if(!sound || !sound[0]) return;
419 if(!text || !text[0]) return;
420 static int last = 0;
421 if(!last || lastmillis-last > 2000)
422 {
423 extern int voicecomsounds;
424 defformatstring(soundpath)("voicecom/%s", sound);
425 int s = audiomgr.findsound(soundpath, 0, gamesounds);
426 if(s < 0 || s < S_AFFIRMATIVE || s >= S_NULL) return;
427 if(voicecomsounds>0) audiomgr.playsound(s, SP_HIGH);
428 if(s >= S_NICESHOT) // public
429 {
430 addmsg(SV_VOICECOM, "ri", s);
431 toserver(text);
432 }
433 else // team
434 {
435 addmsg(SV_VOICECOMTEAM, "ri", s);
436 defformatstring(teamtext)("%c%s", '%', text);
437 toserver(teamtext);
438 }
439 last = lastmillis;
440 }
441 }
442
443 COMMAND(voicecom, "ss");
444
soundtest()445 void soundtest()
446 {
447 loopi(S_NULL) audiomgr.playsound(i, rnd(SP_HIGH+1));
448 }
449
450 COMMAND(soundtest, "");
451
452 // sound configuration
453
soundconfig(sbuffer * b,int vol,int maxuses,bool loop,int audibleradius)454 soundconfig::soundconfig(sbuffer *b, int vol, int maxuses, bool loop, int audibleradius)
455 {
456 buf = b;
457 this->vol = vol > 0 ? vol : 100;
458 this->maxuses = maxuses;
459 this->loop = loop;
460 this->audibleradius = audibleradius;
461 this->model = audibleradius > 0 ? DM_LINEAR : DM_DEFAULT; // use linear model when radius is configured
462 uses = 0;
463 muted = false;
464 }
465
onattach()466 void soundconfig::onattach()
467 {
468 uses++;
469 }
470
ondetach()471 void soundconfig::ondetach()
472 {
473 uses--;
474 }
475
476 vector<soundconfig> gamesounds, mapsounds;
477
detachsounds(playerent * owner)478 void audiomanager::detachsounds(playerent *owner)
479 {
480 if(nosound) return;
481 // make all dependent locations static
482 locations.replaceworldobjreference(physentreference(owner), staticreference(owner->o));
483 }
484
485
486 VARP(maxsoundsatonce, 0, 32, 100);
487
_playsound(int n,const worldobjreference & r,int priority,float offset,bool loop)488 location *audiomanager::_playsound(int n, const worldobjreference &r, int priority, float offset, bool loop)
489 {
490 if(nosound || !soundvol) return NULL;
491 if(soundmuted(n)) return NULL;
492 DEBUGVAR(n);
493 DEBUGVAR(priority);
494
495 if(r.type!=worldobjreference::WR_ENTITY)
496 {
497 // avoid bursts of sounds with heavy packetloss and in sp
498 static int soundsatonce = 0, lastsoundmillis = 0;
499 if(totalmillis==lastsoundmillis) soundsatonce++; else soundsatonce = 1;
500 lastsoundmillis = totalmillis;
501 if(maxsoundsatonce && soundsatonce>maxsoundsatonce)
502 {
503 DEBUGVAR(soundsatonce);
504 return NULL;
505 }
506 }
507
508 location *loc = new location(n, r, priority);
509 locations.add(loc);
510 if(!loc->stale)
511 {
512 if(offset>0) loc->offset(offset);
513 if(currentpitch!=1.0f) loc->pitch(currentpitch);
514 loc->play(loop);
515 }
516
517 return loc;
518 }
519
playsound(int n,int priority)520 void audiomanager::playsound(int n, int priority) { _playsound(n, camerareference(), priority); }
playsound(int n,physent * p,int priority)521 void audiomanager::playsound(int n, physent *p, int priority) { if(p) _playsound(n, physentreference(p), priority); }
playsound(int n,entity * e,int priority)522 void audiomanager::playsound(int n, entity *e, int priority) { if(e) _playsound(n, entityreference(e), priority); }
playsound(int n,const vec * v,int priority)523 void audiomanager::playsound(int n, const vec *v, int priority) { if(v) _playsound(n, staticreference(*v), priority); }
524
playsoundname(char * s,const vec * loc,int vol)525 void audiomanager::playsoundname(char *s, const vec *loc, int vol)
526 {
527 if(!nosound) return;
528
529 if(vol <= 0) vol = 100;
530 int id = findsound(s, vol, gamesounds);
531 if(id < 0) id = addsound(s, vol, 0, false, gamesounds, true, 0);
532 playsound(id, loc, SP_NORMAL);
533 }
534
playsoundc(int n,physent * p,int priority)535 void audiomanager::playsoundc(int n, physent *p, int priority)
536 {
537 if(p && p!=player1) playsound(n, p, priority);
538 else
539 {
540 addmsg(SV_SOUND, "i", n);
541 playsound(n, priority);
542 }
543 }
544
stopsound()545 void audiomanager::stopsound()
546 {
547 if(nosound) return;
548 DELETEA(musicdonecmd);
549 if(gamemusic) gamemusic->reset();
550 }
551
552 VARP(heartbeat, 0, 0, 99);
553
554 // main audio update routine
555
updateaudio()556 void audiomanager::updateaudio()
557 {
558 if(nosound) return;
559
560 alcSuspendContext(context); // don't process sounds while we mess around
561
562 bool alive = player1->state!=CS_DEAD;
563 bool firstperson = camera1==player1 || (player1->isspectating() && player1->spectatemode==SM_DEATHCAM);
564
565 // footsteps
566 updateplayerfootsteps(player1);
567 loopv(players)
568 {
569 playerent *p = players[i];
570 if(!p) continue;
571 updateplayerfootsteps(p);
572 }
573
574 // water
575 bool underwater = /*alive &&*/ firstperson && hdr.waterlevel>player1->o.z+player1->aboveeye;
576 updateloopsound(S_UNDERWATER, underwater);
577
578 // tinnitus
579 bool tinnitus = alive && firstperson && player1->eardamagemillis>0 && lastmillis<=player1->eardamagemillis;
580 location *tinnitusloc = updateloopsound(S_TINNITUS, tinnitus);
581
582 // heartbeat
583 bool heartbeatsound = heartbeat && alive && firstperson && !m_osok && player1->health <= heartbeat;
584 updateloopsound(S_HEARTBEAT, heartbeatsound);
585
586 // pitch fx
587 const float lowpitch = 0.65f;
588 bool pitchfx = underwater || tinnitus;
589 if(pitchfx && currentpitch!=lowpitch)
590 {
591 currentpitch = lowpitch;
592 locations.forcepitch(currentpitch);
593 if(tinnitusloc) tinnitusloc->pitch(1.9f); // super high pitched tinnitus
594 }
595 else if(!pitchfx && currentpitch==lowpitch)
596 {
597 currentpitch = 1.0f;
598 locations.forcepitch(currentpitch);
599 }
600
601 // update map sounds
602 static int lastmapsound = 0;
603 if(!lastmapsound || totalmillis-lastmapsound>mapsoundrefresh || !mapsoundrefresh)
604 {
605 loopv(ents)
606 {
607 entity &e = ents[i];
608 vec o(e.x, e.y, e.z);
609 if(e.type!=SOUND) continue;
610
611 int sound = e.attr1;
612 int radius = e.attr2;
613 bool hearable = (radius==0 || camera1->o.dist(o)<radius);
614 entityreference entref(&e);
615
616 // search existing sound loc
617 location *loc = locations.find(sound, &entref, mapsounds);
618
619 if(hearable && !loc) // play
620 {
621 _playsound(sound, entref, SP_HIGH, 0.0f, true);
622 }
623 else if(!hearable && loc) // stop
624 {
625 loc->drop();
626 }
627 }
628 lastmapsound = totalmillis;
629 }
630
631 // update all sound locations
632 locations.updatelocations();
633
634 // update background music
635 if(gamemusic)
636 {
637 if(!gamemusic->update())
638 {
639 // music ended, exec command
640 if(musicdonecmd)
641 {
642 char *cmd = musicdonecmd;
643 musicdonecmd = NULL;
644 execute(cmd);
645 delete[] cmd;
646 }
647 }
648 }
649
650 // listener
651 vec o[2];
652 o[0].x = (float)(cosf(RAD*(camera1->yaw-90)));
653 o[0].y = (float)(sinf(RAD*(camera1->yaw-90)));
654 o[0].z = 0.0f;
655 o[1].x = o[1].y = 0.0f;
656 o[1].z = -1.0f;
657 alListenerfv(AL_ORIENTATION, (ALfloat *) &o);
658 alListenerfv(AL_POSITION, (ALfloat *) &camera1->o);
659
660 alcProcessContext(context);
661 }
662
663 // binding of sounds to the 3D world
664
665 // camera
666
camerareference()667 camerareference::camerareference() : worldobjreference(WR_CAMERA) {}
668
clone() const669 worldobjreference *camerareference::clone() const
670 {
671 return new camerareference(*this);
672 }
673
currentposition() const674 const vec &camerareference::currentposition() const
675 {
676 return camera1->o;
677 }
678
nodistance()679 bool camerareference::nodistance()
680 {
681 return true;
682 }
683
operator ==(const worldobjreference & other)684 bool camerareference::operator==(const worldobjreference &other)
685 {
686 return type==other.type;
687 }
688
689 // physent
690
physentreference(physent * ref)691 physentreference::physentreference(physent *ref) : worldobjreference(WR_PHYSENT)
692 {
693 ASSERT(ref);
694 phys = ref;
695 }
696
clone() const697 worldobjreference *physentreference::clone() const
698 {
699 return new physentreference(*this);
700 }
701
currentposition() const702 const vec &physentreference::currentposition() const
703 {
704 return phys->o;
705 }
706
nodistance()707 bool physentreference::nodistance()
708 {
709 return phys==camera1;
710 }
711
operator ==(const worldobjreference & other)712 bool physentreference::operator==(const worldobjreference &other)
713 {
714 return type==other.type && phys==((physentreference &)other).phys;
715 }
716
717 // entity
718
entityreference(entity * ref)719 entityreference::entityreference(entity *ref) : worldobjreference(WR_ENTITY)
720 {
721 ASSERT(ref);
722 ent = ref;
723 }
724
clone() const725 worldobjreference *entityreference::clone() const
726 {
727 return new entityreference(*this);
728 }
currentposition() const729 const vec &entityreference::currentposition() const
730 {
731 static vec tmp;
732 tmp = vec(ent->x, ent->y, ent->z);
733 return tmp;
734 }
nodistance()735 bool entityreference::nodistance() { return ent->attr3>0; }
operator ==(const worldobjreference & other)736 bool entityreference::operator==(const worldobjreference &other) { return type==other.type && ent==((entityreference &)other).ent; }
737
738 // static
739
staticreference(const vec & ref)740 staticreference::staticreference(const vec &ref) : worldobjreference(WR_STATICPOS)
741 {
742 pos = ref;
743 }
744
clone() const745 worldobjreference *staticreference::clone() const
746 {
747 return new staticreference(*this);
748 }
749
currentposition() const750 const vec &staticreference::currentposition() const
751 {
752 return pos;
753 }
754
nodistance()755 bool staticreference::nodistance()
756 {
757 return false;
758 }
759
operator ==(const worldobjreference & other)760 bool staticreference::operator==(const worldobjreference &other)
761 {
762 return type==other.type && pos==((staticreference &)other).pos;
763 }
764
765 // instance
766
767 audiomanager audiomgr;
768
769 COMMANDF(sound, "i", (int *n)
770 {
771 audiomgr.playsound(*n);
772 });
773
774 COMMANDF(applymapsoundchanges, "", (){
775 audiomgr.applymapsoundchanges();
776 });
777
778 COMMANDF(unmuteallsounds, "", () {
779 audiomgr.unmuteallsounds();
780 });
781
782 COMMANDF(mutesound, "ii", (int *n, int *off)
783 {
784 audiomgr.mutesound(*n, *off);
785 });
786
787 COMMANDF(soundmuted, "i", (int *n)
788 {
789 intret(audiomgr.soundmuted(*n));
790 });
791
792 COMMANDF(mapsoundreset, "", ()
793 {
794 audiomgr.mapsoundreset();
795 });
796
797 VARF(soundchannels, 4, 32, 1024, audiomgr.setchannels(soundchannels); );
798
799 VARFP(soundvol, 0, 128, 255, audiomgr.setlistenervol(soundvol); );
800
801 COMMANDF(registersound, "siii", (char *name, int *vol, int *loop, int *audibleradius)
802 {
803 intret(audiomgr.addsound(name, *vol, -1, *loop != 0, gamesounds, true, *audibleradius));
804 });
805
806 COMMANDF(mapsound, "si", (char *name, int *maxuses)
807 {
808 audiomgr.addsound(name, 255, *maxuses, true, mapsounds, false, 0);
809 });
810
811 COMMANDF(registermusic, "s", (char *name)
812 {
813 audiomgr.registermusic(name);
814 });
815
816 COMMANDF(musicpreload, "i", (int *id)
817 {
818 audiomgr.musicpreload(*id);
819 });
820
821 COMMANDF(music, "sis", (char *name, int *millis, char *cmd)
822 {
823 audiomgr.music(name, *millis, cmd);
824 });
825
826