Sentences: Speed up the word search using hashtables
This commit is contained in:
parent
8ec3d00e93
commit
2c72d358ce
21 changed files with 270 additions and 241 deletions
|
@ -164,7 +164,10 @@ Event_Parse(float type)
|
||||||
EnvSprite_ParseEvent();
|
EnvSprite_ParseEvent();
|
||||||
break;
|
break;
|
||||||
case EV_TEXT:
|
case EV_TEXT:
|
||||||
GameText_Parse();
|
GameText_ParseString();
|
||||||
|
break;
|
||||||
|
case EV_TEXT_STRING:
|
||||||
|
GameText_ParseString();
|
||||||
break;
|
break;
|
||||||
case EV_MESSAGE:
|
case EV_MESSAGE:
|
||||||
GameMessage_Parse();
|
GameMessage_Parse();
|
||||||
|
|
|
@ -5,7 +5,6 @@ fog.qc
|
||||||
font.qc
|
font.qc
|
||||||
sky.qc
|
sky.qc
|
||||||
music.qc
|
music.qc
|
||||||
sentences.qc
|
|
||||||
prints.qc
|
prints.qc
|
||||||
voice.qc
|
voice.qc
|
||||||
fade.qc
|
fade.qc
|
||||||
|
|
|
@ -142,6 +142,33 @@ GameText_Draw(void)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
GameText_ParseString(void)
|
||||||
|
{
|
||||||
|
int chan = readbyte();
|
||||||
|
|
||||||
|
/* last channel is reserved for text menus */
|
||||||
|
if (!(chan >= 0 && chan <= 4)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_textchannels[chan].m_strMessage = Titles_ParseFunString(readstring());
|
||||||
|
g_textchannels[chan].m_flPosX = readfloat();
|
||||||
|
g_textchannels[chan].m_flPosY = readfloat();
|
||||||
|
g_textchannels[chan].m_iEffect = readbyte();
|
||||||
|
g_textchannels[chan].m_vecColor1[0] = readbyte() / 255;
|
||||||
|
g_textchannels[chan].m_vecColor1[1] = readbyte() / 255;
|
||||||
|
g_textchannels[chan].m_vecColor1[2] = readbyte() / 255;
|
||||||
|
g_textchannels[chan].m_vecColor2[0] = readbyte() / 255;
|
||||||
|
g_textchannels[chan].m_vecColor2[1] = readbyte() / 255;
|
||||||
|
g_textchannels[chan].m_vecColor2[2] = readbyte() / 255;
|
||||||
|
g_textchannels[chan].m_flFadeIn = readfloat();
|
||||||
|
g_textchannels[chan].m_flFadeOut = readfloat();
|
||||||
|
g_textchannels[chan].m_flHoldTime = readfloat();
|
||||||
|
g_textchannels[chan].m_flFXTime = readfloat();
|
||||||
|
g_textchannels[chan].m_flTime = 0.0f;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
GameText_Parse(void)
|
GameText_Parse(void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -127,7 +127,7 @@ func_plat::Restore(string strKey, string strValue)
|
||||||
m_strSndStop = ReadString(strValue);
|
m_strSndStop = ReadString(strValue);
|
||||||
break;
|
break;
|
||||||
case "m_handler":
|
case "m_handler":
|
||||||
m_handler = ReadEntity(strValue);
|
m_handler = (func_plat_helper)ReadEntity(strValue);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
super::Restore(strKey, strValue);
|
super::Restore(strKey, strValue);
|
||||||
|
|
|
@ -183,7 +183,7 @@ void
|
||||||
game_text::Trigger(entity act, triggermode_t state)
|
game_text::Trigger(entity act, triggermode_t state)
|
||||||
{
|
{
|
||||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||||
WriteByte(MSG_MULTICAST, EV_TEXT);
|
WriteByte(MSG_MULTICAST, EV_TEXT_STRING);
|
||||||
WriteByte(MSG_MULTICAST, m_iChannel);
|
WriteByte(MSG_MULTICAST, m_iChannel);
|
||||||
WriteString(MSG_MULTICAST, m_strMessage);
|
WriteString(MSG_MULTICAST, m_strMessage);
|
||||||
WriteFloat(MSG_MULTICAST, m_flPosX);
|
WriteFloat(MSG_MULTICAST, m_flPosX);
|
||||||
|
|
|
@ -160,7 +160,7 @@ scripted_sentence::Trigger(entity act, triggermode_t unused)
|
||||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||||
WriteByte(MSG_MULTICAST, EV_SENTENCE);
|
WriteByte(MSG_MULTICAST, EV_SENTENCE);
|
||||||
WriteEntity(MSG_MULTICAST, npc);
|
WriteEntity(MSG_MULTICAST, npc);
|
||||||
WriteString(MSG_MULTICAST, m_strSentence);
|
WriteInt(MSG_MULTICAST, Sentences_GetID(m_strSentence));
|
||||||
msg_entity = npc;
|
msg_entity = npc;
|
||||||
multicast(npc.origin, MULTICAST_PVS);
|
multicast(npc.origin, MULTICAST_PVS);
|
||||||
npc.m_flNextSentence = time + m_flDuration;
|
npc.m_flNextSentence = time + m_flDuration;
|
||||||
|
|
|
@ -161,7 +161,7 @@ speaker::Announce(void)
|
||||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||||
WriteByte(MSG_MULTICAST, EV_SENTENCE);
|
WriteByte(MSG_MULTICAST, EV_SENTENCE);
|
||||||
WriteEntity(MSG_MULTICAST, this);
|
WriteEntity(MSG_MULTICAST, this);
|
||||||
WriteString(MSG_MULTICAST, seq);
|
WriteInt(MSG_MULTICAST, Sentences_GetID(seq));
|
||||||
msg_entity = this;
|
msg_entity = this;
|
||||||
multicast(origin, MULTICAST_PVS);
|
multicast(origin, MULTICAST_PVS);
|
||||||
|
|
||||||
|
|
|
@ -272,7 +272,7 @@ ambient_generic::UseNormal(entity act, triggermode_t state)
|
||||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||||
WriteByte(MSG_MULTICAST, EV_SENTENCE);
|
WriteByte(MSG_MULTICAST, EV_SENTENCE);
|
||||||
WriteEntity(MSG_MULTICAST, this);
|
WriteEntity(MSG_MULTICAST, this);
|
||||||
WriteString(MSG_MULTICAST, seq);
|
WriteInt(MSG_MULTICAST, Sentences_GetID(m_strActivePath));
|
||||||
msg_entity = this;
|
msg_entity = this;
|
||||||
multicast(origin, MULTICAST_PHS);
|
multicast(origin, MULTICAST_PHS);
|
||||||
} else
|
} else
|
||||||
|
|
|
@ -229,7 +229,7 @@ info_waypoint::postdraw(void)
|
||||||
if (drawicon_visible(origin) != 0) {
|
if (drawicon_visible(origin) != 0) {
|
||||||
float textLength = Font_StringWidth(m_strText, true, FONT_CON);
|
float textLength = Font_StringWidth(m_strText, true, FONT_CON);
|
||||||
vector vecProj = project(origin) - [32, 32];
|
vector vecProj = project(origin) - [32, 32];
|
||||||
vector projectedPos = project(origin) - (textLength/2) + [0, 114];
|
vector projectedPos = project(origin) + [-(textLength/2), 48];
|
||||||
float a = (visible == 2) ? 0.25 : 1.0f;
|
float a = (visible == 2) ? 0.25 : 1.0f;
|
||||||
float dist = vlen(origin - g_view.GetCameraOrigin()) / WAYPOINT_METER;
|
float dist = vlen(origin - g_view.GetCameraOrigin()) / WAYPOINT_METER;
|
||||||
string distText = sprintf("Distance: %d m", dist);
|
string distText = sprintf("Distance: %d m", dist);
|
||||||
|
|
2
src/plugins/banner.qc
Executable file → Normal file
2
src/plugins/banner.qc
Executable file → Normal file
|
@ -24,7 +24,7 @@ BannerPlug_Broadcast(string bmsg)
|
||||||
{
|
{
|
||||||
|
|
||||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||||
WriteByte(MSG_MULTICAST, EV_TEXT);
|
WriteByte(MSG_MULTICAST, EV_TEXT_STRING);
|
||||||
WriteByte(MSG_MULTICAST, 1);
|
WriteByte(MSG_MULTICAST, 1);
|
||||||
WriteString(MSG_MULTICAST, bmsg);
|
WriteString(MSG_MULTICAST, bmsg);
|
||||||
WriteFloat(MSG_MULTICAST, -1);
|
WriteFloat(MSG_MULTICAST, -1);
|
||||||
|
|
|
@ -16,7 +16,6 @@
|
||||||
|
|
||||||
#include "NSOutput.h"
|
#include "NSOutput.h"
|
||||||
#include "NSGameRules.h"
|
#include "NSGameRules.h"
|
||||||
#include "sentences.h"
|
|
||||||
#include "skill.h"
|
#include "skill.h"
|
||||||
#include "logging.h"
|
#include "logging.h"
|
||||||
#include "nodes.h"
|
#include "nodes.h"
|
||||||
|
|
|
@ -4,7 +4,6 @@ plugins.qc
|
||||||
logging.qc
|
logging.qc
|
||||||
nodes.qc
|
nodes.qc
|
||||||
skill.qc
|
skill.qc
|
||||||
sentences.qc
|
|
||||||
spawn.qc
|
spawn.qc
|
||||||
NSGameRules.qc
|
NSGameRules.qc
|
||||||
client.qc
|
client.qc
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2016-2020 Marco Cawthorne <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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#define DYNAMIC_SENTENCES
|
|
||||||
|
|
||||||
#ifdef DYNAMIC_SENTENCES
|
|
||||||
string *g_sentences;
|
|
||||||
int g_sentences_count;
|
|
||||||
#else
|
|
||||||
#define SENTENCES_LIMIT 1024
|
|
||||||
string g_sentences[SENTENCES_LIMIT];
|
|
||||||
int g_sentences_count;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
void Sentences_Init(void);
|
|
||||||
string Sentences_GetSamples(string);
|
|
|
@ -1,125 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2016-2022 Vera Visions LLC.
|
|
||||||
*
|
|
||||||
* 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
void
|
|
||||||
Sentences_Init(void)
|
|
||||||
{
|
|
||||||
filestream fs_sentences;
|
|
||||||
string temp;
|
|
||||||
int c;
|
|
||||||
|
|
||||||
print("--------- Initializing SentencesDef (SERVER) ----------\n");
|
|
||||||
|
|
||||||
if (g_sentences_count > 0) {
|
|
||||||
g_sentences_count = 0;
|
|
||||||
#ifndef DYNAMIC_SENTENCES
|
|
||||||
if (g_sentences) {
|
|
||||||
memfree(g_sentences);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
fs_sentences = fopen("sound/sentences.txt", FILE_READ);
|
|
||||||
|
|
||||||
if (fs_sentences < 0) {
|
|
||||||
print("^1could 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 = (string *)memrealloc(g_sentences,
|
|
||||||
sizeof(string),
|
|
||||||
x,
|
|
||||||
g_sentences_count);
|
|
||||||
#else
|
|
||||||
if (g_sentences_count + 1 >= SENTENCES_LIMIT) {
|
|
||||||
print("^1reached limit of max sentences!\n");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_sentences_count++;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
g_sentences[x] = strtoupper(strcat("!", argv(0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
fclose(fs_sentences);
|
|
||||||
print(sprintf("SentencesDef initialized with %i entries.\n", g_sentences_count));
|
|
||||||
}
|
|
||||||
|
|
||||||
string
|
|
||||||
Sentences_GetSamples(string word)
|
|
||||||
{
|
|
||||||
int len;
|
|
||||||
int gc = 0;
|
|
||||||
|
|
||||||
/* you never know what NPCs might do */
|
|
||||||
if (word == "") {
|
|
||||||
return ("");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* check if the word is present at all */
|
|
||||||
for (int i = 0; i < g_sentences_count; i++) {
|
|
||||||
if (g_sentences[i] == word) {
|
|
||||||
NSLog("^3Sentences_GetSamples^7: Found %s", 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));
|
|
||||||
NSLog("^3Sentences_GetSamples^7: Choosing %s%i", word, r);
|
|
||||||
return sprintf("%s%i", word, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* we've somehow messed up catastrophically */
|
|
||||||
print(sprintf("^1ERROR: Invalid sentence keyword %s\n", word));
|
|
||||||
return ("");
|
|
||||||
}
|
|
|
@ -374,11 +374,7 @@ NSIO::Save(float handle)
|
||||||
SaveFloat(handle, "baseframe", baseframe);
|
SaveFloat(handle, "baseframe", baseframe);
|
||||||
SaveFloat(handle, "drawflags", drawflags);
|
SaveFloat(handle, "drawflags", drawflags);
|
||||||
SaveString(handle, "customphysics", getentityfieldstring(findentityfield("customphysics"), this));
|
SaveString(handle, "customphysics", getentityfieldstring(findentityfield("customphysics"), this));
|
||||||
SaveFloat(handle, "dimension_see", dimension_see);
|
|
||||||
SaveFloat(handle, "dimension_seen", dimension_seen);
|
|
||||||
SaveFloat(handle, "dimension_seen", dimension_seen);
|
|
||||||
SaveString(handle, "SendEntity", getentityfieldstring(findentityfield("SendEntity"), this));
|
SaveString(handle, "SendEntity", getentityfieldstring(findentityfield("SendEntity"), this));
|
||||||
SaveFloat(handle, "Version", Version);
|
|
||||||
SaveFloat(handle, "viewzoom", viewzoom);
|
SaveFloat(handle, "viewzoom", viewzoom);
|
||||||
SaveFloat(handle, "uniquespawnid", uniquespawnid);
|
SaveFloat(handle, "uniquespawnid", uniquespawnid);
|
||||||
|
|
||||||
|
@ -386,9 +382,6 @@ NSIO::Save(float handle)
|
||||||
SaveFloat(handle, "jumptime", jumptime);
|
SaveFloat(handle, "jumptime", jumptime);
|
||||||
SaveFloat(handle, "identity", identity);
|
SaveFloat(handle, "identity", identity);
|
||||||
SaveFloat(handle, "iBleeds", iBleeds);
|
SaveFloat(handle, "iBleeds", iBleeds);
|
||||||
SaveFloat(handle, "subblend2frac", subblend2frac);
|
|
||||||
SaveFloat(handle, "subblendfrac", subblendfrac);
|
|
||||||
SaveFloat(handle, "baseframe1time", baseframe1time);
|
|
||||||
|
|
||||||
SaveString(handle, "m_strOnTrigger", m_strOnTrigger);
|
SaveString(handle, "m_strOnTrigger", m_strOnTrigger);
|
||||||
SaveString(handle, "m_strOnUser1", m_strOnUser1);
|
SaveString(handle, "m_strOnUser1", m_strOnUser1);
|
||||||
|
@ -659,21 +652,9 @@ NSIO::Restore(string strKey, string strValue)
|
||||||
case "customphysics":
|
case "customphysics":
|
||||||
customphysics = externvalue(-1, strValue);
|
customphysics = externvalue(-1, strValue);
|
||||||
break;
|
break;
|
||||||
case "dimension_see":
|
|
||||||
dimension_see = ReadFloat(strValue);
|
|
||||||
break;
|
|
||||||
case "dimension_seen":
|
|
||||||
dimension_seen = ReadFloat(strValue);
|
|
||||||
break;
|
|
||||||
case "dimension_seen":
|
|
||||||
dimension_seen = ReadFloat(strValue);
|
|
||||||
break;
|
|
||||||
case "SendEntity":
|
case "SendEntity":
|
||||||
SendEntity = externvalue(-1, strValue);
|
SendEntity = externvalue(-1, strValue);
|
||||||
break;
|
break;
|
||||||
case "Version":
|
|
||||||
Version = ReadFloat(strValue);
|
|
||||||
break;
|
|
||||||
case "viewzoom":
|
case "viewzoom":
|
||||||
viewzoom = ReadFloat(strValue);
|
viewzoom = ReadFloat(strValue);
|
||||||
break;
|
break;
|
||||||
|
@ -691,15 +672,6 @@ NSIO::Restore(string strKey, string strValue)
|
||||||
case "iBleeds":
|
case "iBleeds":
|
||||||
iBleeds = ReadFloat(strValue);
|
iBleeds = ReadFloat(strValue);
|
||||||
break;
|
break;
|
||||||
case "subblend2frac":
|
|
||||||
subblend2frac = ReadFloat(strValue);
|
|
||||||
break;
|
|
||||||
case "subblendfrac":
|
|
||||||
subblendfrac = ReadFloat(strValue);
|
|
||||||
break;
|
|
||||||
case "baseframe1time":
|
|
||||||
baseframe1time = ReadFloat(strValue);
|
|
||||||
break;
|
|
||||||
|
|
||||||
/* END: all the stock Quake fields the engine is aware of */
|
/* END: all the stock Quake fields the engine is aware of */
|
||||||
case "m_strOnTrigger":
|
case "m_strOnTrigger":
|
||||||
|
|
|
@ -250,7 +250,7 @@ NSTalkMonster::Sentence(string sentence)
|
||||||
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
WriteByte(MSG_MULTICAST, SVC_CGAMEPACKET);
|
||||||
WriteByte(MSG_MULTICAST, EV_SENTENCE);
|
WriteByte(MSG_MULTICAST, EV_SENTENCE);
|
||||||
WriteEntity(MSG_MULTICAST, this);
|
WriteEntity(MSG_MULTICAST, this);
|
||||||
WriteString(MSG_MULTICAST, seq);
|
WriteInt(MSG_MULTICAST, Sentences_GetID(seq));
|
||||||
msg_entity = this;
|
msg_entity = this;
|
||||||
multicast(origin, MULTICAST_PVS);
|
multicast(origin, MULTICAST_PVS);
|
||||||
}
|
}
|
||||||
|
@ -891,7 +891,7 @@ NSTalkMonster_ParseSentence(void)
|
||||||
|
|
||||||
/* parse packets */
|
/* parse packets */
|
||||||
e = readentitynum();
|
e = readentitynum();
|
||||||
sentence = readstring();
|
sentence = Sentences_GetString(readint());
|
||||||
|
|
||||||
ent = findfloat(world, entnum, e);
|
ent = findfloat(world, entnum, e);
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,8 @@ string __fullspawndata;
|
||||||
#include "../gs-entbase/server/defs.h"
|
#include "../gs-entbase/server/defs.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "sentences.h"
|
||||||
|
|
||||||
#include "NSIO.h"
|
#include "NSIO.h"
|
||||||
#include "NSTrigger.h"
|
#include "NSTrigger.h"
|
||||||
#include "NSEntity.h"
|
#include "NSEntity.h"
|
||||||
|
|
|
@ -37,6 +37,7 @@ enum
|
||||||
EV_HUDHINT,
|
EV_HUDHINT,
|
||||||
EV_FADE,
|
EV_FADE,
|
||||||
EV_TEXT,
|
EV_TEXT,
|
||||||
|
EV_TEXT_STRING,
|
||||||
EV_MESSAGE,
|
EV_MESSAGE,
|
||||||
EV_SPRITE,
|
EV_SPRITE,
|
||||||
EV_MODELGIB,
|
EV_MODELGIB,
|
||||||
|
|
|
@ -29,6 +29,7 @@ propdata.qc
|
||||||
surfaceproperties.qc
|
surfaceproperties.qc
|
||||||
decalgroups.qc
|
decalgroups.qc
|
||||||
materials.qc
|
materials.qc
|
||||||
|
sentences.qc
|
||||||
NSSpraylogo.qc
|
NSSpraylogo.qc
|
||||||
util.qc
|
util.qc
|
||||||
weapons.qc
|
weapons.qc
|
||||||
|
|
83
src/shared/sentences.h
Normal file
83
src/shared/sentences.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2016-2020 Marco Cawthorne <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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef SERVER
|
||||||
|
#define DYNAMIC_SENTENCES
|
||||||
|
|
||||||
|
#ifdef DYNAMIC_SENTENCES
|
||||||
|
string *g_sentences;
|
||||||
|
int g_sentences_count;
|
||||||
|
#else
|
||||||
|
#define SENTENCES_LIMIT 1024
|
||||||
|
string g_sentences[SENTENCES_LIMIT];
|
||||||
|
int g_sentences_count;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
string Sentences_GetSamples(string);
|
||||||
|
int Sentences_GetID(string);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef CLIENT
|
||||||
|
string Sentences_GetString(int id);
|
||||||
|
void Sentences_Shutdown(void);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
void Sentences_Init(void);
|
||||||
|
|
||||||
|
var hashtable g_hashsentences;
|
|
@ -14,49 +14,7 @@
|
||||||
* OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
* 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.
|
#ifdef CLIENT
|
||||||
* 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 */
|
/* enable this if you want to use memalloc */
|
||||||
#define DYNAMIC_SENTENCES
|
#define DYNAMIC_SENTENCES
|
||||||
|
|
||||||
|
@ -106,6 +64,11 @@ Sentences_Init(void)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* create the hash-table if it doesn't exist */
|
||||||
|
if (!g_hashsentences) {
|
||||||
|
g_hashsentences = hash_createtab(2, HASH_ADD);
|
||||||
|
}
|
||||||
|
|
||||||
while ((temp = fgets(fs_sentences))) {
|
while ((temp = fgets(fs_sentences))) {
|
||||||
/* tons of comments/garbage in those files,
|
/* tons of comments/garbage in those files,
|
||||||
* so tokenize appropriately */
|
* so tokenize appropriately */
|
||||||
|
@ -140,6 +103,7 @@ Sentences_Init(void)
|
||||||
/* first entry is the id, prefix with ! as well */
|
/* first entry is the id, prefix with ! as well */
|
||||||
if (i==0) {
|
if (i==0) {
|
||||||
g_sentences[x].m_strID = strtoupper(strcat("!", argv(0)));
|
g_sentences[x].m_strID = strtoupper(strcat("!", argv(0)));
|
||||||
|
hash_add(g_hashsentences, g_sentences[x].m_strID, x);
|
||||||
} else {
|
} else {
|
||||||
if (i == 1) {
|
if (i == 1) {
|
||||||
g_sentences[x].m_strSamples = sprintf("%s", argv(i));
|
g_sentences[x].m_strSamples = sprintf("%s", argv(i));
|
||||||
|
@ -182,11 +146,144 @@ string
|
||||||
Sentences_GetSamples(string msg)
|
Sentences_GetSamples(string msg)
|
||||||
{
|
{
|
||||||
Sentences_ResetSample();
|
Sentences_ResetSample();
|
||||||
|
int i = (int)hash_get(g_hashsentences, msg, -1i);
|
||||||
|
|
||||||
for (int i = 0; i < g_sentences_count; i++) {
|
if (i != -1i)
|
||||||
if (g_sentences[i].m_strID == msg) {
|
return g_sentences[i].m_strSamples;
|
||||||
return g_sentences[i].m_strSamples;
|
else {
|
||||||
|
print(sprintf("^1ERROR: Cannot find sentence %S\n", msg));
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
Sentences_GetString(int id)
|
||||||
|
{
|
||||||
|
return g_sentences[id].m_strID;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef SERVER
|
||||||
|
void
|
||||||
|
Sentences_Init(void)
|
||||||
|
{
|
||||||
|
filestream fs_sentences;
|
||||||
|
string temp;
|
||||||
|
int c;
|
||||||
|
|
||||||
|
print("--------- Initializing SentencesDef (SERVER) ----------\n");
|
||||||
|
|
||||||
|
if (g_sentences_count > 0) {
|
||||||
|
g_sentences_count = 0;
|
||||||
|
#ifndef DYNAMIC_SENTENCES
|
||||||
|
if (g_sentences) {
|
||||||
|
memfree(g_sentences);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
fs_sentences = fopen("sound/sentences.txt", FILE_READ);
|
||||||
|
|
||||||
|
if (fs_sentences < 0) {
|
||||||
|
print("^1could not load sound/sentences.txt\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* create the hash-table if it doesn't exist */
|
||||||
|
if (!g_hashsentences) {
|
||||||
|
g_hashsentences = hash_createtab(2, HASH_ADD);
|
||||||
|
}
|
||||||
|
|
||||||
|
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 = (string *)memrealloc(g_sentences,
|
||||||
|
sizeof(string),
|
||||||
|
x,
|
||||||
|
g_sentences_count);
|
||||||
|
#else
|
||||||
|
if (g_sentences_count + 1 >= SENTENCES_LIMIT) {
|
||||||
|
print("^1reached limit of max sentences!\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_sentences_count++;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
g_sentences[x] = strtoupper(strcat("!", argv(0)));
|
||||||
|
hash_add(g_hashsentences, g_sentences[x], x);
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(fs_sentences);
|
||||||
|
print(sprintf("SentencesDef initialized with %i entries.\n", g_sentences_count));
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
Sentences_GetSamples(string word)
|
||||||
|
{
|
||||||
|
int len;
|
||||||
|
int gc = 0;
|
||||||
|
int r, x;
|
||||||
|
|
||||||
|
/* you never know what NPCs might do */
|
||||||
|
if (word == "") {
|
||||||
|
return ("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* check if the word is present at all */
|
||||||
|
x = (int)hash_get(g_hashsentences, word, -1i);
|
||||||
|
if (x != -1i) {
|
||||||
|
return g_sentences[x];
|
||||||
|
}
|
||||||
|
|
||||||
|
/* it may be a random group of words. */
|
||||||
|
/* start at [WORD]0 */
|
||||||
|
r = (int)hash_get(g_hashsentences, strcat(word, "0"), 0i);
|
||||||
|
|
||||||
|
len = strlen(word);
|
||||||
|
for (int i = r; 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) {
|
||||||
|
r = floor(random(0, gc));
|
||||||
|
NSLog("^3Sentences_GetSamples^7: Choosing %s%i", word, r);
|
||||||
|
return sprintf("%s%i", word, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* we've somehow messed up catastrophically */
|
||||||
|
print(sprintf("^1ERROR: Invalid sentence keyword %s\n", word));
|
||||||
return ("");
|
return ("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
Sentences_GetID(string sentence)
|
||||||
|
{
|
||||||
|
int i = (int)hash_get(g_hashsentences, sentence, -1i);
|
||||||
|
|
||||||
|
if (i != -1i) {
|
||||||
|
print(sprintf("^2SUCCESS: Found sentence %S\n", sentence));
|
||||||
|
return i;
|
||||||
|
} else {
|
||||||
|
print(sprintf("^1ERROR: Cannot find sentence %S\n", sentence));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
Loading…
Reference in a new issue