diff --git a/source/src/entities.cpp b/source/src/entities.cpp index 78ffd99f..8997c883 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -10,7 +10,7 @@ char *entnames[] = "clips", "ammobox","grenades", "health", "armour", "akimbo", "mapmodel", "trigger", - "ladder", "ctf-flag", "?", "?", "?", + "ladder", "ctf-flag", "sound", "?", "?", }; char *entmdlnames[] = { diff --git a/source/src/entity.h b/source/src/entity.h index 08a8d803..490097ad 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -9,6 +9,7 @@ enum // static entity types CARROT, // attr1 = tag, attr2 = type LADDER, CTF_FLAG, // attr1 = angle, attr2 = red/blue + SOUND, MAXENTTYPES }; @@ -24,7 +25,8 @@ struct persistent_entity // map entity struct entity : public persistent_entity { - bool spawned; // the only dynamic state of a map entity + bool spawned; // fixme,ah//the only dynamic state of a map entity + bool visible; }; struct itemstat { int add, start, max, sound; }; diff --git a/source/src/main.cpp b/source/src/main.cpp index 2ddef061..74db51b0 100644 --- a/source/src/main.cpp +++ b/source/src/main.cpp @@ -455,6 +455,8 @@ int main(int argc, char **argv) fps = (1000.0f/elapsed+fps*10)/11; frames++; + checkmapsounds(); + computeraytable(camera1->o.x, camera1->o.y); if(frames>4) SDL_GL_SwapBuffers(); extern void updatevol(); updatevol(); diff --git a/source/src/protos.h b/source/src/protos.h index b2cf4119..18862be9 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -294,11 +294,12 @@ extern void fixcamerarange(physent *cam = camera1); extern float floor(short x, short y); // sound -extern void playsound(int n, vec *loc = 0); +extern void playsound(int n, const vec *loc = NULL, entity *ent = NULL); extern void playsoundc(int n); extern void initsound(); extern void cleansound(); -extern void music(char *name); +extern void checkmapsounds(); +extern void music(char *name, char *cmd); // rendermodel extern void rendermodel(char *mdl, int anim, int tex, float rad, const vec &o, float yaw, float pitch, float speed = 0, int basetime = 0, playerent *d = NULL, char *vwepmdl = NULL, float scale = 1.0f); diff --git a/source/src/sound.cpp b/source/src/sound.cpp index 3ae3af51..fede1849 100644 --- a/source/src/sound.cpp +++ b/source/src/sound.cpp @@ -1,7 +1,6 @@ // sound.cpp: uses fmod on windows and sdl_mixer on unix (both had problems on the other platform) #include "cube.h" -#include "bot/bot.h" //#ifndef WIN32 // NOTE: fmod not being supported for the moment as it does not allow stereo pan/vol updating during playback #define USE_MIXER @@ -13,12 +12,13 @@ bool nosound = true; #include "SDL_mixer.h" #define MAXVOL MIX_MAX_VOLUME Mix_Music *mod = NULL; - void *stream = NULL; + void *stream = NULL; // TODO #else #include "fmod.h" - #define MAXVOL 255 FMUSIC_MODULE *mod = NULL; FSOUND_STREAM *stream = NULL; + + #define MAXVOL 255 int musicchan; #endif @@ -31,11 +31,25 @@ struct sample FSOUND_SAMPLE *sound; #endif - sample(const char *name) : name(newstring(name)), sound(NULL) {} + sample() : name(NULL) {} ~sample() { DELETEA(name); } }; -struct soundloc { vec loc; bool inuse; }; +struct soundslot +{ + sample *s; + int vol; + int uses, maxuses; +}; + +struct soundloc +{ + vec loc; + bool inuse; + soundslot *slot; + entity *ent; +}; + vector soundlocs; void setmusicvol(int musicvol) @@ -49,12 +63,15 @@ void setmusicvol(int musicvol) #endif } -VARP(soundvol, 0, 150, 255); +VARP(soundvol, 0, 255, 255); VARFP(musicvol, 0, 128, 255, setmusicvol(musicvol)); +char *musicdonecmd = NULL; + void stopsound() { if(nosound) return; + DELETEA(musicdonecmd); if(mod) { #ifdef USE_MIXER @@ -86,7 +103,7 @@ void initsound() conoutf("sound init failed (SDL_mixer): %s", (size_t)Mix_GetError()); return; } - Mix_AllocateChannels(soundchans); + Mix_AllocateChannels(soundchans); #else if(FSOUND_GetVersion()=0) { FSOUND_SetVolume(musicchan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(musicchan, false); } } - #endif + #endif else { conoutf("could not play music: %s", sn); } - } } -COMMAND(music, ARG_1STR); +COMMAND(music, ARG_2STR); -vector samples; +hashtable samples; +vector gamesounds, mapsounds; -int registersound(char *name) +int findsound(char *name, int vol, vector &sounds) { - loopv(samples) if(!strcmp(samples[i]->name, name)) return i; - samples.add(new sample(name)); - return samples.length()-1; + loopv(sounds) + { + if(!strcmp(sounds[i].s->name, name) && (!vol || sounds[i].vol==vol)) return i; + } + return -1; } -COMMAND(registersound, ARG_1EST); +int addsound(char *name, int vol, int maxuses, vector &sounds) +{ + sample *s = samples.access(name); + if(!s) + { + char *n = newstring(name); + s = &samples[n]; + s->name = n; + s->sound = NULL; + } + soundslot &slot = sounds.add(); + slot.s = s; + slot.vol = vol ? vol : 100; + slot.uses = 0; + slot.maxuses = maxuses; + return sounds.length()-1; +} + +void registersound(char *name, char *vol) { addsound(name, atoi(vol), 0, gamesounds); } +COMMAND(registersound, ARG_2STR); + +void mapsound(char *name, char *vol, char *maxuses) { addsound(name, atoi(vol), atoi(maxuses) < 0 ? 0 : max(1, *maxuses), mapsounds); } +COMMAND(mapsound, ARG_3STR); void cleansound() { if(nosound) return; stopsound(); + gamesounds.setsizenodelete(0); + mapsounds.setsizenodelete(0); + samples.clear(); #ifdef USE_MIXER Mix_CloseAudio(); #else @@ -156,24 +219,66 @@ void cleansound() #endif } +void clearmapsounds() +{ + loopv(soundlocs) if(soundlocs[i].inuse && soundlocs[i].ent) + { + #ifdef USE_MIXER + if(Mix_Playing(i)) Mix_HaltChannel(i); + #else + if(FSOUND_IsPlaying(i)) FSOUND_StopSound(); + #endif + soundlocs[i].inuse = false; + soundlocs[i].ent->visible = false; + soundlocs[i].slot->uses--; + } + mapsounds.setsizenodelete(0); +} + +void checkmapsounds() +{ + //const vector &ents = et->getents(); + loopv(ents) + { + entity &e = ents[i]; + vec o(e.x, e.y, e.z); + if(e.type!=SOUND || e.visible || camera1->o.dist(o)>=e.attr2) continue; + playsound(e.attr1, NULL, &e); + } +} + VAR(stereo, 0, 1, 1); -void updatechanvol(int chan, vec *loc) +void updatechanvol(int chan, int svol, const vec *loc = NULL, entity *ent = NULL) { int vol = soundvol, pan = 255/2; if(loc) { vec v; float dist = camera1->o.dist(*loc, v); - vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation - if(vol<0) vol = 0; - if(stereo && (v.x != 0 || v.y != 0)) + if(ent) + { + int rad = ent->attr2; + if(ent->attr3) + { + rad -= ent->attr3; + dist -= ent->attr3; + } + vol -= (int)(min(max(dist/rad, 0), 1)*soundvol); + } + else + { + vol -= (int)(dist*3/4*soundvol/255); // simple mono distance attenuation + if(vol<0) vol = 0; + } + if(stereo && (v.x != 0 || v.y != 0) && dist>0) { float yaw = -atan2f(v.x, v.y) - camera1->yaw*RAD; // relative angle of sound along X-Y axis pan = int(255.9f*(0.5f*sinf(yaw)+0.5f)); // range is from 0 (left) to 255 (right) } } - vol = (vol*MAXVOL)/255; + vol = (vol*MAXVOL*svol)/255/255; + vol = min(vol, MAXVOL); #ifdef USE_MIXER Mix_Volume(chan, vol); Mix_SetPanning(chan, 255-pan, pan); @@ -183,11 +288,13 @@ void updatechanvol(int chan, vec *loc) #endif } -void newsoundloc(int chan, vec *loc) +void newsoundloc(int chan, const vec *loc, soundslot *slot, entity *ent = NULL) { while(chan >= soundlocs.length()) soundlocs.add().inuse = false; soundlocs[chan].loc = *loc; soundlocs[chan].inuse = true; + soundlocs[chan].slot = slot; + soundlocs[chan].ent = ent; } void updatevol() @@ -200,63 +307,100 @@ void updatevol() #else if(FSOUND_IsPlaying(i)) #endif - updatechanvol(i, &soundlocs[i].loc); - else soundlocs[i].inuse = false; + updatechanvol(i, soundlocs[i].slot->vol, &soundlocs[i].loc, soundlocs[i].ent); + else + { + soundlocs[i].inuse = false; + if(soundlocs[i].ent) + { + soundlocs[i].ent->visible = false; + soundlocs[i].slot->uses--; + } + } } +#ifndef USE_MIXER + if(mod && FMUSIC_IsFinished(mod)) musicdone(); + else if(stream && !FSOUND_IsPlaying(musicchan)) musicdone(); +#else + if(mod && !Mix_PlayingMusic()) musicdone(); +#endif } -void playsoundc(int n) -{ - addmsg(SV_SOUND, "i", n); - playsound(n); -} - -void playsound(int n, vec *loc) +void playsound(int n, const vec *loc, entity *ent) { if(nosound) return; if(!soundvol) return; - if(n==S_NULL) return; - if(m_botmode) BotManager.LetBotsHear(n, loc); - static int soundsatonce = 0, lastsoundmillis = 0; - if(lastmillis==lastsoundmillis) soundsatonce++; else soundsatonce = 1; - lastsoundmillis = lastmillis; - if(soundsatonce>5) return; // avoid bursts of sounds with heavy packetloss and in sp - - if(!samples.inrange(n)) { conoutf("unregistered sound: %d", n); return; } - - sample &s = *samples[n]; - if(!s.sound) + if(!ent) { - s_sprintfd(buf)("packages/audio/sounds/%s", s.name); + static int soundsatonce = 0, lastsoundmillis = 0; + if(totalmillis==lastsoundmillis) soundsatonce++; else soundsatonce = 1; + lastsoundmillis = totalmillis; + if(soundsatonce>5) return; // avoid bursts of sounds with heavy packetloss and in sp + } + + vector &sounds = ent ? mapsounds : gamesounds; + if(!sounds.inrange(n)) { conoutf("unregistered sound: %d", n); return; } + soundslot &slot = sounds[n]; + if(ent && slot.maxuses && slot.uses>=slot.maxuses) return; + + if(!slot.s->sound) + { + s_sprintfd(buf)("packages/audio/sounds/%s", slot.s->name); loopi(2) { if(i) s_strcat(buf, ".wav"); const char *file = findfile(path(buf), "rb"); #ifdef USE_MIXER - s.sound = Mix_LoadWAV(file); + slot.s->sound = Mix_LoadWAV(file); #else - s.sound = FSOUND_Sample_Load(n, file, FSOUND_LOOP_OFF, 0, 0); + slot.s->sound = FSOUND_Sample_Load(ent ? n+gamesounds.length() : n, file, FSOUND_LOOP_OFF, 0, 0); #endif - if(s.sound) break; + if(slot.s->sound) break; } - if(!s.sound) { conoutf("failed to load sample: %s", buf); return; } + if(!slot.s->sound) { conoutf("failed to load sample: %s", buf); return; } } - + #ifdef USE_MIXER - int chan = Mix_PlayChannel(-1, s.sound, 0); + int chan = Mix_PlayChannel(-1, slot.s->sound, 0); #else - int chan = FSOUND_PlaySoundEx(FSOUND_FREE, s.sound, NULL, true); + int chan = FSOUND_PlaySoundEx(FSOUND_FREE, slot.s->sound, NULL, true); #endif if(chan<0) return; - if(loc) newsoundloc(chan, loc); - updatechanvol(chan, loc); + + vec eloc; //fixme,ah + if(ent) + { + eloc.x = ent->x; + eloc.y = ent->y; + eloc.z = ent->z; + loc = &eloc; + //loc = &ent->o; + ent->visible = true; + slot.uses++; + } + if(loc) newsoundloc(chan, loc, &slot, ent); + updatechanvol(chan, slot.vol, loc, ent); #ifndef USE_MIXER FSOUND_SetPaused(chan, false); #endif } -void sound(int n) { playsound(n, NULL); } +void playsoundname(char *s, const vec *loc, int vol) +{ + if(!vol) vol = 100; + int id = findsound(s, vol, gamesounds); + if(id < 0) id = addsound(s, vol, 0, gamesounds); + playsound(id, loc); +} + +void sound(int *n) { playsound(*n); } COMMAND(sound, ARG_1INT); + +void playsoundc(int n) +{ + addmsg(SV_SOUND, "i", n); + playsound(n); +} \ No newline at end of file diff --git a/source/src/sound.h b/source/src/sound.h index 177609a7..e55f3ef5 100644 --- a/source/src/sound.h +++ b/source/src/sound.h @@ -20,6 +20,7 @@ enum S_GRENADEPULL, S_GRENADETHROW, S_RAKIMBO, S_GUNCHANGE, S_GIB, S_HEADSHOT, S_CALLVOTE, S_VOTEPASS, S_VOTEFAIL, + S_FOOTSTEPS, S_NULL };