// Copyright (C) 2001-2002 Raven Software. // // ai_util.c #include "g_local.h" #include "q_shared.h" #include "botlib.h" #include "ai_main.h" #ifdef BOT_ZMALLOC #define MAX_BALLOC 8192 void *BAllocList[MAX_BALLOC]; #endif char gBotChatBuffer[MAX_CLIENTS][MAX_CHAT_BUFFER_SIZE]; //A total of 4194304 bytes. Not very nice at all, but we really //want to have at least 65k for the total chat buffer just in //case and we have no method of dynamic allocation here. void *B_TempAlloc(int size) { return trap_VM_LocalTempAlloc(size); } void B_TempFree(int size) { trap_VM_LocalTempFree(size); } void *B_Alloc(int size) { #ifdef BOT_ZMALLOC void *ptr = NULL; int i = 0; #ifdef BOTMEMTRACK int free = 0; int used = 0; while (i < MAX_BALLOC) { if (!BAllocList[i]) { free++; } else { used++; } i++; } Com_Printf("Allocations used: %i\nFree allocation slots: %i\n", used, free); i = 0; #endif ptr = trap_BotGetMemoryGame(size); while (i < MAX_BALLOC) { if (!BAllocList[i]) { BAllocList[i] = ptr; break; } i++; } if (i == MAX_BALLOC) { //If this happens we'll have to rely on this chunk being freed manually with B_Free, which it hopefully will be #ifdef DEBUG Com_Printf("WARNING: MAXIMUM B_ALLOC ALLOCATIONS EXCEEDED\n"); #endif } return ptr; #else return trap_VM_LocalAlloc(size); #endif } void B_Free(void *ptr) { #ifdef BOT_ZMALLOC int i = 0; #ifdef BOTMEMTRACK int free = 0; int used = 0; while (i < MAX_BALLOC) { if (!BAllocList[i]) { free++; } else { used++; } i++; } Com_Printf("Allocations used: %i\nFree allocation slots: %i\n", used, free); i = 0; #endif while (i < MAX_BALLOC) { if (BAllocList[i] == ptr) { BAllocList[i] = NULL; break; } i++; } if (i == MAX_BALLOC) { //Likely because the limit was exceeded and we're now freeing the chunk manually as we hoped would happen #ifdef DEBUG Com_Printf("WARNING: Freeing allocation which is not in the allocation structure\n"); #endif } trap_BotFreeMemoryGame(ptr); #endif } void B_InitAlloc(void) { #ifdef BOT_ZMALLOC memset(BAllocList, 0, sizeof(BAllocList)); #endif memset(gWPArray, 0, sizeof(gWPArray)); } void B_CleanupAlloc(void) { #ifdef BOT_ZMALLOC int i = 0; while (i < MAX_BALLOC) { if (BAllocList[i]) { trap_BotFreeMemoryGame(BAllocList[i]); BAllocList[i] = NULL; } i++; } #endif } int GetValueGroup(char *buf, char *group, char *outbuf) { char *place, *placesecond; int iplace; int failure; int i; int startpoint, startletter; int subg = 0; i = 0; iplace = 0; place = strstr(buf, group); if (!place) { return 0; } startpoint = place - buf + strlen(group) + 1; startletter = (place - buf) - 1; failure = 0; while (buf[startpoint+1] != '{' || buf[startletter] != '\n') { placesecond = strstr(place+1, group); if (placesecond) { startpoint += (placesecond - place); startletter += (placesecond - place); place = placesecond; } else { failure = 1; break; } } if (failure) { return 0; } //we have found the proper group name if we made it here, so find the opening brace and read into the outbuf //until hitting the end brace while (buf[startpoint] != '{') { startpoint++; } startpoint++; while (buf[startpoint] != '}' || subg) { if (buf[startpoint] == '{') { subg++; } else if (buf[startpoint] == '}') { subg--; } outbuf[i] = buf[startpoint]; i++; startpoint++; } outbuf[i] = '\0'; return 1; } int GetPairedValue(char *buf, char *key, char *outbuf) { char *place, *placesecond; int startpoint, startletter; int i, found; if (!buf || !key || !outbuf) { return 0; } i = 0; while (buf[i] && buf[i] != '\0') { if (buf[i] == '/') { if (buf[i+1] && buf[i+1] != '\0' && buf[i+1] == '/') { while (buf[i] != '\n') { buf[i] = '/'; i++; } } } i++; } place = strstr(buf, key); if (!place) { return 0; } //tab == 9 startpoint = place - buf + strlen(key); startletter = (place - buf) - 1; found = 0; while (!found) { if (startletter == 0 || !buf[startletter] || buf[startletter] == '\0' || buf[startletter] == 9 || buf[startletter] == ' ' || buf[startletter] == '\n') { if (buf[startpoint] == '\0' || buf[startpoint] == 9 || buf[startpoint] == ' ' || buf[startpoint] == '\n') { found = 1; break; } } placesecond = strstr(place+1, key); if (placesecond) { startpoint += placesecond - place; startletter += placesecond - place; place = placesecond; } else { place = NULL; break; } } if (!found || !place || !buf[startpoint] || buf[startpoint] == '\0') { return 0; } while (buf[startpoint] == ' ' || buf[startpoint] == 9 || buf[startpoint] == '\n') { startpoint++; } i = 0; while (buf[startpoint] && buf[startpoint] != '\0' && buf[startpoint] != '\n') { outbuf[i] = buf[startpoint]; i++; startpoint++; } outbuf[i] = '\0'; return 1; } int BotDoChat(bot_state_t *bs, char *section, int always) { char *chatgroup; int rVal; int inc_1; int inc_2; int inc_n; int lines; int checkedline; int getthisline; gentity_t *cobject; if (!bs->canChat) { return 0; } if (bs->doChat) { //already have a chat scheduled return 0; } if (Q_irand(1, 10) > bs->chatFrequency && !always) { return 0; } bs->chatTeam = 0; chatgroup = (char *)B_TempAlloc(MAX_CHAT_BUFFER_SIZE); rVal = GetValueGroup(gBotChatBuffer[bs->client], section, chatgroup); if (!rVal) //the bot has no group defined for the specified chat event { B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup return 0; } inc_1 = 0; inc_2 = 2; while (chatgroup[inc_2] && chatgroup[inc_2] != '\0') { if (chatgroup[inc_2] != 13 && chatgroup[inc_2] != 9) { chatgroup[inc_1] = chatgroup[inc_2]; inc_1++; } inc_2++; } chatgroup[inc_1] = '\0'; inc_1 = 0; lines = 0; while (chatgroup[inc_1] && chatgroup[inc_1] != '\0') { if (chatgroup[inc_1] == '\n') { lines++; } inc_1++; } if (!lines) { B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup return 0; } getthisline = Q_irand(0, (lines+1)); if (getthisline < 1) { getthisline = 1; } if (getthisline > lines) { getthisline = lines; } checkedline = 1; inc_1 = 0; while (checkedline != getthisline) { if (chatgroup[inc_1] && chatgroup[inc_1] != '\0') { if (chatgroup[inc_1] == '\n') { inc_1++; checkedline++; } } if (checkedline == getthisline) { break; } inc_1++; } //we're at the starting position of the desired line here inc_2 = 0; while (chatgroup[inc_1] != '\n') { chatgroup[inc_2] = chatgroup[inc_1]; inc_2++; inc_1++; } chatgroup[inc_2] = '\0'; //trap_EA_Say(bs->client, chatgroup); inc_1 = 0; inc_2 = 0; if (strlen(chatgroup) > MAX_CHAT_LINE_SIZE) { B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup return 0; } while (chatgroup[inc_1]) { if (chatgroup[inc_1] == '%' && chatgroup[inc_1+1] != '%') { inc_1++; if (chatgroup[inc_1] == 's' && bs->chatObject) { cobject = bs->chatObject; } else if (chatgroup[inc_1] == 'a' && bs->chatAltObject) { cobject = bs->chatAltObject; } else { cobject = NULL; } if (cobject && cobject->client) { inc_n = 0; while (cobject->client->pers.netname[inc_n]) { bs->currentChat[inc_2] = cobject->client->pers.netname[inc_n]; inc_2++; inc_n++; } inc_2--; //to make up for the auto-increment below } } else { bs->currentChat[inc_2] = chatgroup[inc_1]; } inc_2++; inc_1++; } bs->currentChat[inc_2] = '\0'; if (strcmp(section, "GeneralGreetings") == 0) { bs->doChat = 2; } else { bs->doChat = 1; } bs->chatTime_stored = (strlen(bs->currentChat)*45)+Q_irand(1300, 1500); bs->chatTime = level.time + bs->chatTime_stored; B_TempFree(MAX_CHAT_BUFFER_SIZE); //chatgroup return 1; } void ParseEmotionalAttachments(bot_state_t *bs, char *buf) { int i = 0; int i_c = 0; char tbuf[16]; while (buf[i] && buf[i] != '}') { while (buf[i] == ' ' || buf[i] == '{' || buf[i] == 9 || buf[i] == 13 || buf[i] == '\n') { i++; } if (buf[i] && buf[i] != '}') { i_c = 0; while (buf[i] != '{' && buf[i] != 9 && buf[i] != 13 && buf[i] != '\n') { bs->loved[bs->lovednum].name[i_c] = buf[i]; i_c++; i++; } bs->loved[bs->lovednum].name[i_c] = '\0'; while (buf[i] == ' ' || buf[i] == '{' || buf[i] == 9 || buf[i] == 13 || buf[i] == '\n') { i++; } i_c = 0; while (buf[i] != '{' && buf[i] != 9 && buf[i] != 13 && buf[i] != '\n') { tbuf[i_c] = buf[i]; i_c++; i++; } tbuf[i_c] = '\0'; bs->loved[bs->lovednum].level = atoi(tbuf); bs->lovednum++; } else { break; } if (bs->lovednum >= MAX_LOVED_ONES) { return; } i++; } } int ReadChatGroups(bot_state_t *bs, char *buf) { char *cgroupbegin; int cgbplace; int i; cgroupbegin = strstr(buf, "BEGIN_CHAT_GROUPS"); if (!cgroupbegin) { return 0; } if (strlen(cgroupbegin) >= MAX_CHAT_BUFFER_SIZE) { Com_Printf(S_COLOR_RED "Error: Personality chat section exceeds max size\n"); return 0; } cgbplace = cgroupbegin - buf+1; while (buf[cgbplace] != '\n') { cgbplace++; } i = 0; while (buf[cgbplace] && buf[cgbplace] != '\0') { gBotChatBuffer[bs->client][i] = buf[cgbplace]; i++; cgbplace++; } gBotChatBuffer[bs->client][i] = '\0'; return 1; } char personalityBuffer[131072]; void BotUtilizePersonality(bot_state_t *bs) { fileHandle_t f; int len, rlen; int failed; int i; char *readbuf, *group; len = trap_FS_FOpenFile(bs->settings.personalityfile, &f, FS_READ); failed = 0; if (!f) { Com_Printf(S_COLOR_RED "Error: Specified personality not found\n"); return; } if (len >= 131072) { Com_Printf(S_COLOR_RED "Personality file exceeds maximum length\n"); return; } trap_FS_Read(personalityBuffer, len, f); rlen = len; while (len < 131072) { //kill all characters after the file length, since sometimes FS_Read doesn't do that entirely (or so it seems) personalityBuffer[len] = '\0'; len++; } len = rlen; readbuf = (char *)trap_VM_LocalTempAlloc(1024); group = (char *)trap_VM_LocalTempAlloc(65536); if (!GetValueGroup(personalityBuffer, "GeneralBotInfo", group)) { Com_Printf(S_COLOR_RED "Personality file contains no GeneralBotInfo group\n"); failed = 1; //set failed so we know to set everything to default values } if (!failed && GetPairedValue(group, "reflex", readbuf)) { bs->skills.reflex = atoi(readbuf); } else { bs->skills.reflex = 100; //default } if (!failed && GetPairedValue(group, "accuracy", readbuf)) { bs->skills.accuracy = atof(readbuf); } else { bs->skills.accuracy = 10; //default } if (!failed && GetPairedValue(group, "turnspeed", readbuf)) { bs->skills.turnspeed = atof(readbuf); } else { bs->skills.turnspeed = 0.01f; //default } if (!failed && GetPairedValue(group, "turnspeed_combat", readbuf)) { bs->skills.turnspeed_combat = atof(readbuf); } else { bs->skills.turnspeed_combat = 0.05f; //default } if (!failed && GetPairedValue(group, "maxturn", readbuf)) { bs->skills.maxturn = atof(readbuf); } else { bs->skills.maxturn = 360; //default } if (!failed && GetPairedValue(group, "perfectaim", readbuf)) { bs->skills.perfectaim = atoi(readbuf); } else { bs->skills.perfectaim = 0; //default } if (!failed && GetPairedValue(group, "chatability", readbuf)) { bs->canChat = atoi(readbuf); } else { bs->canChat = 0; //default } if (!failed && GetPairedValue(group, "chatfrequency", readbuf)) { bs->chatFrequency = atoi(readbuf); } else { bs->chatFrequency = 5; //default } if (!failed && GetPairedValue(group, "hatelevel", readbuf)) { bs->loved_death_thresh = atoi(readbuf); } else { bs->loved_death_thresh = 3; //default } if (!failed && GetPairedValue(group, "camper", readbuf)) { bs->isCamper = atoi(readbuf); } else { bs->isCamper = 0; //default } if (!failed && GetPairedValue(group, "meleeSpecialist", readbuf)) { bs->meleeSpecialist = atoi(readbuf); } else { bs->meleeSpecialist = 0; //default } i = 0; while (i < MAX_CHAT_BUFFER_SIZE) { //clear out the chat buffer for this bot gBotChatBuffer[bs->client][i] = '\0'; i++; } if (bs->canChat) { if (!ReadChatGroups(bs, personalityBuffer)) { bs->canChat = 0; } } if (GetValueGroup(personalityBuffer, "BotWeaponWeights", group)) { for (i=0; i < WP_NUM_WEAPONS; i++) { if (GetPairedValue(group, bg_weaponNames[i], readbuf)) { bs->botWeaponWeights[i] = atoi(readbuf); } } } bs->lovednum = 0; if (GetValueGroup(personalityBuffer, "EmotionalAttachments", group)) { ParseEmotionalAttachments(bs, group); } trap_VM_LocalTempFree(65536); trap_VM_LocalTempFree(1024); trap_FS_FCloseFile(f); }