468 lines
15 KiB
C++
468 lines
15 KiB
C++
//
|
|
// Copyright (c) 2014, by YaPB Development Team. All rights reserved.
|
|
//
|
|
// Permission is hereby granted, free of charge, to any person obtaining a
|
|
// copy of this software and associated documentation files (the "Software"),
|
|
// to deal in the Software without restriction, including without limitation
|
|
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
// and/or sell copies of the Software, and to permit persons to whom the
|
|
// Software is furnished to do so, subject to the following conditions:
|
|
//
|
|
// The above copyright notice and this permission notice shall be included in
|
|
// all copies or substantial portions of the Software.
|
|
//
|
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
// THE SOFTWARE.
|
|
//
|
|
// Version: $Id:$
|
|
//
|
|
|
|
#include <core.h>
|
|
|
|
ConVar yb_chat ("yb_chat", "1");
|
|
|
|
void StripTags (char *buffer)
|
|
{
|
|
// this function strips 'clan' tags specified below in given string buffer
|
|
|
|
// first three tags for Enhanced POD-Bot (e[POD], 3[POD], E[POD])
|
|
char *tagOpen[] = {"e[P", "3[P", "E[P", "-=", "-[", "-]", "-}", "-{", "<[", "<]", "[-", "]-", "{-", "}-", "[[", "[", "{", "]", "}", "<", ">", "-", "|", "=", "+"};
|
|
char *tagClose[] = {"]", "]", "]", "=-", "]-", "[-", "{-", "}-", "]>", "[>", "-]", "-[", "-}", "-{", "]]", "]", "}", "[", "{", ">", "<", "-", "|", "=", "+"};
|
|
|
|
int index, fieldStart, fieldStop, i;
|
|
int length = strlen (buffer); // get length of string
|
|
|
|
// foreach known tag...
|
|
for (index = 0; index < ARRAYSIZE_HLSDK (tagOpen); index++)
|
|
{
|
|
fieldStart = strstr (buffer, tagOpen[index]) - buffer; // look for a tag start
|
|
|
|
// have we found a tag start?
|
|
if (fieldStart >= 0 && fieldStart < 32)
|
|
{
|
|
fieldStop = strstr (buffer, tagClose[index]) - buffer; // look for a tag stop
|
|
|
|
// have we found a tag stop?
|
|
if ((fieldStop > fieldStart) && (fieldStop < 32))
|
|
{
|
|
int tagLength = strlen (tagClose[index]);
|
|
|
|
for (i = fieldStart; i < length - (fieldStop + tagLength - fieldStart); i++)
|
|
buffer[i] = buffer[i + (fieldStop + tagLength - fieldStart)]; // overwrite the buffer with the stripped string
|
|
|
|
buffer[i] = 0x0; // terminate the string
|
|
}
|
|
}
|
|
}
|
|
|
|
// have we stripped too much (all the stuff)?
|
|
if (buffer[0] != '\0')
|
|
{
|
|
strtrim (buffer); // if so, string is just a tag
|
|
|
|
// strip just the tag part...
|
|
for (index = 0; index < ARRAYSIZE_HLSDK(tagOpen); index++)
|
|
{
|
|
fieldStart = strstr (buffer, tagOpen[index]) - buffer; // look for a tag start
|
|
|
|
// have we found a tag start?
|
|
if (fieldStart >= 0 && fieldStart < 32)
|
|
{
|
|
fieldStop = fieldStart + strlen (tagOpen[index]); // set the tag stop
|
|
int tagLength = strlen (tagOpen[index]);
|
|
|
|
for (i = fieldStart; i < length - tagLength; i++)
|
|
buffer[i] = buffer[i + tagLength]; // overwrite the buffer with the stripped string
|
|
|
|
buffer[i] = 0x0; // terminate the string
|
|
|
|
fieldStart = strstr (buffer, tagClose[index]) - buffer; // look for a tag stop
|
|
|
|
// have we found a tag stop ?
|
|
if (fieldStart >= 0 && fieldStart < 32)
|
|
{
|
|
fieldStop = fieldStart + strlen (tagClose[index]); // set the tag
|
|
int tagLength = strlen (tagClose[index]);
|
|
|
|
for (i = fieldStart; i < length - tagLength; i++)
|
|
buffer[i] = buffer[i + tagLength]; // overwrite the buffer with the stripped string
|
|
|
|
buffer[i] = 0; // terminate the string
|
|
}
|
|
}
|
|
}
|
|
}
|
|
strtrim (buffer); // to finish, strip eventual blanks after and before the tag marks
|
|
}
|
|
|
|
char *HumanizeName (char *name)
|
|
{
|
|
// this function humanize player name (i.e. trim clan and switch to lower case (sometimes))
|
|
|
|
static char outputName[256]; // create return name buffer
|
|
strcpy (outputName, name); // copy name to new buffer
|
|
|
|
// drop tag marks, 80 percent of time
|
|
if (g_randGen.Long (1, 100) < 80)
|
|
StripTags (outputName);
|
|
else
|
|
strtrim (outputName);
|
|
|
|
// sometimes switch name to lower characters
|
|
// note: since we're using russian names written in english, we reduce this shit to 6 percent
|
|
if (g_randGen.Long (1, 100) <= 6)
|
|
{
|
|
for (int i = 0; i < static_cast <int> (strlen (outputName)); i++)
|
|
outputName[i] = tolower (outputName[i]); // to lower case
|
|
}
|
|
return &outputName[0]; // return terminated string
|
|
}
|
|
|
|
void HumanizeChat (char *buffer)
|
|
{
|
|
// this function humanize chat string to be more handwritten
|
|
|
|
int length = strlen (buffer); // get length of string
|
|
int i = 0;
|
|
|
|
// sometimes switch text to lowercase
|
|
// note: since we're using russian chat written in english, we reduce this shit to 4 percent
|
|
if (g_randGen.Long (1, 100) <= 4)
|
|
{
|
|
for (i = 0; i < length; i++)
|
|
buffer[i] = tolower (buffer[i]); // switch to lowercase
|
|
}
|
|
|
|
if (length > 15)
|
|
{
|
|
// "length / 2" percent of time drop a character
|
|
if (g_randGen.Long (1, 100) < (length / 2))
|
|
{
|
|
int pos = g_randGen.Long ((length / 8), length - (length / 8)); // chose random position in string
|
|
|
|
for (i = pos; i < length - 1; i++)
|
|
buffer[i] = buffer[i + 1]; // overwrite the buffer with stripped string
|
|
|
|
buffer[i] = 0x0; // terminate string;
|
|
length--; // update new string length
|
|
}
|
|
|
|
// "length" / 4 precent of time swap character
|
|
if (g_randGen.Long (1, 100) < (length / 4))
|
|
{
|
|
int pos = g_randGen.Long ((length / 8), ((3 * length) / 8)); // choose random position in string
|
|
char ch = buffer[pos]; // swap characters
|
|
|
|
buffer[pos] = buffer[pos + 1];
|
|
buffer[pos + 1] = ch;
|
|
}
|
|
}
|
|
buffer[length] = 0; // terminate string
|
|
}
|
|
|
|
void Bot::PrepareChatMessage (char *text)
|
|
{
|
|
// this function parses messages from the botchat, replaces keywords and converts names into a more human style
|
|
|
|
if (!yb_chat.GetBool () || IsNullString (text))
|
|
return;
|
|
|
|
memset (&m_tempStrings, 0, sizeof (m_tempStrings));
|
|
|
|
char *textStart = text;
|
|
char *pattern = text;
|
|
|
|
edict_t *talkEntity = NULL;
|
|
|
|
while (pattern != NULL)
|
|
{
|
|
// all replacement placeholders start with a %
|
|
pattern = strstr (textStart, "%");
|
|
|
|
if (pattern != NULL)
|
|
{
|
|
int length = pattern - textStart;
|
|
|
|
if (length > 0)
|
|
strncpy (m_tempStrings, textStart, length);
|
|
|
|
pattern++;
|
|
|
|
// player with most frags?
|
|
if (*pattern == 'f')
|
|
{
|
|
int highestFrags = -9000; // just pick some start value
|
|
int index = 0;
|
|
|
|
for (int i = 0; i < GetMaxClients (); i++)
|
|
{
|
|
if (!(g_clients[i].flags & CF_USED) || g_clients[i].ent == GetEntity ())
|
|
continue;
|
|
|
|
int frags = static_cast <int> (g_clients[i].ent->v.frags);
|
|
|
|
if (frags > highestFrags)
|
|
{
|
|
highestFrags = frags;
|
|
index = i;
|
|
}
|
|
}
|
|
talkEntity = g_clients[index].ent;
|
|
|
|
if (!FNullEnt (talkEntity))
|
|
strcat (m_tempStrings, HumanizeName (const_cast <char *> (STRING (talkEntity->v.netname))));
|
|
}
|
|
// mapname?
|
|
else if (*pattern == 'm')
|
|
strcat (m_tempStrings, GetMapName ());
|
|
// roundtime?
|
|
else if (*pattern == 'r')
|
|
{
|
|
int time = static_cast <int> (g_timeRoundEnd - GetWorldTime ());
|
|
strcat (m_tempStrings, FormatBuffer ("%02d:%02d", time / 60, time % 60));
|
|
}
|
|
// chat reply?
|
|
else if (*pattern == 's')
|
|
{
|
|
talkEntity = INDEXENT (m_sayTextBuffer.entityIndex);
|
|
|
|
if (!FNullEnt (talkEntity))
|
|
strcat (m_tempStrings, HumanizeName (const_cast <char *> (STRING (talkEntity->v.netname))));
|
|
}
|
|
// teammate alive?
|
|
else if (*pattern == 't')
|
|
{
|
|
int team = GetTeam (GetEntity ());
|
|
int i;
|
|
|
|
for (i = 0; i < GetMaxClients (); i++)
|
|
{
|
|
if (!(g_clients[i].flags & CF_USED) || !(g_clients[i].flags & CF_ALIVE) || (g_clients[i].team != team) || (g_clients[i].ent == GetEntity ()))
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
if (i < GetMaxClients ())
|
|
{
|
|
if (!FNullEnt (pev->dmg_inflictor) && (GetTeam (GetEntity ()) == GetTeam (pev->dmg_inflictor)))
|
|
talkEntity = pev->dmg_inflictor;
|
|
else
|
|
talkEntity = g_clients[i].ent;
|
|
|
|
if (!FNullEnt (talkEntity))
|
|
strcat (m_tempStrings, HumanizeName (const_cast <char *> (STRING (talkEntity->v.netname))));
|
|
}
|
|
else // no teammates alive...
|
|
{
|
|
for (i = 0; i < GetMaxClients (); i++)
|
|
{
|
|
if (!(g_clients[i].flags & CF_USED) || (g_clients[i].team != team) || (g_clients[i].ent == GetEntity ()))
|
|
continue;
|
|
|
|
break;
|
|
}
|
|
|
|
if (i < GetMaxClients ())
|
|
{
|
|
talkEntity = g_clients[i].ent;
|
|
|
|
if (!FNullEnt (talkEntity))
|
|
strcat (m_tempStrings, HumanizeName (const_cast <char *> (STRING (talkEntity->v.netname))));
|
|
}
|
|
}
|
|
}
|
|
else if (*pattern == 'e')
|
|
{
|
|
int team = GetTeam (GetEntity ());
|
|
int i;
|
|
|
|
for (i = 0; i < GetMaxClients (); i++)
|
|
{
|
|
if (!(g_clients[i].flags & CF_USED) || !(g_clients[i].flags & CF_ALIVE) || (g_clients[i].team == team) || (g_clients[i].ent == GetEntity ()))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if (i < GetMaxClients ())
|
|
{
|
|
talkEntity = g_clients[i].ent;
|
|
|
|
if (!FNullEnt (talkEntity))
|
|
strcat (m_tempStrings, HumanizeName (const_cast <char *> (STRING (talkEntity->v.netname))));
|
|
}
|
|
else // no teammates alive...
|
|
{
|
|
for (i = 0; i < GetMaxClients (); i++)
|
|
{
|
|
if (!(g_clients[i].flags & CF_USED) || (g_clients[i].team == team) || (g_clients[i].ent == GetEntity ()))
|
|
continue;
|
|
break;
|
|
}
|
|
if (i < GetMaxClients ())
|
|
{
|
|
talkEntity = g_clients[i].ent;
|
|
|
|
if (!FNullEnt (talkEntity))
|
|
strcat (m_tempStrings, HumanizeName (const_cast <char *> (STRING (talkEntity->v.netname))));
|
|
}
|
|
}
|
|
}
|
|
else if (*pattern == 'd')
|
|
{
|
|
if (g_gameVersion == CSV_CZERO)
|
|
{
|
|
if (g_randGen.Long (1, 100) < 30)
|
|
strcat (m_tempStrings, "CZ");
|
|
else if (g_randGen.Long (1, 100) < 80)
|
|
strcat (m_tempStrings, "KoHTpa K3");
|
|
else
|
|
strcat (m_tempStrings, "Condition Zero");
|
|
}
|
|
else if ((g_gameVersion == CSV_STEAM) || (g_gameVersion == CSV_OLD))
|
|
{
|
|
if (g_randGen.Long (1, 100) < 30)
|
|
strcat (m_tempStrings, "CS");
|
|
else if (g_randGen.Long (1, 100) < 80)
|
|
strcat (m_tempStrings, "KoHTpa");
|
|
else
|
|
strcat (m_tempStrings, "Counter-Strike");
|
|
}
|
|
}
|
|
else if (*pattern == 'v')
|
|
{
|
|
talkEntity = m_lastVictim;
|
|
|
|
if (!FNullEnt (talkEntity))
|
|
strcat (m_tempStrings, HumanizeName (const_cast <char *> (STRING (talkEntity->v.netname))));
|
|
}
|
|
pattern++;
|
|
textStart = pattern;
|
|
}
|
|
}
|
|
|
|
// let the bots make some mistakes...
|
|
char tempString[160];
|
|
strncpy (tempString, textStart, 159);
|
|
|
|
HumanizeChat (tempString);
|
|
strcat (m_tempStrings, tempString);
|
|
}
|
|
|
|
bool CheckKeywords (char *tempMessage, char *reply)
|
|
{
|
|
// this function checks is string contain keyword, and generates relpy to it
|
|
|
|
if (!yb_chat.GetBool () || IsNullString (tempMessage))
|
|
return false;
|
|
|
|
IterateArray (g_replyFactory, i)
|
|
{
|
|
IterateArray (g_replyFactory[i].keywords, j)
|
|
{
|
|
// check is keyword has occurred in message
|
|
if (strstr (tempMessage, g_replyFactory[i].keywords[j].GetBuffer ()) != NULL)
|
|
{
|
|
Array <String> &replies = g_replyFactory[i].usedReplies;
|
|
|
|
if (replies.GetElementNumber () >= g_replyFactory[i].replies.GetElementNumber () / 2)
|
|
replies.RemoveAll ();
|
|
|
|
bool replyUsed = false;
|
|
const char *generatedReply = g_replyFactory[i].replies.GetRandomElement ();
|
|
|
|
// don't say this twice
|
|
IterateArray (replies, k)
|
|
{
|
|
if (strstr (replies[k].GetBuffer (), generatedReply) != NULL)
|
|
replyUsed = true;
|
|
}
|
|
|
|
// reply not used, so use it
|
|
if (!replyUsed)
|
|
{
|
|
strcpy (reply, generatedReply); // update final buffer
|
|
replies.Push (generatedReply); //add to ignore list
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// didn't find a keyword? 70% of the time use some universal reply
|
|
if (g_randGen.Long (1, 100) < 70 && !g_chatFactory[CHAT_NOKW].IsEmpty ())
|
|
{
|
|
strcpy (reply, g_chatFactory[CHAT_NOKW].GetRandomElement ().GetBuffer ());
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool Bot::ParseChat (char *reply)
|
|
{
|
|
// this function parse chat buffer, and prepare buffer to keyword searching
|
|
|
|
char tempMessage[512];
|
|
strcpy (tempMessage, m_sayTextBuffer.sayText); // copy to safe place
|
|
|
|
// text to uppercase for keyword parsing
|
|
for (int i = 0; i < static_cast <int> (strlen (tempMessage)); i++)
|
|
tempMessage[i] = toupper (tempMessage[i]);
|
|
|
|
return CheckKeywords (tempMessage, reply);
|
|
}
|
|
|
|
bool Bot::RepliesToPlayer (void)
|
|
{
|
|
// this function sends reply to a player
|
|
|
|
if (m_sayTextBuffer.entityIndex != -1 && !IsNullString (m_sayTextBuffer.sayText))
|
|
{
|
|
char text[256];
|
|
|
|
// check is time to chat is good
|
|
if (m_sayTextBuffer.timeNextChat < GetWorldTime ())
|
|
{
|
|
if (g_randGen.Long (1, 100) < m_sayTextBuffer.chatProbability + g_randGen.Long (2, 10) && ParseChat (reinterpret_cast <char *> (&text)))
|
|
{
|
|
PrepareChatMessage (text);
|
|
PushMessageQueue (GSM_SAY);
|
|
|
|
m_sayTextBuffer.entityIndex = -1;
|
|
m_sayTextBuffer.sayText[0] = 0x0;
|
|
m_sayTextBuffer.timeNextChat = GetWorldTime () + m_sayTextBuffer.chatDelay;
|
|
|
|
return true;
|
|
}
|
|
m_sayTextBuffer.entityIndex = -1;
|
|
m_sayTextBuffer.sayText[0] = 0x0;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void Bot::SayText (const char *text)
|
|
{
|
|
// this function prints saytext message to all players
|
|
|
|
if (IsNullString (text))
|
|
return;
|
|
|
|
FakeClientCommand (GetEntity (), "say \"%s\"", text);
|
|
}
|
|
|
|
void Bot::TeamSayText (const char *text)
|
|
{
|
|
// this function prints saytext message only for teammates
|
|
|
|
if (IsNullString (text))
|
|
return;
|
|
|
|
FakeClientCommand (GetEntity (), "say_team \"%s\"", text);
|
|
}
|