yapb-noob-edition/source/chatlib.cpp

452 lines
14 KiB
C++
Raw Normal View History

2014-07-30 14:17:46 +04:00
//
// Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd").
// Copyright (c) YaPB Development Team.
2014-07-30 14:17:46 +04:00
//
// This software is licensed under the BSD-style license.
// Additional exceptions apply. For full license details, see LICENSE.txt or visit:
// https://yapb.jeefo.net/license
2014-07-30 14:17:46 +04:00
//
#include <core.h>
ConVar yb_chat ("yb_chat", "1");
void StripTags (char *buffer)
{
// this function strips 'clan' tags specified below in given string buffer
const char *tagOpen[] = {"-=", "-[", "-]", "-}", "-{", "<[", "<]", "[-", "]-", "{-", "}-", "[[", "[", "{", "]", "}", "<", ">", "-", "|", "=", "+", "(", ")"};
const char *tagClose[] = {"=-", "]-", "[-", "{-", "}-", "]>", "[>", "-]", "-[", "-}", "-{", "]]", "]", "}", "[", "{", ">", "<", "-", "|", "=", "+", ")", "("};
2014-07-30 14:17:46 +04:00
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)
2014-07-30 14:17:46 +04:00
{
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')
{
String::TrimExternalBuffer (buffer); // if so, string is just a tag
2014-07-30 14:17:46 +04:00
2015-07-11 19:54:46 +03:00
int tagLength = 0;
2015-06-04 11:52:48 +03:00
2014-07-30 14:17:46 +04:00
// strip just the tag part...
2015-07-11 19:54:46 +03:00
for (index = 0; index < ARRAYSIZE_HLSDK (tagOpen); index++)
2014-07-30 14:17:46 +04:00
{
fieldStart = strstr (buffer, tagOpen[index]) - buffer; // look for a tag start
// have we found a tag start?
if (fieldStart >= 0 && fieldStart < 32)
{
2015-06-04 11:52:48 +03:00
tagLength = strlen (tagOpen[index]);
2014-07-30 14:17:46 +04:00
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)
{
2015-06-04 11:52:48 +03:00
tagLength = strlen (tagClose[index]);
2014-07-30 14:17:46 +04:00
for (i = fieldStart; i < length - tagLength; i++)
buffer[i] = buffer[i + tagLength]; // overwrite the buffer with the stripped string
2015-07-11 19:54:46 +03:00
2014-07-30 14:17:46 +04:00
buffer[i] = 0; // terminate the string
}
}
}
}
String::TrimExternalBuffer (buffer); // to finish, strip eventual blanks after and before the tag marks
2014-07-30 14:17:46 +04:00
}
char *HumanizeName (char *name)
{
// this function humanize player name (i.e. trim clan and switch to lower case (sometimes))
2015-06-29 20:51:25 +03:00
static char outputName[64]; // create return name buffer
strncpy (outputName, name, SIZEOF_CHAR (outputName)); // copy name to new buffer
2014-07-30 14:17:46 +04:00
// drop tag marks, 80 percent of time
if (Random.Int (1, 100) < 80)
2014-07-30 14:17:46 +04:00
StripTags (outputName);
else
String::TrimExternalBuffer (outputName);
2014-07-30 14:17:46 +04:00
// sometimes switch name to lower characters
// note: since we're using russian names written in english, we reduce this shit to 6 percent
if (Random.Int (1, 100) <= 6)
2014-07-30 14:17:46 +04:00
{
for (int i = 0; i < static_cast <int> (strlen (outputName)); i++)
outputName[i] = static_cast <char> (tolower (static_cast <int> (outputName[i]))); // to lower case
2014-07-30 14:17:46 +04:00
}
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 (Random.Int (1, 100) <= 4)
2014-07-30 14:17:46 +04:00
{
for (i = 0; i < length; i++)
buffer[i] = static_cast <char> (tolower (static_cast <int> (buffer[i])));; // switch to lowercase
2014-07-30 14:17:46 +04:00
}
if (length > 15)
{
// "length / 2" percent of time drop a character
if (Random.Int (1, 100) < (length / 2))
2014-07-30 14:17:46 +04:00
{
int pos = Random.Int ((length / 8), length - (length / 8)); // chose random position in string
2014-07-30 14:17:46 +04:00
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 (Random.Int (1, 100) < (length / 4))
2014-07-30 14:17:46 +04:00
{
int pos = Random.Int ((length / 8), ((3 * length) / 8)); // choose random position in string
2014-07-30 14:17:46 +04:00
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;
#define ASSIGN_TALK_ENTITY() if (!engine.IsNullEntity (talkEntity)) strncat (m_tempStrings, HumanizeName (const_cast <char *> (STRING (talkEntity->v.netname))), SIZEOF_CHAR (m_tempStrings))
2014-07-30 14:17:46 +04:00
memset (&m_tempStrings, 0, sizeof (m_tempStrings));
char *textStart = text;
char *pattern = text;
edict_t *talkEntity = nullptr;
2014-07-30 14:17:46 +04:00
while (pattern != nullptr)
2014-07-30 14:17:46 +04:00
{
// all replacement placeholders start with a %
pattern = strstr (textStart, "%");
if (pattern != nullptr)
2014-07-30 14:17:46 +04:00
{
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;
2016-03-01 13:37:10 +03:00
for (int i = 0; i < engine.MaxClients (); i++)
2014-07-30 14:17:46 +04:00
{
2016-09-10 19:31:38 +03:00
const Client &client = g_clients[i];
if (!(client.flags & CF_USED) || client.ent == GetEntity ())
2014-07-30 14:17:46 +04:00
continue;
2016-09-10 19:31:38 +03:00
int frags = static_cast <int> (client.ent->v.frags);
2014-07-30 14:17:46 +04:00
if (frags > highestFrags)
{
highestFrags = frags;
index = i;
}
}
talkEntity = g_clients[index].ent;
ASSIGN_TALK_ENTITY ();
2014-07-30 14:17:46 +04:00
}
// mapname?
else if (*pattern == 'm')
2016-03-01 13:37:10 +03:00
strncat (m_tempStrings, engine.GetMapName (), SIZEOF_CHAR (m_tempStrings));
2014-07-30 14:17:46 +04:00
// roundtime?
else if (*pattern == 'r')
{
2016-03-01 13:37:10 +03:00
int time = static_cast <int> (g_timeRoundEnd - engine.Time ());
2015-06-29 22:06:55 +03:00
strncat (m_tempStrings, FormatBuffer ("%02d:%02d", time / 60, time % 60), SIZEOF_CHAR (m_tempStrings));
2014-07-30 14:17:46 +04:00
}
// chat reply?
else if (*pattern == 's')
{
talkEntity = engine.EntityOfIndex (m_sayTextBuffer.entityIndex);
ASSIGN_TALK_ENTITY ();
2014-07-30 14:17:46 +04:00
}
// teammate alive?
else if (*pattern == 't')
{
int i;
2016-03-01 13:37:10 +03:00
for (i = 0; i < engine.MaxClients (); i++)
2014-07-30 14:17:46 +04:00
{
2016-09-10 19:31:38 +03:00
const Client &client = g_clients[i];
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team != m_team || client.ent == GetEntity ())
2014-07-30 14:17:46 +04:00
continue;
break;
}
2016-03-01 13:37:10 +03:00
if (i < engine.MaxClients ())
2014-07-30 14:17:46 +04:00
{
if (!engine.IsNullEntity (pev->dmg_inflictor) && m_team == engine.GetTeam (pev->dmg_inflictor))
2014-07-30 14:17:46 +04:00
talkEntity = pev->dmg_inflictor;
else
talkEntity = g_clients[i].ent;
ASSIGN_TALK_ENTITY ();
2014-07-30 14:17:46 +04:00
}
else // no teammates alive...
{
2016-03-01 13:37:10 +03:00
for (i = 0; i < engine.MaxClients (); i++)
2014-07-30 14:17:46 +04:00
{
2016-09-10 19:31:38 +03:00
const Client &client = g_clients[i];
if (!(client.flags & CF_USED) || client.team != m_team || client.ent == GetEntity ())
2014-07-30 14:17:46 +04:00
continue;
break;
}
2016-03-01 13:37:10 +03:00
if (i < engine.MaxClients ())
2014-07-30 14:17:46 +04:00
{
talkEntity = g_clients[i].ent;
ASSIGN_TALK_ENTITY ();
2014-07-30 14:17:46 +04:00
}
}
}
else if (*pattern == 'e')
{
int i;
2016-03-01 13:37:10 +03:00
for (i = 0; i < engine.MaxClients (); i++)
2014-07-30 14:17:46 +04:00
{
2016-09-10 19:31:38 +03:00
const Client &client = g_clients[i];
if (!(client.flags & CF_USED) || !(client.flags & CF_ALIVE) || client.team == m_team || client.ent == GetEntity ())
2014-07-30 14:17:46 +04:00
continue;
2016-09-10 19:31:38 +03:00
2014-07-30 14:17:46 +04:00
break;
}
2016-03-01 13:37:10 +03:00
if (i < engine.MaxClients ())
2014-07-30 14:17:46 +04:00
{
talkEntity = g_clients[i].ent;
ASSIGN_TALK_ENTITY ();
2014-07-30 14:17:46 +04:00
}
else // no teammates alive...
{
2016-03-01 13:37:10 +03:00
for (i = 0; i < engine.MaxClients (); i++)
2014-07-30 14:17:46 +04:00
{
2016-09-10 19:31:38 +03:00
const Client &client = g_clients[i];
if (!(client.flags & CF_USED) || client.team == m_team || client.ent == GetEntity ())
2014-07-30 14:17:46 +04:00
continue;
2016-09-10 19:31:38 +03:00
2014-07-30 14:17:46 +04:00
break;
}
2016-03-01 13:37:10 +03:00
if (i < engine.MaxClients ())
2014-07-30 14:17:46 +04:00
{
talkEntity = g_clients[i].ent;
ASSIGN_TALK_ENTITY ();
2014-07-30 14:17:46 +04:00
}
}
}
else if (*pattern == 'd')
{
if (g_gameFlags & GAME_CZERO)
2014-07-30 14:17:46 +04:00
{
if (Random.Int (1, 100) < 30)
2014-07-30 14:17:46 +04:00
strcat (m_tempStrings, "CZ");
else
strcat (m_tempStrings, "Condition Zero");
}
else if ((g_gameFlags & GAME_CSTRIKE16) || (g_gameFlags & GAME_LEGACY))
2014-07-30 14:17:46 +04:00
{
if (Random.Int (1, 100) < 30)
2014-07-30 14:17:46 +04:00
strcat (m_tempStrings, "CS");
else
strcat (m_tempStrings, "Counter-Strike");
}
}
else if (*pattern == 'v')
{
talkEntity = m_lastVictim;
ASSIGN_TALK_ENTITY ();
2014-07-30 14:17:46 +04:00
}
pattern++;
textStart = pattern;
}
}
if (textStart != nullptr)
{
// let the bots make some mistakes...
char tempString[160];
strncpy (tempString, textStart, SIZEOF_CHAR (tempString));
2014-07-30 14:17:46 +04:00
HumanizeChat (tempString);
2015-06-29 22:06:55 +03:00
strncat (m_tempStrings, tempString, SIZEOF_CHAR (m_tempStrings));
}
2014-07-30 14:17:46 +04:00
}
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;
FOR_EACH_AE (g_replyFactory, i)
2014-07-30 14:17:46 +04:00
{
FOR_EACH_AE (g_replyFactory[i].keywords, j)
2014-07-30 14:17:46 +04:00
{
// check is keyword has occurred in message
if (strstr (tempMessage, g_replyFactory[i].keywords[j].GetBuffer ()) != nullptr)
2014-07-30 14:17:46 +04:00
{
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
FOR_EACH_AE (replies, k)
2014-07-30 14:17:46 +04:00
{
if (strstr (replies[k].GetBuffer (), generatedReply) != nullptr)
2014-07-30 14:17:46 +04:00
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 (Random.Int (1, 100) < 70 && !g_chatFactory[CHAT_NOKW].IsEmpty ())
2014-07-30 14:17:46 +04:00
{
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] = static_cast <char> (tolower (static_cast <int> (tempMessage[i])));
2014-07-30 14:17:46 +04:00
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
2016-03-01 13:37:10 +03:00
if (m_sayTextBuffer.timeNextChat < engine.Time ())
2014-07-30 14:17:46 +04:00
{
if (Random.Int (1, 100) < m_sayTextBuffer.chatProbability + Random.Int (2, 10) && ParseChat (reinterpret_cast <char *> (&text)))
2014-07-30 14:17:46 +04:00
{
PrepareChatMessage (text);
PushMessageQueue (GAME_MSG_SAY_CMD);
2014-07-30 14:17:46 +04:00
m_sayTextBuffer.entityIndex = -1;
m_sayTextBuffer.sayText[0] = 0x0;
2016-03-01 13:37:10 +03:00
m_sayTextBuffer.timeNextChat = engine.Time () + m_sayTextBuffer.chatDelay;
2014-07-30 14:17:46 +04:00
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;
engine.IssueBotCommand (GetEntity (), "say \"%s\"", text);
2014-07-30 14:17:46 +04:00
}
void Bot::TeamSayText (const char *text)
{
// this function prints saytext message only for teammates
if (IsNullString (text))
return;
engine.IssueBotCommand (GetEntity (), "say_team \"%s\"", text);
2014-07-30 14:17:46 +04:00
}