From db4ed5ac3d1365b7f436d38be60243436af98e69 Mon Sep 17 00:00:00 2001 From: Marco Hladik Date: Thu, 26 Mar 2020 11:24:33 +0100 Subject: [PATCH] Added initial support for basic sentences.txt on monsters. --- src/client/entities.c | 6 +- src/client/entry.c | 2 +- src/client/npc.c | 179 +++++++++++++----- src/client/sentences.c | 88 +++++---- src/client/sound.c | 12 +- src/client/valve/progs.src | 2 +- src/gs-entbase/server.src | 1 + src/gs-entbase/server/basemonster.cpp | 12 +- src/gs-entbase/server/basenpc.cpp | 87 +++++++++ src/server/entry.c | 3 +- src/server/gearbox/progs.src | 1 + src/server/hunger/progs.src | 1 + src/server/poke646/progs.src | 1 + src/server/rewolf/progs.src | 1 + src/server/scihunt/monster_scientist.cpp | 2 +- src/server/scihunt/progs.src | 1 + src/server/sentences.c | 117 ++++++++++++ src/server/tfc/progs.src | 1 + src/server/valve/monster_alien_controller.cpp | 6 +- src/server/valve/monster_alien_grunt.cpp | 6 +- src/server/valve/monster_alien_slave.cpp | 4 +- src/server/valve/monster_barney.cpp | 93 +++------ src/server/valve/monster_headcrab.cpp | 6 +- src/server/valve/monster_houndeye.cpp | 6 +- src/server/valve/monster_ichthyosaur.cpp | 6 +- src/server/valve/monster_scientist.cpp | 2 +- src/server/valve/monster_tentacle.cpp | 2 +- src/server/valve/monster_zombie.cpp | 6 +- src/server/valve/progs.src | 1 + 29 files changed, 458 insertions(+), 197 deletions(-) create mode 100644 src/gs-entbase/server/basenpc.cpp create mode 100644 src/server/sentences.c diff --git a/src/client/entities.c b/src/client/entities.c index d225fd87..348ea17c 100644 --- a/src/client/entities.c +++ b/src/client/entities.c @@ -38,7 +38,11 @@ void CSQC_Ent_Update(float new) Player_ReadEntity(new); break; case ENT_NPC: - NPC_ReadEntity(new); + CBaseNPC n = (CBaseNPC)self; + if (new) { + spawnfunc_CBaseNPC(); + } + n.ReadEntity(readshort()); break; case ENT_SPRITE: Sprite_Animated(); diff --git a/src/client/entry.c b/src/client/entry.c index dc0e3f24..5a64afa4 100644 --- a/src/client/entry.c +++ b/src/client/entry.c @@ -461,7 +461,7 @@ CSQC_Parse_Event(void) sound(t, CHAN_VOICE, msg, 1.0, ATTN_NORM, pit); break; case EV_SENTENCE: - Sentences_Parse(); + CBaseNPC_ParseSentence(); break; case EV_FADE: Fade_Parse(); diff --git a/src/client/npc.c b/src/client/npc.c index 40dd4f47..0b889fe6 100644 --- a/src/client/npc.c +++ b/src/client/npc.c @@ -14,16 +14,31 @@ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -class monster_npc +class CBaseNPC { + float m_flSentenceTime; + sound_t *m_pSentenceQue; + int m_iSentenceCount; + int m_iSentencePos; + int body; float frame_last; virtual float() predraw; + virtual void(string) Speak; + virtual void(string) Sentence; + virtual void() ProcessWordQue; + virtual void(float flChanged) ReadEntity; }; +void +CBaseNPC::Speak(string msg) +{ + sound(this, CHAN_VOICE, msg, 1.0, ATTN_NORM); +} + float -monster_npc::predraw(void) +CBaseNPC::predraw(void) { if (lerpfrac > 0) { lerpfrac -= frametime * 5; @@ -44,59 +59,131 @@ monster_npc::predraw(void) frame1time += clframetime; bonecontrol5 = getchannellevel(this, CHAN_VOICE) * 20; + ProcessWordQue(); + addentity(this); return PREDRAW_NEXT; } void -NPC_ReadEntity(float new) +CBaseNPC::ProcessWordQue(void) { - float fl; - monster_npc pl = (monster_npc)self; - - if (new) { - spawnfunc_monster_npc(); - pl.classname = "npc"; - pl.solid = SOLID_SLIDEBOX; - pl.movetype = MOVETYPE_NONE; - pl.drawmask = MASK_ENGINE; - pl.customphysics = Empty; - setsize(pl, VEC_HULL_MIN + [0,0,36], VEC_HULL_MAX + [0,0,36]); + if (time < 2 || !m_iSentenceCount) { + return; } - fl = readshort(); - - if (fl & NPC_MODELINDEX) - pl.modelindex = readshort(); - if (fl & NPC_ORIGIN_X) - pl.origin[0] = readcoord(); - if (fl & NPC_ORIGIN_Y) - pl.origin[1] = readcoord(); - if (fl & NPC_ORIGIN_Z) - pl.origin[2] = readcoord(); - if (fl & NPC_ANGLES_X) - pl.angles[0] = readfloat(); - if (fl & NPC_ANGLES_Y) - pl.angles[1] = readfloat(); - if (fl & NPC_ANGLES_Z) - pl.angles[2] = readfloat(); - if (fl & NPC_VELOCITY_X) - pl.velocity[0] = readcoord(); - if (fl & NPC_VELOCITY_Y) - pl.velocity[1] = readcoord(); - if (fl & NPC_VELOCITY_Z) - pl.velocity[2] = readcoord(); - if (fl & NPC_FRAME) - pl.frame = readbyte(); - if (fl & NPC_SKIN) - pl.skin = readbyte(); - if (fl & NPC_BODY) - pl.body = readbyte(); - - if (new || (fl & NPC_BODY)) { - setcustomskin(pl, "", sprintf("geomset 1 %i\n", pl.body)); + if (m_flSentenceTime > time) { + return; } + Speak(m_pSentenceQue[m_iSentencePos].m_strSnd); + dprint(sprintf("^2CBaseNPC: Speaking %s\n", m_pSentenceQue[m_iSentencePos].m_strSnd)); + m_iSentencePos++; - setorigin(pl, pl.origin); + if (m_iSentenceCount == m_iSentenceCount) { + memfree(m_pSentenceQue); + m_iSentenceCount = 0; + m_iSentencePos = 0; + m_pSentenceQue = 0; + } else { + m_flSentenceTime = time + m_pSentenceQue[m_iSentenceCount - 1].len; + } } +/* we'll pass it a sentences.txt word (e.g. !BA_TEST) and start queing it */ +void +CBaseNPC::Sentence(string msg) +{ + /* not defined */ + if (msg == "") { + return; + } + + if (m_iSentenceCount) { + dprint(sprintf("^1CBaseNPC::Sentence: Freeing que for new sentence\n", m_iSentenceCount)); + memfree(m_pSentenceQue); + m_iSentenceCount = 0; + m_pSentenceQue = 0; + m_iSentencePos = 0; + } + + m_iSentenceCount = tokenize(Sentences_GetSamples(msg)); + dprint(sprintf("^2CBaseNPC::Sentence: Speaking %i word/s\n", m_iSentenceCount)); + m_pSentenceQue = memalloc(sizeof(sound_t) * m_iSentenceCount); + + for (int i = 0; i < m_iSentenceCount; i++) { + dprint(sprintf("^2CBaseNPC::Sentence: Constructing... %s\n", m_pSentenceQue[i].m_strSnd)); + m_pSentenceQue[i].m_strSnd = sprintf("%s.wav", argv(i)); + m_pSentenceQue[i].len = soundlength(m_pSentenceQue[i].m_strSnd); + m_pSentenceQue[i].m_flPitch = 100; + } + m_flSentenceTime = time; +} + +void +CBaseNPC::ReadEntity(float fl) +{ + if (fl & NPC_MODELINDEX) + modelindex = readshort(); + if (fl & NPC_ORIGIN_X) + origin[0] = readcoord(); + if (fl & NPC_ORIGIN_Y) + origin[1] = readcoord(); + if (fl & NPC_ORIGIN_Z) + origin[2] = readcoord(); + if (fl & NPC_ANGLES_X) + angles[0] = readfloat(); + if (fl & NPC_ANGLES_Y) + angles[1] = readfloat(); + if (fl & NPC_ANGLES_Z) + angles[2] = readfloat(); + if (fl & NPC_VELOCITY_X) + velocity[0] = readcoord(); + if (fl & NPC_VELOCITY_Y) + velocity[1] = readcoord(); + if (fl & NPC_VELOCITY_Z) + velocity[2] = readcoord(); + if (fl & NPC_FRAME) + frame = readbyte(); + if (fl & NPC_SKIN) + skin = readbyte(); + + if (fl & NPC_BODY) { + body = readbyte(); + setcustomskin(this, "", sprintf("geomset 1 %i\n", body)); + } + + setorigin(this, origin); +} + +void +CBaseNPC::CBaseNPC(void) +{ + solid = SOLID_SLIDEBOX; + movetype = MOVETYPE_NONE; + drawmask = MASK_ENGINE; + customphysics = Empty; + setsize(this, VEC_HULL_MIN + [0,0,36], VEC_HULL_MAX + [0,0,36]); +} + +/* our EV_SENTENCE event */ +void +CBaseNPC_ParseSentence(void) +{ + entity ent; + CBaseNPC targ; + string sentence; + float e; + + /* parse packets */ + e = readentitynum(); + sentence = readstring(); + + ent = findfloat(world, entnum, e); + + if (ent) { + targ = (CBaseNPC)ent; + targ.Sentence(sentence); + } else { + dprint(sprintf("^1CBaseNPC_ParseSentence: Entity %d not in PVS\n", e)); + } +} diff --git a/src/client/sentences.c b/src/client/sentences.c index 1074fcad..5c57bacd 100644 --- a/src/client/sentences.c +++ b/src/client/sentences.c @@ -23,21 +23,35 @@ * we'll just default to those whenever there's no custom value set. */ -typedef struct -{ - string m_strSnd; - float m_flPitch; -} snt_t; +#define DYNAMIC_SENTENCES 0 typedef struct { string m_strID; - snt_t *m_samples; - int m_count; + string m_strSamples; } sentences_t; -sentences_t *g_sentences; -int g_sentences_count; +#ifdef DYNAMIC_SENTENCES + sentences_t *g_sentences; + int g_sentences_count; +#else + sentences_t g_sentences[1024]; + int g_sentences_count; +#endif + +string g_sentences_path; + +void +Sentences_Path(string word) +{ + int c = tokenizebyseparator(word, "/"); + + if (c > 1) { + g_sentences_path = sprintf("%s/", argv(0)); + } else { + g_sentences_path = ""; + } +} void Sentences_Init(void) @@ -71,15 +85,18 @@ Sentences_Init(void) /* starts of at 0, for every line increases */ int x = g_sentences_count; + /* default path is vox */ + g_sentences_path = "vox/"; + /* allocate memory and increase count */ +#ifdef DYNAMIC_SENTENCES 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)); +#else + g_sentences_count++; +#endif /* loop through the parts of the line */ for (i=0; i < c; i++) { @@ -87,41 +104,28 @@ Sentences_Init(void) 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"); + /* check whether or not our keyword contains a path */ + Sentences_Path(argv(i)); + //Sentencens_Pitch(argv(i)); + g_sentences[x].m_strSamples = + sprintf( + "%s %s%s", + g_sentences[x].m_strSamples, + g_sentences_path, + argv(i) + ); } } + print(sprintf("%s\n", g_sentences[x].m_strSamples)); } - - /*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 +Sentences_GetSamples(string msg) { - 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) { - sound(world, CHAN_VOICE, - g_sentences[a].m_samples[0].m_strSnd, 1.0, - ATTN_NONE); + for (int i = 0; i < g_sentences_count; i++) { + if (g_sentences[i].m_strID == msg) { + return g_sentences[i].m_strSamples; } } } diff --git a/src/client/sound.c b/src/client/sound.c index 1c855df3..f3c8b006 100644 --- a/src/client/sound.c +++ b/src/client/sound.c @@ -53,8 +53,10 @@ void Sound_ParseLoopingEntity(entity sndent, float isNew) } -typedef struct { - string sample; +typedef struct +{ + string m_strSnd; + float m_flPitch; float len; } sound_t; @@ -70,8 +72,8 @@ void Sound_PlayVOX(string msg) g_voxque = memalloc(sizeof(sound_t) * g_voxcount); for (int i = 0; i < g_voxcount; i++) { - g_voxque[i].sample = sprintf("vox/%s.wav", argv(i)); - g_voxque[i].len = soundlength(g_voxque[i].sample); + g_voxque[i].m_strSnd = sprintf("vox/%s.wav", argv(i)); + g_voxque[i].len = soundlength(g_voxque[i].m_strSnd); } g_voxtime = time; } @@ -83,7 +85,7 @@ void Sound_ProcessWordQue(void) } if (g_voxtime < time) { - localcmd(sprintf("play %s\n", g_voxque[g_voxpos].sample)); + localcmd(sprintf("play %s\n", g_voxque[g_voxpos].m_strSnd)); g_voxpos++; if (g_voxpos == g_voxcount) { diff --git a/src/client/valve/progs.src b/src/client/valve/progs.src index 019a8a19..e9a8db58 100755 --- a/src/client/valve/progs.src +++ b/src/client/valve/progs.src @@ -24,12 +24,12 @@ init.c ../sprite.cpp ../titles.c ../text.c -../sentences.c ../../gs-entbase/client.src ../sky.c ../sound.c +../sentences.c ../prints.c ../voice.c diff --git a/src/gs-entbase/server.src b/src/gs-entbase/server.src index 89c2714a..d67c15f6 100644 --- a/src/gs-entbase/server.src +++ b/src/gs-entbase/server.src @@ -8,6 +8,7 @@ server/defs.h server/baseentity.cpp server/basetrigger.cpp server/basemonster.cpp +server/basenpc.cpp server/basephysics.cpp server/ambient_generic.cpp server/cycler.cpp diff --git a/src/gs-entbase/server/basemonster.cpp b/src/gs-entbase/server/basemonster.cpp index ce05d0e5..7a7cac4b 100644 --- a/src/gs-entbase/server/basemonster.cpp +++ b/src/gs-entbase/server/basemonster.cpp @@ -43,20 +43,14 @@ class CBaseMonster:CBaseEntity virtual void() Physics; virtual void() IdleNoise; virtual void() Gib; - virtual void(string) Speak; + virtual void(string) Sound; virtual float(entity, float) SendEntity; virtual void() ParentUpdate; }; -void CBaseMonster::Speak(string msg) +void CBaseMonster::Sound(string msg) { - WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); - WriteByte(MSG_MULTICAST, EV_SPEAK); - WriteEntity(MSG_MULTICAST, this); - WriteString(MSG_MULTICAST, msg); - WriteFloat(MSG_MULTICAST, m_flPitch); - msg_entity = this; - multicast(origin, MULTICAST_PVS); + sound(this, CHAN_VOICE, msg, 1.0, ATTN_NORM); } float CBaseMonster::SendEntity(entity ePEnt, float fChanged) diff --git a/src/gs-entbase/server/basenpc.cpp b/src/gs-entbase/server/basenpc.cpp new file mode 100644 index 00000000..71e90296 --- /dev/null +++ b/src/gs-entbase/server/basenpc.cpp @@ -0,0 +1,87 @@ +/* + * 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. + */ + +/* NPCs are more advanced than regular monsters in that they express emotions + * and are able to interact more with the environment */ + +class CBaseNPC:CBaseMonster +{ + /* our NPCs can have a unique pitch to their voice */ + float m_flPitch; + + /* sentences identifiers */ + string m_talkAnswer; /* random answer to whenever a question is asked */ + string m_talkAsk; /* asks a random generic question */ + string m_talkAllyShot; /* asks to not shoot an ally further */ + string m_talkGreet; /* greet other NPCs */ + string m_talkIdle; /* idle chatter */ + string m_talkSmelling; /* is something smelling bad? */ + string m_talkStare; /* when NPC is being stared at */ + string m_talkSurvived; /* we're near death */ + string m_talkWounded; /* we've sustained light wounds */ + + /* things that NPCs will only say to the player */ + string m_talkPlayerAsk; /* ask player some question */ + string m_talkPlayerGreet; /* say hello to the player */ + string m_talkPlayerIdle; /* idle chatter with the player */ + string m_talkPlayerWounded1; /* slightly wounded player comment */ + string m_talkPlayerWounded2; /* a bit worse */ + string m_talkPlayerWounded3; /* yup, got thus far */ + string m_talkUnfollow; /* when the player asks us to stop following */ + string m_talkFollow; /* whenever player asks the NPC to follow */ + string m_talkStopFollow; /* we have to stop following */ + + void() CBaseNPC; + virtual void(string) Speak; + virtual void(string) Sentence; +}; + +void +CBaseNPC::Sentence(string sentence) +{ + string seq = Sentences_GetSamples(sentence); + + print(sprintf("^2CBaseNPC::Sentence: Attempting %s\n", seq)); + + if (seq == "") { + return; + } + + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_SENTENCE); + WriteEntity(MSG_MULTICAST, this); + WriteString(MSG_MULTICAST, Sentences_GetSamples(sentence)); + msg_entity = this; + multicast(origin, MULTICAST_PVS); +} + +void +CBaseNPC::Speak(string sentence) +{ + WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET); + WriteByte(MSG_MULTICAST, EV_SPEAK); + WriteEntity(MSG_MULTICAST, this); + WriteString(MSG_MULTICAST, sentence); + WriteFloat(MSG_MULTICAST, m_flPitch); + msg_entity = this; + multicast(origin, MULTICAST_PVS); +} + +void +CBaseNPC::CBaseNPC(void) +{ + +} diff --git a/src/server/entry.c b/src/server/entry.c index 617a68e9..e3ffe70d 100644 --- a/src/server/entry.c +++ b/src/server/entry.c @@ -206,7 +206,8 @@ void initents(void) Game_Worldspawn(); Decals_Init(); - + Sentences_Init(); + entity respawntimer = spawn(); respawntimer.think = init_respawn; respawntimer.nextthink = time + 0.1f; diff --git a/src/server/gearbox/progs.src b/src/server/gearbox/progs.src index 9abcba10..01dbf05a 100755 --- a/src/server/gearbox/progs.src +++ b/src/server/gearbox/progs.src @@ -19,6 +19,7 @@ ../logging.c ../nodes.c ../skill.c +../sentences.c ../../gs-entbase/server.src ../valve/monster_apache.cpp diff --git a/src/server/hunger/progs.src b/src/server/hunger/progs.src index efd6355c..6eadfcde 100755 --- a/src/server/hunger/progs.src +++ b/src/server/hunger/progs.src @@ -19,6 +19,7 @@ ../logging.c ../nodes.c ../skill.c +../sentences.c ../../gs-entbase/server.src ../valve/monster_apache.cpp diff --git a/src/server/poke646/progs.src b/src/server/poke646/progs.src index 293569fb..3bad219f 100755 --- a/src/server/poke646/progs.src +++ b/src/server/poke646/progs.src @@ -19,6 +19,7 @@ ../logging.c ../nodes.c ../skill.c +../sentences.c ../../gs-entbase/server.src ../valve/monster_rat.cpp ../valve/monster_scientist.cpp diff --git a/src/server/rewolf/progs.src b/src/server/rewolf/progs.src index a60a5d82..f5c50ffc 100755 --- a/src/server/rewolf/progs.src +++ b/src/server/rewolf/progs.src @@ -18,6 +18,7 @@ ../logging.c ../nodes.c ../skill.c +../sentences.c ../../gs-entbase/server.src ../valve/monster_rat.cpp ../valve/monster_scientist_dead.cpp diff --git a/src/server/scihunt/monster_scientist.cpp b/src/server/scihunt/monster_scientist.cpp index 4b532c6b..39019ffa 100644 --- a/src/server/scihunt/monster_scientist.cpp +++ b/src/server/scihunt/monster_scientist.cpp @@ -299,7 +299,7 @@ string sci_sndidle[] = { "scientist/hopenominal.wav", }; -class monster_scientist:CBaseMonster +class monster_scientist:CBaseNPC { vector m_vecLastUserPos; entity m_eUser; diff --git a/src/server/scihunt/progs.src b/src/server/scihunt/progs.src index dbca30b0..95b13b30 100755 --- a/src/server/scihunt/progs.src +++ b/src/server/scihunt/progs.src @@ -20,6 +20,7 @@ ../logging.c ../nodes.c ../skill.c +../sentences.c ../../gs-entbase/server.src diff --git a/src/server/sentences.c b/src/server/sentences.c new file mode 100644 index 00000000..bf5ae96a --- /dev/null +++ b/src/server/sentences.c @@ -0,0 +1,117 @@ +/* + * 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; + float len; +} sound_t; + +string *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(string), + g_sentences_count, + ++g_sentences_count); + + g_sentences[x] = strcat("!", argv(0)); + } +} + +string +Sentences_GetSamples(string word) +{ + int len; + int gc; + + /* you never know what NPCs might do */ + if (word == "") { + print("^1ERROR: No sentence supplied.\n"); + return ""; + } + + /* check if the word is present at all */ + for (int i = 0; i < g_sentences_count; i++) { + if (g_sentences[i] == word) { + print(sprintf("^2Sentences: Found %s\n", word)); + return word; + } + } + + /* it may be a random group of words. */ + len = strlen(word); + for (int i = 0; i < g_sentences_count; i++) { + string sub = substring(g_sentences[i], 0, len); + if (sub == word) { + gc++; + } + } + + /* if we've got one, choose a random sample of them */ + if (gc) { + int r = floor(random(0, gc)); + print(sprintf("^2Sentences: Choosing %s%i\n", word, r)); + return sprintf("%s%i", word, r); + } + + /* we've somehow messed up catastrophically */ + print(sprintf("^1ERROR: Invalid sentence keyword %s\n", word)); + return ""; +} diff --git a/src/server/tfc/progs.src b/src/server/tfc/progs.src index 7c016b51..d03e3145 100755 --- a/src/server/tfc/progs.src +++ b/src/server/tfc/progs.src @@ -18,6 +18,7 @@ ../logging.c ../nodes.c ../skill.c +../sentences.c ../../gs-entbase/server.src ../../shared/decals.c ../../shared/effects.c diff --git a/src/server/valve/monster_alien_controller.cpp b/src/server/valve/monster_alien_controller.cpp index 51a99836..3a02293e 100644 --- a/src/server/valve/monster_alien_controller.cpp +++ b/src/server/valve/monster_alien_controller.cpp @@ -95,7 +95,7 @@ monster_alien_controller::Pain(int iHitBody) } int rand = floor(random(0,con_sndpain.length)); - Speak(con_sndpain[rand]); + Sound(con_sndpain[rand]); frame = CON_FLINCH + floor(random(0, 2)); m_flPainTime = time + 0.25f; } @@ -109,7 +109,7 @@ monster_alien_controller::Death(int iHitBody) /* the sound */ int rand = floor(random(0,con_sndpain.length)); - Speak(con_sndpain[rand]); + Sound(con_sndpain[rand]); } /* set the functional differences */ @@ -130,7 +130,7 @@ monster_alien_controller::IdleNoise(void) m_flIdleTime = time + 2.0f + random(0,5); int rand = floor(random(0, con_sndidle.length)); - Speak(con_sndidle[rand]); + Sound(con_sndidle[rand]); } void diff --git a/src/server/valve/monster_alien_grunt.cpp b/src/server/valve/monster_alien_grunt.cpp index 7c4a604e..b1b77d92 100644 --- a/src/server/valve/monster_alien_grunt.cpp +++ b/src/server/valve/monster_alien_grunt.cpp @@ -112,7 +112,7 @@ monster_alien_grunt::Pain(int iHitBody) } int rand = floor(random(0,ag_sndpain.length)); - Speak(ag_sndpain[rand]); + Sound(ag_sndpain[rand]); frame = AG_FLINCH + floor(random(0, 2)); m_flPainTime = time + 0.25f; } @@ -135,7 +135,7 @@ monster_alien_grunt::Death(int iHitBody) /* the sound */ int rand = floor(random(0,ag_sndpain.length)); - Speak(ag_sndpain[rand]); + Sound(ag_sndpain[rand]); } /* set the functional differences */ @@ -156,7 +156,7 @@ monster_alien_grunt::IdleNoise(void) m_flIdleTime = time + 2.0f + random(0,5); int rand = floor(random(0, ag_sndidle.length)); - Speak(ag_sndidle[rand]); + Sound(ag_sndidle[rand]); } void diff --git a/src/server/valve/monster_alien_slave.cpp b/src/server/valve/monster_alien_slave.cpp index 78dd0107..6366aaac 100644 --- a/src/server/valve/monster_alien_slave.cpp +++ b/src/server/valve/monster_alien_slave.cpp @@ -87,7 +87,7 @@ monster_alien_slave::Pain(int iHitBody) } int rand = floor(random(0,slv_sndpain.length)); - Speak(slv_sndpain[rand]); + Sound(slv_sndpain[rand]); frame = SLV_FLINCH + floor(random(0, 2)); m_flPainTime = time + 0.25f; } @@ -110,7 +110,7 @@ monster_alien_slave::Death(int iHitBody) /* the sound */ int rand = floor(random(0,slv_sndpain.length)); - Speak(slv_sndpain[rand]); + Sound(slv_sndpain[rand]); } /* set the functional differences */ diff --git a/src/server/valve/monster_barney.cpp b/src/server/valve/monster_barney.cpp index 409d6d7c..80a8435e 100644 --- a/src/server/valve/monster_barney.cpp +++ b/src/server/valve/monster_barney.cpp @@ -62,61 +62,19 @@ string barney_snddie[] = { "barney/ba_die2.wav" }; -string barney_sndchitchat[] = { - "barney/ba_pain1.wav", - "barney/ba_pain1.wav", - "barney/ba_pain1.wav" -}; - -string barney_sndhear[] = { - "barney/ba_pain1.wav", - "barney/ba_pain1.wav", - "barney/ba_pain1.wav" -}; - string barney_sndpain[] = { "barney/ba_pain1.wav", "barney/ba_pain1.wav", "barney/ba_pain1.wav" }; -string barney_sndsee[] = { - "barney/ba_pain1.wav", - "barney/ba_pain1.wav", - "barney/ba_pain1.wav" -}; - string barney_sndscream[] = { "barney/ba_pain1.wav", "barney/ba_pain1.wav", "barney/ba_pain1.wav" }; -string barney_sndstop[] = { - "barney/ba_pain1.wav", - "barney/ba_pain1.wav", - "barney/ba_pain1.wav" -}; - -string barney_snduse[] = { - "barney/ba_pain1.wav", - "barney/ba_pain1.wav", - "barney/ba_pain1.wav" -}; - -string barney_snduseno[] = { - "barney/ba_pain1.wav", - "barney/ba_pain1.wav", - "barney/ba_pain1.wav" -}; - -string barney_sndidle[] = { - "barney/ba_pain1.wav", - "barney/ba_pain1.wav", - "barney/ba_pain1.wav" -}; - -class monster_barney:CBaseMonster +class monster_barney:CBaseNPC { vector m_vecLastUserPos; entity m_eUser; @@ -173,8 +131,7 @@ void monster_barney::IdleChat(void) return; } - int rand = floor(random(0,barney_sndchitchat.length)); - Speak(barney_sndchitchat[rand]); + Sentence(m_talkPlayerIdle); m_flScreamTime = time + 5.0f + random(0,20); } @@ -196,11 +153,7 @@ void monster_barney::Physics(void) continue; } - if (random() < 0.5) { - int rand = floor(random(0,barney_sndsee.length)); - Speak(barney_sndsee[rand]); - } - + Sentence(m_talkPlayerGreet); m_iFlags |= BARNF_SEEN; break; } @@ -347,16 +300,12 @@ void monster_barney::PlayerUse(void) m_iFlags |= BARNF_USED; } - r = floor(random(0,barney_snduse.length)); - Speak(barney_snduse[r]); - + Sentence(m_talkFollow); m_eUser = eActivator; m_eRescuer = m_eUser; m_vecLastUserPos = m_eUser.origin; } else { - r = floor(random(0,barney_snduseno.length)); - Speak(barney_snduseno[r]); - + Sentence(m_talkUnfollow); m_eUser = world; } } @@ -459,18 +408,26 @@ void monster_barney::monster_barney(void) for (int i = 0; i < barney_sndscream.length; i++) { precache_sound(barney_sndscream[i]); } - for (int i = 0; i < barney_snduse.length; i++) { - precache_sound(barney_snduse[i]); - } - for (int i = 0; i < barney_snduseno.length; i++) { - precache_sound(barney_snduseno[i]); - } - for (int i = 0; i < barney_sndsee.length; i++) { - precache_sound(barney_sndsee[i]); - } - for (int i = 0; i < barney_sndidle.length; i++) { - precache_sound(barney_sndidle[i]); - } + + m_talkAnswer = "!BA_ANSWER"; + m_talkAsk = ""; + m_talkAllyShot = "!BA_SCARED"; + m_talkGreet = ""; + m_talkIdle = ""; + m_talkSmelling = "!BA_SMELL"; + m_talkStare = "!BA_STARE"; + m_talkSurvived = "!BA_ANSWER"; + m_talkWounded = "!BA_WOUND"; + + m_talkPlayerAsk = "!BA_QUESTION"; + m_talkPlayerGreet = "!BA_HELLO"; + m_talkPlayerIdle = "!BA_IDLE"; + m_talkPlayerWounded1 = "!BA_CUREA"; + m_talkPlayerWounded2 = "!BA_CUREB"; + m_talkPlayerWounded3 = "!BA_CUREC"; + m_talkUnfollow = "!BA_WAIT"; + m_talkFollow = "!BA_OK"; + m_talkStopFollow = "!BA_STOP"; model = "models/barney.mdl"; netname = "Barney"; diff --git a/src/server/valve/monster_headcrab.cpp b/src/server/valve/monster_headcrab.cpp index d39be98b..46d825ea 100644 --- a/src/server/valve/monster_headcrab.cpp +++ b/src/server/valve/monster_headcrab.cpp @@ -101,7 +101,7 @@ monster_headcrab::Pain(int iHitBody) } int rand = floor(random(0,hcb_sndpain.length)); - Speak(hcb_sndpain[rand]); + Sound(hcb_sndpain[rand]); frame = HCBA_FLINCH; m_flPainTime = time + 0.25f; } @@ -115,7 +115,7 @@ monster_headcrab::Death(int iHitBody) /* the sound */ int rand = floor(random(0,hcb_sndpain.length)); - Speak(hcb_sndpain[rand]); + Sound(hcb_sndpain[rand]); } /* set the functional differences */ @@ -136,7 +136,7 @@ monster_headcrab::IdleNoise(void) m_flIdleTime = time + 2.0f + random(0,5); int rand = floor(random(0, hcb_sndidle.length)); - Speak(hcb_sndidle[rand]); + Sound(hcb_sndidle[rand]); } void diff --git a/src/server/valve/monster_houndeye.cpp b/src/server/valve/monster_houndeye.cpp index 7a20a836..c50fa0e3 100644 --- a/src/server/valve/monster_houndeye.cpp +++ b/src/server/valve/monster_houndeye.cpp @@ -119,7 +119,7 @@ monster_houndeye::Pain(int iHitBody) } int rand = floor(random(0,he_sndpain.length)); - Speak(he_sndpain[rand]); + Sound(he_sndpain[rand]); frame = HE_FLINCH + floor(random(0, 2)); m_flPainTime = time + 0.25f; } @@ -133,7 +133,7 @@ monster_houndeye::Death(int iHitBody) /* the sound */ int rand = floor(random(0,he_sndpain.length)); - Speak(he_sndpain[rand]); + Sound(he_sndpain[rand]); } /* set the functional differences */ @@ -154,7 +154,7 @@ monster_houndeye::IdleNoise(void) m_flIdleTime = time + 2.0f + random(0,5); int rand = floor(random(0, he_sndidle.length)); - Speak(he_sndidle[rand]); + Sound(he_sndidle[rand]); } void diff --git a/src/server/valve/monster_ichthyosaur.cpp b/src/server/valve/monster_ichthyosaur.cpp index 052d740a..3b70dee9 100644 --- a/src/server/valve/monster_ichthyosaur.cpp +++ b/src/server/valve/monster_ichthyosaur.cpp @@ -93,7 +93,7 @@ monster_ichthyosaur::Pain(int iHitBody) } int rand = floor(random(0,ichy_sndpain.length)); - Speak(ichy_sndpain[rand]); + Sound(ichy_sndpain[rand]); frame = ICHY_FLINCH + floor(random(0, 2)); m_flPainTime = time + 0.25f; } @@ -119,7 +119,7 @@ monster_ichthyosaur::Death(int iHitBody) /* the sound */ int rand = floor(random(0,ichy_sndpain.length)); - Speak(ichy_sndpain[rand]); + Sound(ichy_sndpain[rand]); } /* set the functional differences */ @@ -140,7 +140,7 @@ monster_ichthyosaur::IdleNoise(void) m_flIdleTime = time + 2.0f + random(0,5); int rand = floor(random(0, ichy_sndidle.length)); - Speak(ichy_sndidle[rand]); + Sound(ichy_sndidle[rand]); } void diff --git a/src/server/valve/monster_scientist.cpp b/src/server/valve/monster_scientist.cpp index 682a9e89..1e561c7f 100644 --- a/src/server/valve/monster_scientist.cpp +++ b/src/server/valve/monster_scientist.cpp @@ -302,7 +302,7 @@ string sci_sndidle[] = { "scientist/hopenominal.wav", }; -class monster_scientist:CBaseMonster +class monster_scientist:CBaseNPC { vector m_vecLastUserPos; entity m_eUser; diff --git a/src/server/valve/monster_tentacle.cpp b/src/server/valve/monster_tentacle.cpp index ddb0f08c..8cf47707 100644 --- a/src/server/valve/monster_tentacle.cpp +++ b/src/server/valve/monster_tentacle.cpp @@ -126,7 +126,7 @@ monster_tentacle::IdleNoise(void) m_flIdleTime = time + 2.0f + random(0,5); int rand = floor(random(0, tent_sndidle.length)); - Speak(tent_sndidle[rand]); + Sound(tent_sndidle[rand]); } void diff --git a/src/server/valve/monster_zombie.cpp b/src/server/valve/monster_zombie.cpp index 00894f68..e42d221e 100644 --- a/src/server/valve/monster_zombie.cpp +++ b/src/server/valve/monster_zombie.cpp @@ -128,7 +128,7 @@ monster_zombie::Pain(int iHitBody) } int rand = floor(random(0,zom_sndpain.length)); - Speak(zom_sndpain[rand]); + Sound(zom_sndpain[rand]); frame = ZOMA_FLINCH + floor(random(0, 2)); m_flPainTime = time + 0.25f; } @@ -151,7 +151,7 @@ monster_zombie::Death(int iHitBody) /* the sound */ int rand = floor(random(0,zom_sndpain.length)); - Speak(zom_sndpain[rand]); + Sound(zom_sndpain[rand]); } /* set the functional differences */ @@ -172,7 +172,7 @@ monster_zombie::IdleNoise(void) m_flIdleTime = time + 2.0f + random(0,5); int rand = floor(random(0, zom_sndidle.length)); - Speak(zom_sndidle[rand]); + Sound(zom_sndidle[rand]); } void diff --git a/src/server/valve/progs.src b/src/server/valve/progs.src index 0da8cb43..fe5eaa4f 100755 --- a/src/server/valve/progs.src +++ b/src/server/valve/progs.src @@ -18,6 +18,7 @@ defs.h ../logging.c ../nodes.c ../skill.c +../sentences.c ../../gs-entbase/server.src ../valve/monster_apache.cpp