You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
621 lines
16 KiB
621 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: |
|
// |
|
//=====================================================================================// |
|
|
|
#include "cbase.h" |
|
#include <ctype.h> |
|
#include "tf_autorp.h" |
|
#include "filesystem.h" |
|
|
|
// memdbgon must be the last include file in a .cpp file!!! |
|
#include "tier0/memdbgon.h" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
CTFAutoRP *AutoRP( void ) |
|
{ |
|
static CTFAutoRP *pSystem = NULL; |
|
if ( !pSystem ) |
|
{ |
|
pSystem = new CTFAutoRP(); |
|
pSystem->ParseDataFile(); |
|
} |
|
|
|
return pSystem; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFAutoRP::ParseDataFile( void ) |
|
{ |
|
Assert( !m_pDataFileKV ); |
|
|
|
// Load & parse the word files |
|
KeyValues *pFileKV = new KeyValues( "AutoRPFile" ); |
|
if ( pFileKV->LoadFromFile( filesystem, "scripts/autorp.txt", "MOD" ) == false ) |
|
return; |
|
|
|
m_pDataFileKV = pFileKV->MakeCopy(); |
|
|
|
// Prepended word list |
|
KeyValues *pKVPrepended = m_pDataFileKV->FindKey( "prepended_words" ); |
|
if ( pKVPrepended ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVPrepended, pKVKey ) |
|
{ |
|
m_a_pszPrependedWords.AddToTail( pKVKey->GetName() ); |
|
} |
|
} |
|
|
|
// Appended word list |
|
KeyValues *pKVAppended = m_pDataFileKV->FindKey( "appended_words" ); |
|
if ( pKVAppended ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVAppended, pKVKey ) |
|
{ |
|
m_a_pszAppendedWords.AddToTail( pKVKey->GetName() ); |
|
} |
|
} |
|
|
|
// Word replacements |
|
KeyValues *pKVReplacements = m_pDataFileKV->FindKey( "word_replacements" ); |
|
if ( pKVReplacements ) |
|
{ |
|
FOR_EACH_SUBKEY( pKVReplacements, pKVEntry ) |
|
{ |
|
int iIdx = m_a_Replacements.AddToTail(); |
|
m_a_Replacements[iIdx].iChance = 1; |
|
m_a_Replacements[iIdx].iPrePendCount = 1; |
|
FOR_EACH_SUBKEY( pKVEntry, pKVKey ) |
|
{ |
|
const char *pszKey = pKVKey->GetName(); |
|
const char *pszValue = pKVKey->GetString(); |
|
|
|
if ( FStrEq(pszKey,"replacement") ) |
|
{ |
|
m_a_Replacements[iIdx].a_pszReplacements.AddToTail( pszValue ); |
|
} |
|
else if ( FStrEq(pszKey,"replacement_prepend") ) |
|
{ |
|
m_a_Replacements[iIdx].a_pszPrepended.AddToTail( pszValue ); |
|
} |
|
else if ( FStrEq(pszKey,"replacement_plural") ) |
|
{ |
|
m_a_Replacements[iIdx].a_pszPluralReplacements.AddToTail( pszValue ); |
|
} |
|
else if ( FStrEq(pszKey,"prepend_count") ) |
|
{ |
|
m_a_Replacements[iIdx].iPrePendCount = pKVKey->GetInt(); |
|
} |
|
else if ( FStrEq(pszKey,"chance") ) |
|
{ |
|
m_a_Replacements[iIdx].iChance = pKVKey->GetInt(); |
|
} |
|
else if ( FStrEq(pszKey,"word") ) |
|
{ |
|
m_a_Replacements[iIdx].m_Words.AddToTail( m_pWordTable->AddString( pszValue ) ); |
|
} |
|
else if ( FStrEq(pszKey,"word_plural") ) |
|
{ |
|
m_a_Replacements[iIdx].m_Plurals.AddToTail( m_pWordTable->AddString( pszValue ) ); |
|
} |
|
else if ( FStrEq(pszKey,"prev") ) |
|
{ |
|
m_a_Replacements[iIdx].m_PrevWords.AddToTail( m_pWordTable->AddString( pszValue ) ); |
|
} |
|
else |
|
{ |
|
Assert(0); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFAutoRP::ApplyRPTo( char *pBuf, int iBufSize ) |
|
{ |
|
if ( !m_pDataFileKV ) |
|
return; |
|
if ( !pBuf || !pBuf[0] ) |
|
return; |
|
|
|
// Ignore sourceMod commands |
|
if ( pBuf[0] == '!' || pBuf[0] == '/' ) |
|
return; |
|
|
|
bool bDoPends = true; |
|
|
|
char *pszIn = new char[iBufSize]; |
|
if ( pBuf[0] == '-' ) |
|
{ |
|
bDoPends = false; |
|
Q_strncpy( pszIn, pBuf+1, iBufSize-1 ); |
|
} |
|
else |
|
{ |
|
Q_strncpy( pszIn, pBuf, iBufSize ); |
|
} |
|
pBuf[0] = '\0'; |
|
|
|
ModifySpeech( pszIn, pBuf, iBufSize, bDoPends, false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CTFAutoRP::GetRandomPre( void ) |
|
{ |
|
if ( RandomInt(1,4) != 1 ) |
|
return NULL; |
|
|
|
if ( !m_a_pszPrependedWords.Count() ) |
|
return NULL; |
|
|
|
static int iPrevPre = 0; |
|
iPrevPre += RandomInt(1,4); |
|
while ( iPrevPre >= m_a_pszPrependedWords.Count() ) |
|
{ |
|
iPrevPre -= m_a_pszPrependedWords.Count(); |
|
} |
|
|
|
return m_a_pszPrependedWords[iPrevPre]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
const char *CTFAutoRP::GetRandomPost( void ) |
|
{ |
|
if ( RandomInt(1,5) != 1 ) |
|
return NULL; |
|
|
|
if ( !m_a_pszAppendedWords.Count() ) |
|
return NULL; |
|
|
|
static int iPrevPost = 0; |
|
iPrevPost += RandomInt(1,3); |
|
while ( iPrevPost >= m_a_pszAppendedWords.Count() ) |
|
{ |
|
iPrevPost -= m_a_pszAppendedWords.Count(); |
|
} |
|
|
|
return m_a_pszAppendedWords[iPrevPost]; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
matchresult_t CTFAutoRP::WordMatches( wordreplacement_t *pRep, replacementcheck_t *pCheck ) |
|
{ |
|
if ( pRep->iChance != 1 ) |
|
{ |
|
if ( RandomInt( 1, pRep->iChance ) > 1 ) |
|
return MATCHES_NOT; |
|
} |
|
|
|
// If it has prewords, make sure the preword matches first |
|
if ( pRep->m_PrevWords.Count() > 0 ) |
|
{ |
|
if ( pCheck->iPrevLen <= 0 ) |
|
return MATCHES_NOT; |
|
|
|
CUtlSymbol sym = m_pWordTable->Find( pCheck->szPrevWord ); |
|
if ( UTL_INVAL_SYMBOL == sym ) |
|
return MATCHES_NOT; |
|
|
|
bool bMatchPrev = false; |
|
FOR_EACH_VEC( pRep->m_PrevWords, i ) |
|
{ |
|
if ( pRep->m_PrevWords[i] == sym ) |
|
{ |
|
bMatchPrev = true; |
|
break; |
|
} |
|
} |
|
|
|
if ( !bMatchPrev ) |
|
return MATCHES_NOT; |
|
|
|
pCheck->bUsedPrevWord = true; |
|
} |
|
|
|
CUtlSymbol sym = m_pWordTable->Find( pCheck->szWord ); |
|
FOR_EACH_VEC( pRep->m_Words, i ) |
|
{ |
|
if ( pRep->m_Words[i] == sym ) |
|
return MATCHES_SINGULAR; |
|
} |
|
|
|
CUtlSymbol pluralsym = m_pWordTable->Find( pCheck->szWord ); |
|
FOR_EACH_VEC( pRep->m_Plurals, i ) |
|
{ |
|
if ( pRep->m_Plurals[i] == pluralsym ) |
|
return MATCHES_PLURAL; |
|
} |
|
|
|
pCheck->bUsedPrevWord = false; |
|
return MATCHES_NOT; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFAutoRP::ReplaceWord( replacementcheck_t *pCheck, char *szRep, int iRepSize, bool bSymbols, bool bWordListOnly ) |
|
{ |
|
szRep[0] = '\0'; |
|
|
|
// First, see if we have a replacement |
|
FOR_EACH_VEC( m_a_Replacements, i ) |
|
{ |
|
wordreplacement_t *pRep = &m_a_Replacements[i]; |
|
matchresult_t iRes = WordMatches( pRep, pCheck ); |
|
if ( iRes == MATCHES_NOT ) |
|
continue; |
|
|
|
if ( pRep->a_pszPrepended.Count() > 0 ) |
|
{ |
|
CUtlVector<int> vecUsed; |
|
for ( int iCount = 0; iCount < pRep->iPrePendCount; iCount++ ) |
|
{ |
|
// Ensure we don't choose two of the same prepends |
|
int rnd = 0; |
|
do |
|
{ |
|
rnd = RandomInt( 0, (int)pRep->a_pszPrepended.Count() - 1 ); |
|
} while ( vecUsed.Find(rnd) != vecUsed.InvalidIndex() ); |
|
vecUsed.AddToTail(rnd); |
|
|
|
Q_strncat( szRep, pRep->a_pszPrepended[rnd], iRepSize ); |
|
if ( (iCount+1) < pRep->iPrePendCount ) |
|
{ |
|
Q_strncat( szRep, ", ", iRepSize ); |
|
} |
|
else |
|
{ |
|
Q_strncat( szRep, " ", iRepSize ); |
|
} |
|
} |
|
} |
|
|
|
if ( iRes == MATCHES_SINGULAR ) |
|
{ |
|
int rnd = RandomInt( 0, (int)pRep->a_pszReplacements.Count() - 1 ); |
|
Q_strncat( szRep, pRep->a_pszReplacements[rnd], iRepSize ); |
|
} |
|
else if ( iRes == MATCHES_PLURAL ) |
|
{ |
|
int rnd = RandomInt( 0, (int)pRep->a_pszPluralReplacements.Count() - 1 ); |
|
Q_strncat( szRep, pRep->a_pszPluralReplacements[rnd], iRepSize ); |
|
} |
|
|
|
return true; |
|
} |
|
|
|
if ( !bSymbols && !bWordListOnly ) |
|
{ |
|
char fc = pCheck->szWord[0]; |
|
|
|
// Randomly replace h's at the front of words with apostrophes |
|
if ( fc == 'h' && RandomInt(1,2) == 1 ) |
|
{ |
|
Q_strncpy( szRep, pCheck->szWord, MIN( iRepSize, pCheck->iWordLen+1 ) ); |
|
szRep[0] = '\''; |
|
return true; |
|
} |
|
|
|
char lc = pCheck->szWord[ pCheck->iWordLen-1 ]; |
|
if ( pCheck->iWordLen > 3 ) |
|
{ |
|
char slc = pCheck->szWord[ pCheck->iWordLen-2 ]; |
|
char lllc = pCheck->szWord[ pCheck->iWordLen-3 ]; |
|
|
|
// Randomly modify words ending in "ed", by replacing the "e" with an apostrophe |
|
// i.e. "worked" -> "work'd", "waited" -> "wait'd" |
|
if ( slc == 'e' && lc == 'd' && lllc != 'e' && RandomInt(1,4) == 1 ) |
|
{ |
|
Q_strncpy( szRep, pCheck->szWord, MIN( iRepSize, pCheck->iWordLen+1 ) ); |
|
szRep[ pCheck->iWordLen-2 ] = '\''; |
|
return true; |
|
} |
|
|
|
// Randomly append "th" or "st" to any word ending in "ke" |
|
// i.e. "take" -> "taketh", "broke" -> "brokest" |
|
if ( slc == 'k' && lc == 'e' && RandomInt(1,3) == 1 ) |
|
{ |
|
Q_strncpy( szRep, pCheck->szWord, MIN( iRepSize, pCheck->iWordLen+1 ) ); |
|
if ( RandomInt(1,2) == 1 ) |
|
{ |
|
Q_strncat( szRep, "th", iRepSize ); |
|
} |
|
else |
|
{ |
|
Q_strncat( szRep, "st", iRepSize ); |
|
} |
|
return true; |
|
} |
|
} |
|
|
|
if ( pCheck->iWordLen >= 3 ) |
|
{ |
|
char slc = pCheck->szWord[ pCheck->iWordLen-2 ]; |
|
|
|
// Randomly append "eth" to words with appropriate last letters. |
|
if ( RandomInt( 1, 5 ) == 1 && |
|
(lc == 't' || lc == 'p' || lc == 'k' || lc == 'g' || lc == 'b' || lc == 'w') ) |
|
{ |
|
Q_strncpy( szRep, pCheck->szWord, MIN( iRepSize, pCheck->iWordLen+1 ) ); |
|
Q_strncat( szRep, "eth", iRepSize ); |
|
return true; |
|
} |
|
|
|
// Randomly append "est" to any word ending in "ss" |
|
// i.e. "pass" -> "passest", "class" -> "classest" |
|
if ( lc == 's' && slc == 's' && RandomInt(1,5) == 1 ) |
|
{ |
|
Q_strncpy( szRep, pCheck->szWord, MIN( iRepSize, pCheck->iWordLen+1 ) ); |
|
Q_strncat( szRep, "est", iRepSize ); |
|
return true; |
|
} |
|
} |
|
|
|
if ( pCheck->iWordLen > 4 ) |
|
{ |
|
// Randomly prepend "a-" to words ending in "ing", and randomly replace the trailing g with an apostrophe |
|
// i.e. "coming" -> "a-comin'", "dancing" -> "a-dancing" |
|
char slc = pCheck->szWord[ pCheck->iWordLen-2 ]; |
|
char lllc = pCheck->szWord[ pCheck->iWordLen-3 ]; |
|
if ( lllc == 'i' && slc == 'n' && lc == 'g' ) |
|
{ |
|
char sc = pCheck->szWord[2]; |
|
if ( sc != '-' ) |
|
{ |
|
Q_strncpy( szRep, "a-", iRepSize ); |
|
|
|
if ( RandomInt(1,2) == 1 ) |
|
{ |
|
Q_strncat( szRep, pCheck->szWord, iRepSize, pCheck->iWordLen ); |
|
} |
|
else |
|
{ |
|
Q_strncat( szRep, pCheck->szWord, iRepSize, pCheck->iWordLen-1 ); |
|
Q_strncat( szRep, "'", iRepSize ); |
|
} |
|
return true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
bool CTFAutoRP::PerformReplacement( const char *pszReplacement, replacementcheck_t *pRepCheck, char *szStoredWord, int iStoredWordSize, char *pszOutText, int iOutLen ) |
|
{ |
|
if ( pszReplacement && pszReplacement[0] ) |
|
{ |
|
// Check to see if the previous word should be modified |
|
char fc = tolower( *pszReplacement ); |
|
if ( !_strnicmp( pRepCheck->szPrevWord, "an", MAX(pRepCheck->iPrevLen,2) ) ) |
|
{ |
|
if ( fc != 'a' && fc != 'e' && fc != 'i' && fc != 'o' && fc != 'u' ) |
|
{ |
|
// Remove the trailing n |
|
int iLen = (int)strlen( szStoredWord ); |
|
szStoredWord[iLen-1] = '\0'; // Move back 3. 1 for null, 1 for space, 1 for n. |
|
} |
|
} |
|
else if ( *pRepCheck->szPrevWord == 'a' && pRepCheck->iPrevLen == 1 ) |
|
{ |
|
if ( fc == 'a' || fc == 'e' || fc == 'i' || fc == 'o' || fc == 'u' ) |
|
{ |
|
// Add a trailing n |
|
Q_strncat( szStoredWord, "n", iStoredWordSize ); |
|
} |
|
} |
|
} |
|
|
|
// Only append the previous word if we didn't use it in our replacement |
|
if ( !pRepCheck->bUsedPrevWord ) |
|
{ |
|
// Append the previous word |
|
Q_strncat( pszOutText, szStoredWord, iOutLen ); |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CTFAutoRP::ModifySpeech( const char *pszInText, char *pszOutText, int iOutLen, bool bGeneratePreAndPost, bool bInPrePost ) |
|
{ |
|
if ( bGeneratePreAndPost ) |
|
{ |
|
// See if we generate a pre. If we do, modify it as well so we can perform replacements on it. |
|
const char *pszPre = GetRandomPre(); |
|
if ( pszPre && pszPre[0] ) |
|
{ |
|
ModifySpeech( pszPre, pszOutText, iOutLen, false, true ); |
|
Q_strncat( pszOutText, " ", iOutLen ); |
|
} |
|
} |
|
|
|
// Iterate through all the words and test them vs our replacement list |
|
const char *pszPrevWord = pszInText; |
|
const char *pszCurWord = pszInText; |
|
const char *pszCh = pszInText; |
|
char szStoredWord[128]; |
|
szStoredWord[0] = '\0'; |
|
char szCurrentWord[128]; |
|
szCurrentWord[0] = '\0'; |
|
|
|
replacementcheck_t repCheck; |
|
|
|
while ( 1 ) |
|
{ |
|
if ( (*pszCh >= 'A' && *pszCh <= 'Z') || (*pszCh >= 'a' && *pszCh <= 'z') || *pszCh == '&' ) |
|
{ |
|
pszCh++; |
|
continue; |
|
} |
|
|
|
// Hit the end of a word/string. |
|
int iCurLen = (int)(pszCh - pszCurWord); |
|
int iPrevLen = MAX( 0, (int)(pszCurWord - pszPrevWord) - 1 ); // -1 for the space |
|
|
|
bool bModifyWord = true; |
|
bool bSkipOneLetter = false; |
|
// Pre/Post pend blocks only modify words that start with an '&' |
|
if ( bInPrePost ) |
|
{ |
|
bModifyWord = ( pszCurWord[0] == '&' ); |
|
bSkipOneLetter = bModifyWord; |
|
} |
|
|
|
if ( bSkipOneLetter ) |
|
{ |
|
Q_strncpy( repCheck.szWord, pszCurWord+1, iCurLen ); |
|
repCheck.iWordLen = iCurLen-1; |
|
} |
|
else |
|
{ |
|
Q_strncpy( repCheck.szWord, pszCurWord, iCurLen+1 ); |
|
repCheck.iWordLen = iCurLen; |
|
} |
|
|
|
Q_strncpy( repCheck.szPrevWord, pszPrevWord, iPrevLen+1 ); |
|
repCheck.iPrevLen = iPrevLen; |
|
repCheck.bUsedPrevWord = false; |
|
|
|
if ( iCurLen > 0 ) |
|
{ |
|
bool bChanged = bModifyWord ? ReplaceWord( &repCheck, szCurrentWord, sizeof(szCurrentWord), false, bInPrePost ) : false; |
|
|
|
// If the character that broke the last two words apart was an apostrophe, see if we can replace the whole word |
|
if ( !bChanged && bModifyWord ) |
|
{ |
|
if ( szStoredWord[0] ) |
|
{ |
|
int iLen = Q_strlen(szStoredWord); |
|
if ( szStoredWord[iLen-1] == '\'' ) |
|
{ |
|
Q_strncpy( repCheck.szWord, szStoredWord, MIN( sizeof(repCheck.szWord),iLen+1 ) ); |
|
Q_strncat( repCheck.szWord, pszCurWord, sizeof(repCheck.szWord), iCurLen ); |
|
repCheck.iWordLen = iLen + iCurLen; |
|
repCheck.szPrevWord[0] = '\0'; |
|
repCheck.iPrevLen = 0; |
|
|
|
bChanged = ReplaceWord( &repCheck, szCurrentWord, sizeof(szCurrentWord), false, bInPrePost ); |
|
if ( bChanged ) |
|
{ |
|
repCheck.bUsedPrevWord = true; |
|
} |
|
} |
|
} |
|
} |
|
|
|
if ( szStoredWord[0] != '\0' ) |
|
{ |
|
if ( PerformReplacement( szCurrentWord, &repCheck, szStoredWord, sizeof(szStoredWord), pszOutText, iOutLen ) ) |
|
{ |
|
// Append a space, but not if the last character is an apostrophe |
|
int iLen = Q_strlen(szStoredWord); |
|
if ( szStoredWord[iLen-1] != '\'' ) |
|
{ |
|
Q_strncat( pszOutText, " ", iOutLen ); |
|
} |
|
} |
|
} |
|
|
|
if ( bChanged ) |
|
{ |
|
Q_strncpy( szStoredWord, szCurrentWord, sizeof(szStoredWord) ); |
|
|
|
// Match case of the first letter in the word we're replacing |
|
if ( pszCurWord[0] >= 'A' && pszCurWord[0] <= 'Z' ) |
|
{ |
|
szStoredWord[0] = toupper( szStoredWord[0] ); |
|
} |
|
else if ( pszCurWord[0] >= 'a' && pszCurWord[0] <= 'a' ) |
|
{ |
|
szStoredWord[0] = tolower( szStoredWord[0] ); |
|
} |
|
} |
|
else |
|
{ |
|
Q_strncpy( szStoredWord, pszCurWord, MIN( (int)sizeof(szStoredWord), (int)(pszCh - pszCurWord)+1 ) ); |
|
} |
|
} |
|
|
|
// Finished? |
|
if ( *pszCh == '\0' ) |
|
{ |
|
repCheck.bUsedPrevWord = false; |
|
if ( szStoredWord[0] != '\0' ) |
|
{ |
|
PerformReplacement( NULL, &repCheck, szStoredWord, sizeof(szStoredWord), pszOutText, iOutLen ); |
|
} |
|
break; |
|
} |
|
|
|
// If it wasn't a space that ended this word, try checking it for a symbol |
|
if ( *pszCh != ' ' ) |
|
{ |
|
Q_strncpy( repCheck.szWord, pszCh, 2 ); |
|
repCheck.iWordLen = 1; |
|
repCheck.iPrevLen = 0; |
|
repCheck.bUsedPrevWord = false; |
|
|
|
char szSymbolRep[128]; |
|
szSymbolRep[0] = '\0'; |
|
if ( ReplaceWord( &repCheck, szSymbolRep, sizeof(szSymbolRep), true, true ) ) |
|
{ |
|
Q_strncat( szStoredWord, szSymbolRep, (int)sizeof(szStoredWord) ); |
|
} |
|
else |
|
{ |
|
Q_strncat( szStoredWord, pszCh, (int)sizeof(szStoredWord), 1 ); |
|
} |
|
} |
|
|
|
// Move on |
|
pszCh++; |
|
pszPrevWord = pszCurWord; |
|
pszCurWord = pszCh; |
|
} |
|
|
|
if ( bGeneratePreAndPost ) |
|
{ |
|
int iLen = (int)strlen( pszOutText ); |
|
char pszLC = pszOutText[iLen-1]; |
|
if ( pszLC != '?' && pszLC != '!' ) |
|
{ |
|
// See if we generate a post. If we do, modify it as well so we can perform replacements on it. |
|
const char *pszPost = GetRandomPost(); |
|
if ( pszPost && pszPost[0] ) |
|
{ |
|
if ( pszLC != '.' ) |
|
{ |
|
Q_strncat( pszOutText, ". ", iOutLen ); |
|
} |
|
else |
|
{ |
|
Q_strncat( pszOutText, " ", iOutLen ); |
|
} |
|
|
|
ModifySpeech( pszPost, pszOutText, iOutLen, false, true ); |
|
} |
|
} |
|
} |
|
} |