diff --git a/src/client/entry.c b/src/client/entry.c index 7a6788a3..7fec22af 100644 --- a/src/client/entry.c +++ b/src/client/entry.c @@ -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; diff --git a/src/events.h b/src/events.h index d85f3d72..49ca6386 100644 --- a/src/events.h +++ b/src/events.h @@ -34,6 +34,7 @@ enum { EV_CAMERATRIGGER, EV_ORBITUARY, EV_SPEAK, + EV_SENTENCE, EV_CHAT, EV_CHAT_TEAM, EV_CHAT_VOX, diff --git a/src/gs-entbase/client.src b/src/gs-entbase/client.src index 3201ae94..80e1e94b 100644 --- a/src/gs-entbase/client.src +++ b/src/gs-entbase/client.src @@ -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 diff --git a/src/gs-entbase/client/env_cubemap.cpp b/src/gs-entbase/client/env_cubemap.cpp index 3dc7e497..6dfbb2bf 100644 --- a/src/gs-entbase/client/env_cubemap.cpp +++ b/src/gs-entbase/client/env_cubemap.cpp @@ -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; diff --git a/src/gs-entbase/client/sentences.cpp b/src/gs-entbase/client/sentences.cpp new file mode 100644 index 00000000..31f5902e --- /dev/null +++ b/src/gs-entbase/client/sentences.cpp @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2016-2019 Marco Hladik + * + * 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); + } + } +} diff --git a/src/gs-entbase/client/titles.cpp b/src/gs-entbase/client/titles.cpp index 5f358e7c..fb242bec 100644 --- a/src/gs-entbase/client/titles.cpp +++ b/src/gs-entbase/client/titles.cpp @@ -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; } diff --git a/src/gs-entbase/server.src b/src/gs-entbase/server.src index 21ad8042..b22496bd 100644 --- a/src/gs-entbase/server.src +++ b/src/gs-entbase/server.src @@ -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 diff --git a/src/gs-entbase/server/scripted_sentence.cpp b/src/gs-entbase/server/scripted_sentence.cpp new file mode 100644 index 00000000..9ee751ec --- /dev/null +++ b/src/gs-entbase/server/scripted_sentence.cpp @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2016-2019 Marco Hladik + * + * 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(); +}