// // Yet Another POD-Bot, based on PODBot by Markus Klinge ("CountFloyd"). // Copyright (c) YaPB Development Team. // // This software is licensed under the BSD-style license. // Additional exceptions apply. For full license details, see LICENSE.txt or visit: // http://yapb.jeefo.net/license // #include ConVar yb_chat ("yb_chat", "1"); void StripTags (char *buffer) { // this function strips 'clan' tags specified below in given string buffer char *tagOpen[] = {"-=", "-[", "-]", "-}", "-{", "<[", "<]", "[-", "]-", "{-", "}-", "[[", "[", "{", "]", "}", "<", ">", "-", "|", "=", "+", "(", ")"}; 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 int tagLength = 0; // 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) { 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) { 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 (Random.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 (Random.Long (1, 100) <= 6) { for (int i = 0; i < static_cast (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 (Random.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 (Random.Long (1, 100) < (length / 2)) { int pos = Random.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 (Random.Long (1, 100) < (length / 4)) { int pos = Random.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 (g_clients[i].ent->v.frags); if (frags > highestFrags) { highestFrags = frags; index = i; } } talkEntity = g_clients[index].ent; if (!IsEntityNull (talkEntity)) strcat (m_tempStrings, HumanizeName (const_cast (STRING (talkEntity->v.netname)))); } // mapname? else if (*pattern == 'm') strcat (m_tempStrings, GetMapName ()); // roundtime? else if (*pattern == 'r') { int time = static_cast (g_timeRoundEnd - GetWorldTime ()); strcat (m_tempStrings, FormatBuffer ("%02d:%02d", time / 60, time % 60)); } // chat reply? else if (*pattern == 's') { talkEntity = EntityOfIndex (m_sayTextBuffer.entityIndex); if (!IsEntityNull (talkEntity)) strcat (m_tempStrings, HumanizeName (const_cast (STRING (talkEntity->v.netname)))); } // teammate alive? else if (*pattern == 't') { 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 != m_team) || (g_clients[i].ent == GetEntity ())) continue; break; } if (i < GetMaxClients ()) { if (!IsEntityNull (pev->dmg_inflictor) && (m_team == GetTeam (pev->dmg_inflictor))) talkEntity = pev->dmg_inflictor; else talkEntity = g_clients[i].ent; if (!IsEntityNull (talkEntity)) strcat (m_tempStrings, HumanizeName (const_cast (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 != m_team) || (g_clients[i].ent == GetEntity ())) continue; break; } if (i < GetMaxClients ()) { talkEntity = g_clients[i].ent; if (!IsEntityNull (talkEntity)) strcat (m_tempStrings, HumanizeName (const_cast (STRING (talkEntity->v.netname)))); } } } else if (*pattern == 'e') { 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 == m_team) || (g_clients[i].ent == GetEntity ())) continue; break; } if (i < GetMaxClients ()) { talkEntity = g_clients[i].ent; if (!IsEntityNull (talkEntity)) strcat (m_tempStrings, HumanizeName (const_cast (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 == m_team) || (g_clients[i].ent == GetEntity ())) continue; break; } if (i < GetMaxClients ()) { talkEntity = g_clients[i].ent; if (!IsEntityNull (talkEntity)) strcat (m_tempStrings, HumanizeName (const_cast (STRING (talkEntity->v.netname)))); } } } else if (*pattern == 'd') { if (g_gameVersion == CSV_CZERO) { if (Random.Long (1, 100) < 30) strcat (m_tempStrings, "CZ"); else strcat (m_tempStrings, "Condition Zero"); } else if (g_gameVersion == CSV_STEAM || g_gameVersion == CSV_OLD) { if (Random.Long (1, 100) < 30) strcat (m_tempStrings, "CS"); else strcat (m_tempStrings, "Counter-Strike"); } } else if (*pattern == 'v') { talkEntity = m_lastVictim; if (!IsEntityNull (talkEntity)) strcat (m_tempStrings, HumanizeName (const_cast (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 &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 (Random.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 (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 (Random.Long (1, 100) < m_sayTextBuffer.chatProbability + Random.Long (2, 10) && ParseChat (reinterpret_cast (&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); }