706 lines
15 KiB
Plaintext
706 lines
15 KiB
Plaintext
/*
|
|
* Copyright (c) 2016-2020 Marco Hladik <marco@icculus.org>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
|
|
* IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
|
|
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#ifndef SOUNDSHADER_DYNAMIC
|
|
#ifndef SOUNDSHADER_MAX
|
|
#define SOUNDSHADER_MAX 512
|
|
#endif
|
|
#endif
|
|
|
|
/*
|
|
#define SOUNDSHADER_DYNAMIC
|
|
|
|
Your game can define SOUNDSHADER_DYNAMIC in its progs.src if you want an unpredictable amount of sound shaders.
|
|
Other than that, you can increase the value of SOUNDSHADER_MAX.
|
|
|
|
We switched to up-front allocation because QCLIB fragments memory like hell as there's
|
|
no real garbage collector to speak of
|
|
*/
|
|
|
|
void
|
|
Sound_Shutdown(void)
|
|
{
|
|
if (g_sounds) {
|
|
memfree(g_sounds);
|
|
}
|
|
|
|
g_sounds_count = 0;
|
|
g_hashsounds = 0;
|
|
}
|
|
|
|
void
|
|
Sound_Init(void)
|
|
{
|
|
/* make sure it's all reset */
|
|
Sound_Shutdown();
|
|
|
|
#ifndef SOUNDSHADER_DYNAMIC
|
|
g_sounds = (snd_t *)memalloc(sizeof(snd_t) * SOUNDSHADER_MAX);
|
|
print(sprintf("Sound_Init: Allocated %d bytes\n", sizeof(snd_t) * SOUNDSHADER_MAX));
|
|
#endif
|
|
|
|
precache_sound("misc/missing.wav");
|
|
}
|
|
|
|
void
|
|
Sound_ParseField(int i, int a)
|
|
{
|
|
switch (argv(0)) {
|
|
case "attenuation":
|
|
if (a == 2) {
|
|
switch(argv(1)) {
|
|
case "idle":
|
|
g_sounds[i].dist_max = 1000 / ATTN_IDLE;
|
|
break;
|
|
case "static":
|
|
g_sounds[i].dist_max = 1000 / ATTN_STATIC;
|
|
break;
|
|
case "none":
|
|
g_sounds[i].dist_max = 0;
|
|
break;
|
|
case "normal":
|
|
g_sounds[i].dist_max = 1000 / ATTN_NORM;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
case "dist_min":
|
|
if (a == 2) {
|
|
dprint("\tMin distance set\n");
|
|
g_sounds[i].dist_min = stof(argv(1));
|
|
}
|
|
break;
|
|
case "dist_max":
|
|
if (a == 2) {
|
|
dprint("\tMax distance set\n");
|
|
g_sounds[i].dist_max = stof(argv(1));
|
|
}
|
|
break;
|
|
case "volume":
|
|
if (a == 2) {
|
|
dprint("\tVolume set\n");
|
|
g_sounds[i].volume = stof(argv(1));
|
|
}
|
|
break;
|
|
case "shakes":
|
|
if (a == 2) {
|
|
dprint("\tShake set\n");
|
|
g_sounds[i].shakes = stof(argv(1));
|
|
}
|
|
break;
|
|
case "pitch":
|
|
if (a == 2) {
|
|
dprint("\tPitch set\n");
|
|
g_sounds[i].pitch_min = fabs(stof(argv(1))) * 100;
|
|
g_sounds[i].pitch_max = g_sounds[i].pitch_min;
|
|
}
|
|
break;
|
|
case "pitch_min":
|
|
if (a == 2) {
|
|
dprint("\tMinimum pitch set\n");
|
|
g_sounds[i].pitch_min = fabs(stof(argv(1))) * 100;
|
|
}
|
|
break;
|
|
case "pitch_max":
|
|
if (a == 2) {
|
|
dprint("\tMaximum pitch set\n");
|
|
g_sounds[i].pitch_max = fabs(stof(argv(1))) * 100;
|
|
}
|
|
break;
|
|
case "offset":
|
|
if (a == 2) {
|
|
dprint("\tOffset set\n");
|
|
g_sounds[i].offset = stof(argv(1));
|
|
}
|
|
break;
|
|
case "looping":
|
|
dprint("\tSound set to loop\n");
|
|
g_sounds[i].flags |= SNDFL_LOOPING;
|
|
break;
|
|
case "nodups":
|
|
dprint("\tSound set to not play duplicate samples\n");
|
|
g_sounds[i].flags |= SNDFL_NODUPS;
|
|
break;
|
|
case "global":
|
|
dprint("\tSound set to play everywhere\n");
|
|
g_sounds[i].flags |= SNDFL_GLOBAL;
|
|
break;
|
|
case "private":
|
|
dprint("\tSound set to play privately\n");
|
|
g_sounds[i].flags |= SNDFL_PRIVATE;
|
|
break;
|
|
case "no_reverb":
|
|
dprint("\tSound set to ignore reverb\n");
|
|
g_sounds[i].flags |= SNDFL_NOREVERB;
|
|
break;
|
|
case "omnidirectional":
|
|
dprint("\tSound set to be omnidirectional\n");
|
|
g_sounds[i].flags |= SNDFL_OMNI;
|
|
break;
|
|
case "follow":
|
|
dprint("\tSound set to follow\n");
|
|
g_sounds[i].flags |= SNDFL_FOLLOW;
|
|
break;
|
|
case "footstep":
|
|
g_sounds[i].flags |= SNDFL_STEP;
|
|
break;
|
|
case "distshader":
|
|
g_sounds[i].distshader = argv(1);
|
|
break;
|
|
case "alerts":
|
|
dprint("\tSound set to alert enemy AI\n");
|
|
g_sounds[i].flags |= SNDFL_ALERTS;
|
|
break;
|
|
case "sample":
|
|
if (a == 2) {
|
|
dprint("\tAdded sample ");
|
|
dprint(argv(1));
|
|
dprint("\n");
|
|
precache_sound(argv(1));
|
|
g_sounds[i].samples = sprintf("%s%s\n", g_sounds[i].samples, argv(1));
|
|
g_sounds[i].sample_count++;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static float
|
|
Sound_GetAttenuation(int i)
|
|
{
|
|
if (g_sounds[i].dist_max == 0)
|
|
return 0;
|
|
else
|
|
return cvar("s_nominaldistance") / g_sounds[i].dist_max;
|
|
}
|
|
|
|
int
|
|
Sound_Parse(int i, string line, string shader)
|
|
{
|
|
int c;
|
|
static string t_name;
|
|
static int braced;
|
|
|
|
c = tokenize_console(line);
|
|
|
|
switch(argv(0)) {
|
|
case "{":
|
|
/* skip broken syntax */
|
|
if (braced == TRUE || t_name == "") {
|
|
break;
|
|
}
|
|
|
|
dprint("{\n");
|
|
braced = TRUE;
|
|
break;
|
|
case "}":
|
|
/* skip broken syntax */
|
|
if (braced == FALSE) {
|
|
break;
|
|
}
|
|
dprint("}\n");
|
|
braced = FALSE;
|
|
t_name = "";
|
|
return (1);
|
|
break;
|
|
default:
|
|
if (braced == TRUE) {
|
|
Sound_ParseField(i, c);
|
|
} else {
|
|
/* name/identifer of our message */
|
|
t_name = strtolower(line);
|
|
|
|
if (t_name == shader) {
|
|
/* I guess it's what we want */
|
|
dprint("Found shader ");
|
|
dprint(shader);
|
|
dprint(":\n");
|
|
g_sounds[i].name = shader;
|
|
} else {
|
|
/* not what we're looking for */
|
|
t_name = "";
|
|
}
|
|
}
|
|
}
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
Sound_Precache(string shader)
|
|
{
|
|
searchhandle sh;
|
|
filestream fh;
|
|
string line;
|
|
int index;
|
|
|
|
if (!shader)
|
|
return -1;
|
|
|
|
index = g_sounds_count;
|
|
shader = strtolower(shader);
|
|
|
|
dprint("[SOUND] Precaching sound shader ");
|
|
dprint(shader);
|
|
dprint("\n");
|
|
|
|
/* create the hash-table if it doesn't exist */
|
|
if (!g_hashsounds) {
|
|
g_hashsounds = hash_createtab(2, HASH_ADD);
|
|
}
|
|
|
|
/* check if it's already cached */
|
|
{
|
|
int cache;
|
|
cache = (int)hash_get(g_hashsounds, shader, -1);
|
|
|
|
if (cache >= 0) {
|
|
dprint(sprintf("^1Sound_Precache: shader %s already precached\n", shader));
|
|
return cache;
|
|
}
|
|
}
|
|
|
|
g_sounds_count++;
|
|
|
|
#ifdef SOUNDSHADER_DYNAMIC
|
|
g_sounds = (snd_t *)memrealloc(g_sounds, sizeof(snd_t), index, g_sounds_count);
|
|
#else
|
|
if (g_sounds_count >= SOUNDSHADER_MAX) {
|
|
error(sprintf("Sound_Precache: Reached SOUNDSHADER_MAX (%d)\n", SOUNDSHADER_MAX));
|
|
}
|
|
#endif
|
|
|
|
g_sounds[index].volume = 1.0f;
|
|
g_sounds[index].dist_max = 1000;
|
|
g_sounds[index].pitch_min = g_sounds[index].pitch_max = 100;
|
|
g_sounds[index].offset = 0;
|
|
|
|
sh = search_begin("sound/*.sndshd", TRUE, TRUE);
|
|
|
|
for (int i = 0; i < search_getsize(sh); i++) {
|
|
fh = fopen(search_getfilename(sh, i), FILE_READ);
|
|
if (fh < 0) {
|
|
continue;
|
|
}
|
|
|
|
while ((line = fgets(fh))) {
|
|
/* when we found it, quit */
|
|
if (Sound_Parse(index, line, shader) == TRUE) {
|
|
search_end(sh);
|
|
fclose(fh);
|
|
hash_add(g_hashsounds, shader, (int)index);
|
|
|
|
/* distant shader */
|
|
if (g_sounds[index].distshader) {
|
|
Sound_Precache(g_sounds[index].distshader);
|
|
}
|
|
|
|
return index;
|
|
}
|
|
}
|
|
fclose(fh);
|
|
}
|
|
|
|
dprint("^1[SOUND] No shader found for ");
|
|
dprint(shader);
|
|
dprint("\n");
|
|
|
|
search_end(sh);
|
|
return -1;
|
|
}
|
|
|
|
void
|
|
Sound_Distance(entity target, string shader)
|
|
{
|
|
int r;
|
|
float volume;
|
|
float radius;
|
|
float pitch;
|
|
int flag;
|
|
int sample;
|
|
|
|
flag = 0;
|
|
sample = (int)hash_get(g_hashsounds, shader, -1);
|
|
|
|
if (sample < 0) {
|
|
#ifdef SERVER
|
|
print(sprintf("^1Sound_Distance: shader %s is not precached (SERVER)\n", shader));
|
|
#else
|
|
print(sprintf("^1Sound_Distance: shader %s is not precached (CLIENT)\n", shader));
|
|
#endif
|
|
}
|
|
|
|
/* pick a sample */
|
|
r = floor(random(0, g_sounds[sample].sample_count));
|
|
tokenizebyseparator(g_sounds[sample].samples, "\n");
|
|
|
|
/* set pitch */
|
|
pitch = random(g_sounds[sample].pitch_min, g_sounds[sample].pitch_max);
|
|
radius = Sound_GetAttenuation(sample);
|
|
volume = g_sounds[sample].volume;
|
|
|
|
/* flags */
|
|
if (g_sounds[sample].flags & SNDFL_NOREVERB) {
|
|
flag |= SOUNDFLAG_NOREVERB;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_LOOPING) {
|
|
flag |= SOUNDFLAG_FORCELOOP;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_NODUPS) {
|
|
if (g_sounds[sample].playc >= g_sounds[sample].sample_count) {
|
|
g_sounds[sample].playc = 0;
|
|
}
|
|
r = g_sounds[sample].playc++;
|
|
}
|
|
|
|
#ifdef CLIENT
|
|
if (g_sounds[sample].flags & SNDFL_OMNI) {
|
|
flag |= SOUNDFLAG_NOSPACIALISE;
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEVELOPER
|
|
print(sprintf("Sound_Distance: %s\n", argv(r)));
|
|
#endif
|
|
|
|
sound(
|
|
target,
|
|
5,
|
|
argv(r),
|
|
volume,
|
|
ATTN_NONE,
|
|
pitch,
|
|
flag,
|
|
g_sounds[sample].offset
|
|
);
|
|
}
|
|
|
|
void
|
|
Sound_Play(entity target, int chan, string shader)
|
|
{
|
|
int r;
|
|
float volume;
|
|
float radius;
|
|
float pitch;
|
|
int flag;
|
|
int sample;
|
|
|
|
if (shader == "")
|
|
return;
|
|
|
|
flag = 0;
|
|
sample = (int)hash_get(g_hashsounds, shader, -1);
|
|
|
|
if (sample < 0) {
|
|
crossprint(sprintf("^1Sound_Play: shader %s is not precached\n", shader));
|
|
sound(target, chan, "misc/missing.wav", 1.0f, ATTN_NORM);
|
|
return;
|
|
}
|
|
|
|
/* pick a sample */
|
|
r = floor(random(0, g_sounds[sample].sample_count));
|
|
tokenizebyseparator(g_sounds[sample].samples, "\n");
|
|
|
|
/* set pitch */
|
|
pitch = random(g_sounds[sample].pitch_min, g_sounds[sample].pitch_max);
|
|
radius = Sound_GetAttenuation(sample);
|
|
volume = g_sounds[sample].volume;
|
|
|
|
/* flags */
|
|
if (g_sounds[sample].flags & SNDFL_NOREVERB) {
|
|
flag |= SOUNDFLAG_NOREVERB;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_GLOBAL) {
|
|
radius = ATTN_NONE;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_LOOPING) {
|
|
flag |= SOUNDFLAG_FORCELOOP;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_NODUPS) {
|
|
if (g_sounds[sample].playc >= g_sounds[sample].sample_count) {
|
|
g_sounds[sample].playc = 0;
|
|
}
|
|
r = g_sounds[sample].playc++;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_FOLLOW) {
|
|
flag |= SOUNDFLAG_FOLLOW;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_STEP) {
|
|
float s = vlen(target.velocity);
|
|
|
|
/*if (target.flags & FL_CROUCHING)
|
|
s *= 2.0f;*/
|
|
|
|
if (s < PMOVE_STEP_WALKSPEED) {
|
|
return;
|
|
} else if (s < PMOVE_STEP_RUNSPEED) {
|
|
volume *= 0.35f;
|
|
} else {
|
|
volume *= 0.75f;
|
|
}
|
|
}
|
|
#ifdef CLIENT
|
|
if (g_sounds[sample].flags & SNDFL_OMNI) {
|
|
flag |= SOUNDFLAG_NOSPACIALISE;
|
|
}
|
|
#else
|
|
if (g_sounds[sample].flags & SNDFL_PRIVATE) {
|
|
flag |= SOUNDFLAG_UNICAST;
|
|
msg_entity = target;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_ALERTS) {
|
|
NSMonster_AlertEnemyAlliance(target.origin, g_sounds[sample].dist_max, target.m_iAlliance);
|
|
}
|
|
#endif
|
|
|
|
#ifdef DEVELOPER
|
|
print(sprintf("Sound_Play: %s\n", argv(r)));
|
|
#endif
|
|
|
|
#ifdef SERVER
|
|
if (g_sounds[sample].shakes > 0.0) {
|
|
Client_ShakeOnce(target.origin, 512, 2.5, 1.0, 1.0f);
|
|
}
|
|
#else
|
|
if (g_sounds[sample].shakes > 0.0) {
|
|
float srad = 512;
|
|
float dist = vlen(pSeat->m_vecPredictedOrigin - target.origin);
|
|
if (dist < srad) {
|
|
float dif = 1.0 - (dist/srad);
|
|
pSeat->m_flShakeFreq = (1.0 * dif) * g_sounds[sample].shakes;
|
|
pSeat->m_flShakeAmp = (1.0 * dif) * g_sounds[sample].shakes;
|
|
pSeat->m_flShakeDuration = soundlength(argv(r));
|
|
pSeat->m_flShakeTime = pSeat->m_flShakeDuration;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
sound(
|
|
target,
|
|
chan,
|
|
argv(r),
|
|
volume,
|
|
radius,
|
|
pitch,
|
|
flag,
|
|
g_sounds[sample].offset
|
|
);
|
|
|
|
if (g_sounds[sample].distshader) {
|
|
Sound_Distance(target, g_sounds[sample].distshader);
|
|
}
|
|
}
|
|
|
|
void
|
|
Sound_PlayAt(vector pos, string shader)
|
|
{
|
|
int r;
|
|
float radius;
|
|
float pitch;
|
|
int flag;
|
|
int sample;
|
|
|
|
if (shader == "")
|
|
return;
|
|
|
|
flag = 0;
|
|
sample = (int)hash_get(g_hashsounds, shader, -1);
|
|
|
|
if (sample < 0) {
|
|
crossprint(sprintf("^1Sound_PlayAt: shader %s is not precached\n", shader));
|
|
pointsound(pos, "misc/missing.wav", 1.0f, ATTN_NORM);
|
|
return;
|
|
}
|
|
|
|
/* pick a sample */
|
|
r = floor(random(0, g_sounds[sample].sample_count));
|
|
tokenizebyseparator(g_sounds[sample].samples, "\n");
|
|
|
|
/* set pitch */
|
|
pitch = random(g_sounds[sample].pitch_min, g_sounds[sample].pitch_max);
|
|
|
|
/* flags */
|
|
if (g_sounds[sample].flags & SNDFL_NOREVERB) {
|
|
flag |= SOUNDFLAG_NOREVERB;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_GLOBAL) {
|
|
radius = 0;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_LOOPING) {
|
|
flag |= SOUNDFLAG_FORCELOOP;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_NODUPS) {
|
|
if (g_sounds[sample].playc >= g_sounds[sample].sample_count) {
|
|
g_sounds[sample].playc = 0;
|
|
}
|
|
r = g_sounds[sample].playc++;
|
|
}
|
|
#ifdef CLIENT
|
|
if (g_sounds[sample].flags & SNDFL_OMNI) {
|
|
flag |= SOUNDFLAG_NOSPACIALISE;
|
|
}
|
|
#endif
|
|
|
|
/* really? this doesn't do any more? */
|
|
pointsound(pos, argv(r), g_sounds[sample].volume, Sound_GetAttenuation(sample));
|
|
}
|
|
|
|
#ifdef CLIENT
|
|
void
|
|
Sound_PlayLocal(string shader)
|
|
{
|
|
int r;
|
|
float radius;
|
|
float pitch;
|
|
int flag;
|
|
int sample;
|
|
|
|
if (shader == "")
|
|
return;
|
|
|
|
flag = 0;
|
|
sample = (int)hash_get(g_hashsounds, shader, -1);
|
|
|
|
if (sample < 0) {
|
|
crossprint(sprintf("^1Sound_PlayLocal: shader %s is not precached\n", shader));
|
|
localsound("misc/missing.wav");
|
|
return;
|
|
}
|
|
|
|
/* pick a sample */
|
|
r = floor(random(0, g_sounds[sample].sample_count));
|
|
tokenizebyseparator(g_sounds[sample].samples, "\n");
|
|
|
|
/* really? this doesn't do any more? */
|
|
localsound(argv(r));
|
|
}
|
|
|
|
void
|
|
Sound_Update(entity target, int channel, int sample, float volume)
|
|
{
|
|
int r;
|
|
float radius;
|
|
float pitch;
|
|
int flag;
|
|
|
|
if (sample < 0) {
|
|
return;
|
|
}
|
|
|
|
/* pick a sample */
|
|
r = floor(random(0, g_sounds[sample].sample_count));
|
|
tokenizebyseparator(g_sounds[sample].samples, "\n");
|
|
|
|
/* set pitch */
|
|
pitch = random(g_sounds[sample].pitch_min, g_sounds[sample].pitch_max);
|
|
radius = Sound_GetAttenuation(sample);
|
|
flag = 0;
|
|
|
|
/* flags */
|
|
if (g_sounds[sample].flags & SNDFL_NOREVERB) {
|
|
flag |= SOUNDFLAG_NOREVERB;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_GLOBAL) {
|
|
radius = ATTN_NONE;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_LOOPING) {
|
|
flag |= SOUNDFLAG_FORCELOOP;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_NODUPS) {
|
|
if (g_sounds[sample].playc >= g_sounds[sample].sample_count) {
|
|
g_sounds[sample].playc = 0;
|
|
}
|
|
r = g_sounds[sample].playc++;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_OMNI) {
|
|
flag |= SOUNDFLAG_NOSPACIALISE;
|
|
}
|
|
|
|
soundupdate(
|
|
target,
|
|
channel,
|
|
argv(0),
|
|
g_sounds[sample].volume * volume,
|
|
radius,
|
|
pitch,
|
|
flag,
|
|
g_sounds[sample].offset
|
|
);
|
|
}
|
|
#else
|
|
void
|
|
Sound_Speak(entity target, string shader)
|
|
{
|
|
int r;
|
|
float radius;
|
|
float pitch;
|
|
int flag;
|
|
int sample;
|
|
|
|
sample = (int)hash_get(g_hashsounds, shader, -1);
|
|
|
|
if (sample < 0) {
|
|
#ifdef SERVER
|
|
print(sprintf("^1Sound_Speak: shader %s is not precached (SERVER)\n", shader));
|
|
#else
|
|
print(sprintf("^1Sound_Speak: shader %s is not precached (CLIENT)\n", shader));
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/* pick a sample */
|
|
r = floor(random(0, g_sounds[sample].sample_count));
|
|
tokenizebyseparator(g_sounds[sample].samples, "\n");
|
|
|
|
/* set pitch */
|
|
pitch = random(g_sounds[sample].pitch_min, g_sounds[sample].pitch_max);
|
|
radius = g_sounds[sample].dist_max;
|
|
flag = 0;
|
|
|
|
/* flags */
|
|
if (g_sounds[sample].flags & SNDFL_NOREVERB) {
|
|
flag |= SOUNDFLAG_NOREVERB;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_GLOBAL) {
|
|
radius = ATTN_NONE;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_LOOPING) {
|
|
flag |= SOUNDFLAG_FORCELOOP;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_NODUPS) {
|
|
if (g_sounds[sample].playc >= g_sounds[sample].sample_count) {
|
|
g_sounds[sample].playc = 0;
|
|
}
|
|
r = g_sounds[sample].playc++;
|
|
}
|
|
if (g_sounds[sample].flags & SNDFL_FOLLOW) {
|
|
flag |= SOUNDFLAG_FOLLOW;
|
|
}
|
|
|
|
if (g_sounds[sample].flags & SNDFL_PRIVATE) {
|
|
flag |= SOUNDFLAG_UNICAST;
|
|
msg_entity = target;
|
|
}
|
|
|
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
|
WriteByte(MSG_MULTICAST, EV_SPEAK);
|
|
WriteEntity(MSG_MULTICAST, target);
|
|
WriteString(MSG_MULTICAST, argv(r));
|
|
WriteFloat(MSG_MULTICAST, pitch);
|
|
msg_entity = target;
|
|
multicast(target.origin, MULTICAST_PVS);
|
|
}
|
|
#endif
|