Added initial support for basic sentences.txt on monsters.
This commit is contained in:
parent
d20c3f1db3
commit
db4ed5ac3d
29 changed files with 458 additions and 197 deletions
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
179
src/client/npc.c
179
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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
87
src/gs-entbase/server/basenpc.cpp
Normal file
87
src/gs-entbase/server/basenpc.cpp
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* 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)
|
||||
{
|
||||
|
||||
}
|
|
@ -206,6 +206,7 @@ void initents(void)
|
|||
|
||||
Game_Worldspawn();
|
||||
Decals_Init();
|
||||
Sentences_Init();
|
||||
|
||||
entity respawntimer = spawn();
|
||||
respawntimer.think = init_respawn;
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
../logging.c
|
||||
../nodes.c
|
||||
../skill.c
|
||||
../sentences.c
|
||||
../../gs-entbase/server.src
|
||||
|
||||
../valve/monster_apache.cpp
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
../logging.c
|
||||
../nodes.c
|
||||
../skill.c
|
||||
../sentences.c
|
||||
../../gs-entbase/server.src
|
||||
|
||||
../valve/monster_apache.cpp
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
../logging.c
|
||||
../nodes.c
|
||||
../skill.c
|
||||
../sentences.c
|
||||
../../gs-entbase/server.src
|
||||
../valve/monster_rat.cpp
|
||||
../valve/monster_scientist.cpp
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -299,7 +299,7 @@ string sci_sndidle[] = {
|
|||
"scientist/hopenominal.wav",
|
||||
};
|
||||
|
||||
class monster_scientist:CBaseMonster
|
||||
class monster_scientist:CBaseNPC
|
||||
{
|
||||
vector m_vecLastUserPos;
|
||||
entity m_eUser;
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
../logging.c
|
||||
../nodes.c
|
||||
../skill.c
|
||||
../sentences.c
|
||||
|
||||
../../gs-entbase/server.src
|
||||
|
||||
|
|
117
src/server/sentences.c
Normal file
117
src/server/sentences.c
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* 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;
|
||||
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 "";
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
../logging.c
|
||||
../nodes.c
|
||||
../skill.c
|
||||
../sentences.c
|
||||
../../gs-entbase/server.src
|
||||
../../shared/decals.c
|
||||
../../shared/effects.c
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -302,7 +302,7 @@ string sci_sndidle[] = {
|
|||
"scientist/hopenominal.wav",
|
||||
};
|
||||
|
||||
class monster_scientist:CBaseMonster
|
||||
class monster_scientist:CBaseNPC
|
||||
{
|
||||
vector m_vecLastUserPos;
|
||||
entity m_eUser;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -18,6 +18,7 @@ defs.h
|
|||
../logging.c
|
||||
../nodes.c
|
||||
../skill.c
|
||||
../sentences.c
|
||||
../../gs-entbase/server.src
|
||||
|
||||
../valve/monster_apache.cpp
|
||||
|
|
Loading…
Reference in a new issue