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