190 lines
5.0 KiB
Plaintext
190 lines
5.0 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.
|
|
*/
|
|
|
|
/* voice sentence samples for AI and other triggers that are supposed to talk.
|
|
* the formatting is messy as hell and I feel dirty for even bothering with all
|
|
* this to begin with.
|
|
*
|
|
* the server will send a short string identifer over and we'll look it up.
|
|
* what's annoying is that some NPCs got their own pitch overrides so I guess
|
|
* we'll just default to those whenever there's no custom value set.
|
|
*/
|
|
|
|
/* sentences are the voice-acting backbone of the sound system.
|
|
* http://articles.thewavelength.net/230/
|
|
* has pretty good documentation of how the format is meant to work */
|
|
|
|
/* Sentences Documentation
|
|
|
|
Each line is a new sentence group.
|
|
[GROUPNAME] [...PARAMETERS] [...SAMPLES]
|
|
|
|
If a sample is not in a sub-directory, it'll be assumed to be part
|
|
of the 'vox' sub-directory, or the last valid path of a previous sample.
|
|
For example
|
|
attention male/hello how are you
|
|
becomes
|
|
vox/attention.wav male/hello.wav male/how.wav male/are.wav male/you.wav
|
|
|
|
When parameters are surrounded by spaces, this means they apply
|
|
to all current samples. They can be overwritten later down the parsing.
|
|
When a parameter is attached to a sample, e.g.
|
|
attention(p120)
|
|
Then this parameter only applies to said keyword.
|
|
Whereas...
|
|
(p120) attention everyone alive
|
|
Will apply the pitch effect to all three succeeding samples.
|
|
|
|
Parameters:
|
|
(pXX) = Pitch. Valid values are from 50 to 150.
|
|
(vXX) = Volume. Valid values are from 0 to 100.
|
|
(sXX) = Start point in %. E.g. 10 skips the first 10% of the sample.
|
|
(eXX) = End point in %. E.g. 75 ends playback 75% into the sample.
|
|
(tXX) = Time shift/compression in %. 100 is unaltered speed,
|
|
wheras 50 plays the sample back in half the time.
|
|
*/
|
|
|
|
/* enable this if you want to use memalloc */
|
|
#define DYNAMIC_SENTENCES
|
|
|
|
typedef struct
|
|
{
|
|
string m_strID;
|
|
string m_strSamples;
|
|
} sentences_t;
|
|
|
|
#ifdef DYNAMIC_SENTENCES
|
|
sentences_t *g_sentences;
|
|
int g_sentences_count;
|
|
#else
|
|
#define SENTENCES_LIMIT 1024
|
|
sentences_t g_sentences[SENTENCES_LIMIT];
|
|
int g_sentences_count;
|
|
#endif
|
|
|
|
var string g_sentences_samplepath;
|
|
|
|
void
|
|
Sentences_Shutdown(void)
|
|
{
|
|
#ifdef DYNAMIC_SENTENCES
|
|
if (g_sentences)
|
|
memfree(g_sentences);
|
|
#endif
|
|
|
|
g_sentences_count = 0;
|
|
}
|
|
|
|
void
|
|
Sentences_Init(void)
|
|
{
|
|
filestream fs_sentences;
|
|
string temp;
|
|
int c, i;
|
|
|
|
Sentences_Shutdown();
|
|
|
|
fs_sentences = fopen("sound/sentences.txt", FILE_READ);
|
|
|
|
if (fs_sentences < 0) {
|
|
print("^1WARNING: ^7Could NOT load sound/sentences.txt\n");
|
|
return;
|
|
}
|
|
|
|
while ((temp = fgets(fs_sentences))) {
|
|
/* tons of comments/garbage in those files,
|
|
* so tokenize appropriately */
|
|
c = tokenize_console(temp);
|
|
|
|
/* not enough for an entry. */
|
|
if (c < 2) {
|
|
continue;
|
|
}
|
|
|
|
/* starts of at 0, for every line increases */
|
|
int x = g_sentences_count;
|
|
|
|
/* allocate memory and increase count */
|
|
#ifdef DYNAMIC_SENTENCES
|
|
g_sentences_count++;
|
|
g_sentences = (sentences_t *)memrealloc(g_sentences,
|
|
sizeof(sentences_t),
|
|
x,
|
|
g_sentences_count);
|
|
#else
|
|
if (g_sentences_count + 1 >= SENTENCES_LIMIT) {
|
|
print("^1WARNING: ^7Reached limit of max sentences!\n");
|
|
return;
|
|
}
|
|
|
|
g_sentences_count++;
|
|
#endif
|
|
|
|
/* loop through the parts of the line */
|
|
for (i=0; i < c; i++) {
|
|
/* first entry is the id, prefix with ! as well */
|
|
if (i==0) {
|
|
g_sentences[x].m_strID = strtoupper(strcat("!", argv(0)));
|
|
} else {
|
|
if (i == 1) {
|
|
g_sentences[x].m_strSamples = sprintf("%s", argv(i));
|
|
} else {
|
|
g_sentences[x].m_strSamples = sprintf("%s %s", g_sentences[x].m_strSamples, argv(i));
|
|
}
|
|
|
|
g_sentences[x].m_strSamples =
|
|
strreplace(",", " vox/comma.wav", g_sentences[x].m_strSamples);
|
|
}
|
|
}
|
|
}
|
|
|
|
fclose(fs_sentences);
|
|
}
|
|
|
|
string
|
|
Sentences_ProcessSample(string sample)
|
|
{
|
|
int c = tokenizebyseparator(sample, "/");
|
|
|
|
/* this sample contains a path */
|
|
if (c > 1) {
|
|
g_sentences_samplepath = argv(0);
|
|
return sample;
|
|
}
|
|
|
|
/* we don't have a path, so supply the default */
|
|
return sprintf("%s/%s", g_sentences_samplepath, argv(0));
|
|
}
|
|
|
|
void
|
|
Sentences_ResetSample(void)
|
|
{
|
|
g_sentences_samplepath = "vox";
|
|
}
|
|
|
|
string
|
|
Sentences_GetSamples(string msg)
|
|
{
|
|
Sentences_ResetSample();
|
|
|
|
for (int i = 0; i < g_sentences_count; i++) {
|
|
if (g_sentences[i].m_strID == msg) {
|
|
return g_sentences[i].m_strSamples;
|
|
}
|
|
}
|
|
return ("");
|
|
}
|