Initial support for scripted_sentence and sentences.txt. It doesn't do anything fancy yet.

This commit is contained in:
Marco Cawthorne 2019-09-02 06:29:18 +02:00
parent 13f4044481
commit 0f3cc9c5f5
8 changed files with 261 additions and 42 deletions

View file

@ -106,6 +106,7 @@ CSQC_Init(float apilevel, string enginename, float engineversion)
DSP_Init();
CSQC_RendererRestarted("init");
Titles_Init();
Sentences_Init();
}
void
@ -443,6 +444,9 @@ CSQC_Parse_Event(void)
pit = readfloat();
sound(t, CHAN_VOICE, msg, 1.0, ATTN_NORM, pit);
break;
case EV_SENTENCE:
Sentences_Parse();
break;
case EV_FADE:
Fade_Parse();
break;

View file

@ -34,6 +34,7 @@ enum {
EV_CAMERATRIGGER,
EV_ORBITUARY,
EV_SPEAK,
EV_SENTENCE,
EV_CHAT,
EV_CHAT_TEAM,
EV_CHAT_VOX,

View file

@ -6,6 +6,7 @@ client/fade.cpp
client/sprite.cpp
client/titles.cpp
client/text.cpp
client/sentences.cpp
client/env_cubemap.cpp
client/env_glow.cpp
client/env_sound.cpp

View file

@ -29,36 +29,40 @@ vector g_vecCubePos;
void CMap_Check(void);
void CMap_Shoot(void);
class env_cubemap:CBaseEntity {
class
env_cubemap:CBaseEntity
{
int m_iSize;
void() env_cubemap;
virtual void(string, string) SpawnKey;
};
void env_cubemap::SpawnKey(string strField, string strKey)
void
env_cubemap::SpawnKey(string strField, string strKey)
{
switch (strField) {
case "scale":
m_iSize = stoi(strKey);
if (m_iSize <= 16) {
m_iSize = 16;
} else if (m_iSize <= 32) {
m_iSize = 32;
} else if (m_iSize <= 64) {
m_iSize = 64;
} else if (m_iSize <= 128) {
m_iSize = 128;
} else {
m_iSize = 256;
}
break;
default:
CBaseEntity::SpawnKey(strField, strKey);
case "scale":
m_iSize = stoi(strKey);
if (m_iSize <= 16) {
m_iSize = 16;
} else if (m_iSize <= 32) {
m_iSize = 32;
} else if (m_iSize <= 64) {
m_iSize = 64;
} else if (m_iSize <= 128) {
m_iSize = 128;
} else {
m_iSize = 256;
}
break;
default:
CBaseEntity::SpawnKey(strField, strKey);
}
}
void env_cubemap::env_cubemap(void)
void
env_cubemap::env_cubemap(void)
{
m_iSize = 32;
Init();
@ -71,30 +75,31 @@ CMap_Check
Checks whether or not a specific cubemap file was dumped, goes to the next
=================
*/
void CMap_Shoot(void)
void
CMap_Shoot(void)
{
string strReflectcube;
if ( self.owner ) {
if (self.owner) {
env_cubemap tmp = (env_cubemap) self.owner;
print( "^3Cubemap processing...\n" );
print("^3Cubemap processing...\n");
g_vecCubePos = tmp.origin;
strReflectcube = sprintf(
"env/%s_%d_%d_%d.dds",
mapname,
g_vecCubePos[0],
g_vecCubePos[1],
g_vecCubePos[2]);
"env/%s_%d_%d_%d.dds",
mapname,
g_vecCubePos[0],
g_vecCubePos[1],
g_vecCubePos[2]);
localcmd(sprintf(
"screenshot_cubemap %s %i\n",
strReflectcube,
tmp.m_iSize));
"screenshot_cubemap %s %i\n",
strReflectcube,
tmp.m_iSize));
self.think = CMap_Check;
self.nextthink = time;
} else {
print( "^2Cubemaps done...\n" );
localcmd( "mod_findcubemaps\nvid_reload\n" );
print("^2Cubemaps done...\n");
localcmd("mod_findcubemaps\nvid_reload\n");
g_iCubeProcess = FALSE;
remove( self );
remove(self);
}
}
@ -105,16 +110,19 @@ CMap_Check
Checks whether or not a specific cubemap file was dumped, goes to the next
=================
*/
void CMap_Check(void)
void
CMap_Check(void)
{
if (whichpack(sprintf(
"textures/env/%s_%d_%d_%d.dds",
mapname,
g_vecCubePos[0],
g_vecCubePos[1],
g_vecCubePos[2]))) {
self.owner = find( self.owner, classname, "env_cubemap" );
"textures/env/%s_%d_%d_%d.dds",
mapname,
g_vecCubePos[0],
g_vecCubePos[1],
g_vecCubePos[2])))
{
self.owner = find(self.owner, classname, "env_cubemap");
self.think = CMap_Shoot;
self.nextthink = time + 0.25f; /* because sigh */
}
self.nextthink = time;
}
@ -126,7 +134,8 @@ CMap_Build
Called upon 'buildcubemaps'
=================
*/
void CMap_Build(void)
void
CMap_Build(void)
{
if (g_iCubeProcess == TRUE) {
return;

View file

@ -0,0 +1,128 @@
/*
* Copyright (c) 2016-2019 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.
*/
typedef struct
{
string m_strSnd;
float m_flPitch;
} snt_t;
typedef struct
{
string m_strID;
snt_t *m_samples;
int m_count;
} sentences_t;
sentences_t *g_sentences;
int g_sentences_count;
void
Sentences_Init(void)
{
filestream fs_sentences;
string temp;
int c, i;
fs_sentences = fopen("sound/sentences.txt", FILE_READ);
if (fs_sentences < 0) {
print("^1WARNING: ^7Could NOT load sound/sentences.txt");
return;
}
if (g_sentences_count > 0) {
print("^1WARNING: ^7Attempted to load sentences twice!");
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 */
g_sentences = memrealloc(g_sentences,
sizeof(sentences_t),
g_sentences_count,
++g_sentences_count);
/* allocate sentences */
g_sentences[x].m_count = c-1;
g_sentences[x].m_samples = memalloc(sizeof(snt_t) * (c-1));
/* 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 = strcat("!", argv(0));
} else {
/* TODO: handle pitch and VOX samples */
g_sentences[x].m_samples[i-1].m_strSnd =
strcat(argv(i), ".wav");
}
}
}
for (int a=0; a < g_sentences_count; a++) {
print(sprintf("[^1SENTENCES^7] Found ^2%s\n",
g_sentences[a].m_strID));
for (int b = 0; b < g_sentences[a].m_count; b++) {
print(sprintf("\tSample ^2%s\n",
g_sentences[a].m_samples[b].m_strSnd));
}
}
}
void
Sentences_Parse(void)
{
string sentence;
entity target;
float pitch;
target = findfloat(world, entnum, readentitynum());
sentence = readstring();
pitch = readfloat();
print(sprintf("[^1SENTENCES^7] Playing %s\n", sentence));
for (int a = 0; a < g_sentences_count; a++) {
if (g_sentences[a].m_strID == sentence) {
print("Time to play...\n");
sound(world, CHAN_VOICE,
g_sentences[a].m_samples[0].m_strSnd, 1.0,
ATTN_NONE);
}
}
}

View file

@ -61,7 +61,7 @@ Titles_Init(void)
fs_titles = fopen("titles.txt", FILE_READ);
if (fs_titles < 0) {
print("^1WARNING: ^7Could NOT load titles.lst");
print("^1WARNING: ^7Could NOT load titles.txt");
return;
}

View file

@ -59,5 +59,6 @@ server/monster_furniture.cpp
server/monster_generic.cpp
server/monstermaker.cpp
server/multisource.cpp
server/scripted_sentence.cpp
server/scripted_sequence.cpp
#endlist

View file

@ -0,0 +1,75 @@
/*
* Copyright (c) 2016-2019 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.
*/
class scripted_sentence:CBaseTrigger
{
string m_strSpeaker;
string m_strSentence;
float m_flDelay;
float m_flWait;
float m_flPitch;
void() scripted_sentence;
virtual void() Trigger;
virtual void() Respawn;
};
void scripted_sentence::Trigger(void)
{
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
WriteByte(MSG_MULTICAST, EV_SENTENCE);
WriteEntity(MSG_MULTICAST, find(world, ::classname, m_strSpeaker));
WriteString(MSG_MULTICAST, m_strSentence);
WriteFloat(MSG_MULTICAST, m_flPitch);
msg_entity = this;
multicast(origin, MULTICAST_PHS);
}
/* TODO: Make this redundant */
void scripted_sentence::Respawn(void)
{
solid = SOLID_TRIGGER;
#ifdef GS_DEVELOPER
alpha = 0.5f;
#endif
}
void scripted_sentence::scripted_sentence(void)
{
for (int i = 1; i < (tokenize(__fullspawndata) - 1); i += 2) {
switch (argv(i)) {
case "entity":
m_strSpeaker = argv(i+1);
case "sentence":
m_strSentence = argv(i+1);
break;
case "pitch":
m_flPitch = stof(argv(i+1));
break;
case "delay":
m_flDelay = stof(argv(i+1));
break;
case "wait":
m_flWait = stof(argv(i+1));
break;
default:
break;
}
}
CBaseTrigger::CBaseTrigger();
scripted_sentence::Respawn();
}