/* ================== BotAddressedToBot ================== */ int BotAddressedToBot(bot_state_t *bs, bot_match_t *match) { char addressedto[MAX_MESSAGE_SIZE]; char netname[MAX_MESSAGE_SIZE]; char name[MAX_MESSAGE_SIZE]; char botname[128]; int client; bot_match_t addresseematch; trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientOnSameTeamFromName(bs, netname); if (client < 0) return qfalse; //if the message is addressed to someone if (match->subtype & ST_ADDRESSED) { trap_BotMatchVariable(match, ADDRESSEE, addressedto, sizeof(addressedto)); //the name of this bot ClientName(bs->client, botname, 128); // while(trap_BotFindMatch(addressedto, &addresseematch, MTCONTEXT_ADDRESSEE)) { if (addresseematch.type == MSG_EVERYONE) { return qtrue; } else if (addresseematch.type == MSG_MULTIPLENAMES) { trap_BotMatchVariable(&addresseematch, TEAMMATE, name, sizeof(name)); if (strlen(name)) { if (stristr(botname, name)) return qtrue; if (stristr(bs->subteam, name)) return qtrue; } trap_BotMatchVariable(&addresseematch, MORE, addressedto, MAX_MESSAGE_SIZE); } else { trap_BotMatchVariable(&addresseematch, TEAMMATE, name, MAX_MESSAGE_SIZE); if (strlen(name)) { if (stristr(botname, name)) return qtrue; if (stristr(bs->subteam, name)) return qtrue; } break; } } //Com_sprintf(buf, sizeof(buf), "not addressed to me but %s", addressedto); //trap_EA_Say(bs->client, buf); return qfalse; } else { bot_match_t tellmatch; tellmatch.type = 0; //if this message wasn't directed solely to this bot if (!trap_BotFindMatch(match->string, &tellmatch, MTCONTEXT_REPLYCHAT) || tellmatch.type != MSG_CHATTELL) { //make sure not everyone reacts to this message if (random() > (float ) 1.0 / (NumPlayersOnSameTeam(bs)-1)) return qfalse; } } return qtrue; }
/* ================= BotAddresseeMatch ================= */ qboolean BotAddresseeMatch(bot_state_t *bs, bot_match_t *match) { int teammates; char addressee[MAX_MESSAGE_SIZE]; char name[MAX_MESSAGE_SIZE]; char *botname; bot_match_t submatch; // If the message isn't addressed to anyone in particular, the bot may or may not react if ( !(match->subtype & ST_ADDRESSED) ) { // If the message was only given to this bot, the bot definitely reacts submatch.type = 0; if (trap_BotFindMatch(match->string, &submatch, MTCONTEXT_REPLYCHAT) && submatch.type == MSG_CHATTELL) return qtrue; // The bot still might randomly react, though it's less likely with more teammates teammates = BotTeammates(bs); if (!teammates) return qtrue; return (random() <= 1.0 / teammates); } // Search for the bot's name in the message's addressee list botname = SimplifyName(EntityNameFast(bs->ent)); trap_BotMatchVariable(match, ADDRESSEE, addressee, sizeof(addressee)); while (trap_BotFindMatch(addressee, &submatch, MTCONTEXT_ADDRESSEE)) { // If matching everyone on the team, automatically respond if (submatch.type == MSG_EVERYONE) return qtrue; // The bot responds if the next name matches its name or its subteam's name trap_BotMatchVariable(&submatch, TEAMMATE, name, sizeof(name)); if (strlen(name)) { if (stristr(botname, name)) return qtrue; if (stristr(bs->subteam, name)) return qtrue; } // Exit if this is the last name in the list if (submatch.type != MSG_MULTIPLENAMES) break; // Get the next name in the list trap_BotMatchVariable(&submatch, MORE, addressee, sizeof(addressee)); } // The bot was not found in the specific addressee list return qfalse; }
/* ======================================================================================================================================= BotGetTime ======================================================================================================================================= */ float BotGetTime(bot_match_t *match) { bot_match_t timematch; char timestring[MAX_MESSAGE_SIZE]; float t; // if the matched string has a time if (match->subtype & ST_TIME) { // get the time string trap_BotMatchVariable(match, TIME, timestring, MAX_MESSAGE_SIZE); // match it to find out if the time is in seconds or minutes if (trap_BotFindMatch(timestring, &timematch, MTCONTEXT_TIME)) { if (timematch.type == MSG_FOREVER) { t = 99999999; } else { trap_BotMatchVariable(&timematch, TIME, timestring, MAX_MESSAGE_SIZE); if (timematch.type == MSG_MINUTES) { t = atof(timestring) * 60; } else if (timematch.type == MSG_SECONDS) { t = atof(timestring); } else {t = 0;} } // if there's a valid time if (t > 0) { return trap_AAS_Time() + t; } } } return 0; }
/* ================== BotMatchMessage ================== */ int BotMatchMessage(bot_state_t *bs, char *message) { bot_match_t match; match.type = 0; //if it is an unknown message if (!trap_BotFindMatch(message, &match, MTCONTEXT_MISC|MTCONTEXT_INITIALTEAMCHAT )) { if(bot_developer.integer & AIDBG_CHAT ){ G_Printf("^2no match for ^1%s\n", message); } return qfalse; } if(bot_developer.integer & AIDBG_CHAT){ G_Printf("^6match %d for^1 %s\n", match.type, message); } //react to the found message switch(match.type) { case MSG_WRONGWALL:{ BotMatch_WrongWall(bs, &match); break; } //case MSG_GOFORBALLOON:{ //someone calling for company // BotMatch_GoForBalloon(bs, &match); // break; //} case MSG_DROPCART:{ BotMatch_DropCart(bs, &match); break; } case MSG_GETITEM:{ BotMatch_GetItem(bs, &match); break; } case MSG_ENTERGAME:{ //someone entered the game BotMatch_EnterGame(bs, &match); break; } case MSG_CATCHME:{ BotMatch_CatchMe(bs, &match); break; } default:{ if(bot_developer.integer) BotAI_Print(PRT_MESSAGE, "unknown match type %d\n",match.type); break; } } return qtrue; }
/* =============== BotMatchMessage This function returns if the message could be matched. It does not mean the bot actually processed the message. NOTE: Death messages are sent as an EV_OBITUARY event, not actual console messages. As such, they are processed by BotCheckEvents() in ai_scan.c. =============== */ qboolean BotMatchMessage(bot_state_t *bs, char *message) { bot_match_t match; char name[MAX_MESSAGE_SIZE]; gentity_t *sender; // Try to match this message as a CTF teamchat message match.type = 0; if (!trap_BotFindMatch(message, &match, MTCONTEXT_MISC | MTCONTEXT_INITIALTEAMCHAT | MTCONTEXT_CTF)) return qfalse; // Ignore messages in deathmatch modes, but return true because it's a real message if ( !(game_style & GS_TEAM) ) return qtrue; // Check if this message is a team management message trap_BotMatchVariable(&match, NETNAME, name, sizeof(name)); sender = TeammateFromName(bs, name); if (BotMatch_Team(bs, &match, sender)) return qtrue; // Ignore messages not from a teammate if (!sender) { Bot_InitialChat(bs, "whois", name, NULL); trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return qtrue; } // Ignore other messages if they aren't intended for this bot if (!BotAddresseeMatch(bs, &match)) return qtrue; // Check if this message is an order if (BotMatch_Order(bs, &match, sender)) return qtrue; // Check if this message is a subteam request if (BotMatch_Subteam(bs, &match, sender)) return qtrue; // Still return true because the message matched-- our code just elected not to process it BotAI_Print(PRT_WARNING, "Unknown match type %i\n", match.type); return qtrue; }
/* ======================================================================================================================================= BotMatch_HelpAccompany ======================================================================================================================================= */ void BotMatch_HelpAccompany(bot_state_t *bs, bot_match_t *match) { int client, other, areanum; char teammate[MAX_MESSAGE_SIZE], netname[MAX_MESSAGE_SIZE]; char itemname[MAX_MESSAGE_SIZE]; bot_match_t teammatematch; aas_entityinfo_t entinfo; if (!TeamPlayIsOn()) { return; } // if not addressed to this bot if (!BotAddressedToBot(bs, match)) { return; } // get the team mate name trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate)); // get the client to help if (trap_BotFindMatch(teammate, &teammatematch, MTCONTEXT_TEAMMATE) && // if someone asks for him or herself teammatematch.type == MSG_ME) { // get the netname trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname)); client = ClientFromName(netname); other = qfalse; } else { // asked for someone else client = FindClientByName(teammate); // if this is the bot self if (client == bs->client) { other = qfalse; } else if (!BotSameTeam(bs, client)) { // FIXME: say "I don't help the enemy" return; } else { other = qtrue; } } // if the bot doesn't know who to help (FindClientByName returned -1) if (client < 0) { if (other) { BotAI_BotInitialChat(bs, "whois", teammate, NULL); } else {BotAI_BotInitialChat(bs, "whois", netname, NULL);} trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } // don't help or accompany yourself if (client == bs->client) { return; } bs->teamgoal.entitynum = -1; BotEntityInfo(client, &entinfo); // if info is valid (in PVS) if (entinfo.valid) { areanum = BotPointAreaNum(entinfo.origin); if (areanum && trap_AAS_AreaReachability(areanum)) { bs->teamgoal.entitynum = client; bs->teamgoal.areanum = areanum; VectorCopy(entinfo.origin, bs->teamgoal.origin); VectorSet(bs->teamgoal.mins, -8, -8, -8); VectorSet(bs->teamgoal.maxs, 8, 8, 8); } } // if no teamgoal yet if (bs->teamgoal.entitynum < 0) { // if near an item if (match->subtype & ST_NEARITEM) { // get the match variable trap_BotMatchVariable(match, ITEM, itemname, sizeof(itemname)); if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) { // BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL); // trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } } } if (bs->teamgoal.entitynum < 0) { if (other) { BotAI_BotInitialChat(bs, "whereis", teammate, NULL); } else {BotAI_BotInitialChat(bs, "whereareyou", netname, NULL);} trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); return; } // the team mate bs->teammate = client; // last time the team mate was assumed visible bs->teammatevisible_time = trap_AAS_Time(); // set the time to send a message to the team mates bs->teammessage_time = trap_AAS_Time() + 2 * random(); // get the team goal time bs->teamgoal_time = BotGetTime(match); // set the ltg type if (match->type == MSG_HELP) { bs->ltgtype = LTG_TEAMHELP; if (!bs->teamgoal_time) { bs->teamgoal_time = trap_AAS_Time() + TEAM_HELP_TIME; } } else { bs->ltgtype = LTG_TEAMACCOMPANY; if (!bs->teamgoal_time) { bs->teamgoal_time = trap_AAS_Time() + TEAM_ACCOMPANY_TIME; } bs->formation_dist = 3.5 * 32; // 3.5 meter bs->arrive_time = 0; } #ifdef DEBUG BotPrintTeamGoal(bs); #endif // DEBUG }
/* ======================================================================================================================================= BotGetPatrolWaypoints ======================================================================================================================================= */ int BotGetPatrolWaypoints(bot_state_t *bs, bot_match_t *match) { char keyarea[MAX_MESSAGE_SIZE]; int patrolflags; bot_waypoint_t *wp, *newwp, *newpatrolpoints; bot_match_t keyareamatch; bot_goal_t goal; newpatrolpoints = NULL; patrolflags = 0; trap_BotMatchVariable(match, KEYAREA, keyarea, MAX_MESSAGE_SIZE); while (1) { if (!trap_BotFindMatch(keyarea, &keyareamatch, MTCONTEXT_PATROLKEYAREA)) { trap_EA_SayTeam(bs->client, "what do you say?"); BotFreeWaypoints(newpatrolpoints); bs->patrolpoints = NULL; return qfalse; } trap_BotMatchVariable(&keyareamatch, KEYAREA, keyarea, MAX_MESSAGE_SIZE); if (!BotGetMessageTeamGoal(bs, keyarea, &goal)) { // BotAI_BotInitialChat(bs, "cannotfind", keyarea, NULL); // trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM); BotFreeWaypoints(newpatrolpoints); bs->patrolpoints = NULL; return qfalse; } // create a new waypoint newwp = BotCreateWayPoint(keyarea, goal.origin, goal.areanum); // add the waypoint to the patrol points newwp->next = NULL; for (wp = newpatrolpoints; wp && wp->next; wp = wp->next); if (!wp) { newpatrolpoints = newwp; newwp->prev = NULL; } else { wp->next = newwp; newwp->prev = wp; } if (keyareamatch.subtype & ST_BACK) { patrolflags = PATROL_LOOP; break; } else if (keyareamatch.subtype & ST_REVERSE) { patrolflags = PATROL_REVERSE; break; } else if (keyareamatch.subtype & ST_MORE) { trap_BotMatchVariable(&keyareamatch, MORE, keyarea, MAX_MESSAGE_SIZE); } else { break; } } if (!newpatrolpoints || !newpatrolpoints->next) { trap_EA_SayTeam(bs->client, "I need more key points to patrol\n"); BotFreeWaypoints(newpatrolpoints); newpatrolpoints = NULL; return qfalse; } BotFreeWaypoints(bs->patrolpoints); bs->patrolpoints = newpatrolpoints; bs->curpatrolpoint = bs->patrolpoints; bs->patrolflags = patrolflags; return qtrue; }
/* ======================================================================================================================================= BotMatchMessage ======================================================================================================================================= */ int BotMatchMessage(bot_state_t *bs, char *message) { bot_match_t match; match.type = 0; // if it is an unknown message if (!trap_BotFindMatch(message, &match, MTCONTEXT_ENTERGAME |MTCONTEXT_INITIALTEAMCHAT |MTCONTEXT_CTF)) { return qfalse; } // react to the found message switch (match.type) { case MSG_HELP: // someone calling for help case MSG_ACCOMPANY: // someone calling for company { BotMatch_HelpAccompany(bs, &match); break; } case MSG_DEFENDKEYAREA: // teamplay defend a key area { BotMatch_DefendKeyArea(bs, &match); break; } case MSG_CAMP: // camp somewhere { BotMatch_Camp(bs, &match); break; } case MSG_PATROL: // patrol between several key areas { BotMatch_Patrol(bs, &match); break; } #ifdef CTF case MSG_GETFLAG: // ctf get the enemy flag { BotMatch_GetFlag(bs, &match); break; } case MSG_RUSHBASE: // ctf rush to the base { BotMatch_RushBase(bs, &match); break; } case MSG_RETURNFLAG: { BotMatch_ReturnFlag(bs, &match); break; } #endif // CTF case MSG_GETITEM: { BotMatch_GetItem(bs, &match); break; } case MSG_JOINSUBTEAM: // join a sub team { BotMatch_JoinSubteam(bs, &match); break; } case MSG_LEAVESUBTEAM: // leave a sub team { BotMatch_LeaveSubteam(bs, &match); break; } case MSG_WHICHTEAM: { BotMatch_WhichTeam(bs, &match); break; } case MSG_CHECKPOINT: // remember a check point { BotMatch_CheckPoint(bs, &match); break; } case MSG_CREATENEWFORMATION: // start the creation of a new formation { trap_EA_SayTeam(bs->client, "the part of my brain to create formations has been damaged"); break; } case MSG_FORMATIONPOSITION: // tell someone his / her position in the formation { trap_EA_SayTeam(bs->client, "the part of my brain to create formations has been damaged"); break; } case MSG_FORMATIONSPACE: // set the formation space { BotMatch_FormationSpace(bs, &match); break; } case MSG_DOFORMATION: // form a certain formation { break; } case MSG_DISMISS: // dismiss someone { BotMatch_Dismiss(bs, &match); break; } case MSG_STARTTEAMLEADERSHIP: // someone will become the team leader { BotMatch_StartTeamLeaderShip(bs, &match); break; } case MSG_STOPTEAMLEADERSHIP: // someone will stop being the team leader { BotMatch_StopTeamLeaderShip(bs, &match); break; } case MSG_WHOISTEAMLAEDER: { BotMatch_WhoIsTeamLeader(bs, &match); break; } case MSG_WHATAREYOUDOING: // ask a bot what he / she is doing { BotMatch_WhatAreYouDoing(bs, &match); break; } case MSG_WHATISMYCOMMAND: { BotMatch_WhatIsMyCommand(bs, &match); break; } case MSG_WHEREAREYOU: { BotMatch_WhereAreYou(bs, &match); break; } case MSG_LEADTHEWAY: { BotMatch_LeadTheWay(bs, &match); break; } case MSG_KILL: { BotMatch_Kill(bs, &match); break; } case MSG_ENTERGAME: // someone entered the game { // NOTE: eliza chats will catch this // BotMatchVariable(&match, NETNAME, netname); // Com_sprintf(buf, sizeof(buf), "heya %s", netname); // EA_Say(bs->client, buf); break; } case MSG_CTF: { BotMatch_CTF(bs, &match); break; } case MSG_WAIT: { break; } default: { BotAI_Print(PRT_MESSAGE, "unknown match type\n"); break; } } return qtrue; }
/* ======================== BotMatch_PatrolWaypoints ======================== */ qboolean BotMatch_PatrolWaypoints(bot_state_t *bs, bot_match_t *match, gentity_t *sender) { char keyarea[MAX_MESSAGE_SIZE]; int flags; bot_waypoint_t *new_wp, *last_wp, *new_patrol; bot_match_t keyareamatch; bot_goal_t goal; qboolean success; // Initialize new patrol to a zero length path last_wp = new_patrol = NULL; flags = 0; success = qfalse; // Match successive waypoints in the patrol path trap_BotMatchVariable(match, KEYAREA, keyarea, MAX_MESSAGE_SIZE); while (1) { // Fail if the bot can't match the area name if (!trap_BotFindMatch(keyarea, &keyareamatch, MTCONTEXT_PATROLKEYAREA)) { trap_EA_SayTeam(bs->client, "What did you say?"); break; } // Fail if the bot can't find the requested area trap_BotMatchVariable(&keyareamatch, KEYAREA, keyarea, MAX_MESSAGE_SIZE); if (!GoalFromName(&goal, keyarea, bs)) break; // Try to create a new waypoint new_wp = BotCreateWaypoint(keyarea); if (new_wp == NULL) break; // Copy the matched goal to the waypoint memcpy(&new_wp->goal, &goal, sizeof(bot_goal_t)); // Insert waypoint into patrol point list new_wp->next = NULL; if (last_wp) { last_wp->next = new_wp; new_wp->prev = last_wp; } else { // First waypoint in list new_patrol = new_wp; new_wp->prev = NULL; } last_wp = new_wp; // Check for waypoint message completion if (keyareamatch.subtype & ST_REVERSE) { success = qtrue; flags = PATROL_REVERSE; break; } if (keyareamatch.subtype & ST_BACK) { success = qtrue; flags = PATROL_LOOP; break; } if ( !(keyareamatch.subtype & ST_MORE) ) { success = qtrue; flags = PATROL_LOOP; break; } trap_BotMatchVariable(&keyareamatch, MORE, keyarea, MAX_MESSAGE_SIZE); } // Make sure the bot has at least two patrol points if (success && (!new_patrol || !new_patrol->next) ) { trap_EA_SayTeam(bs->client, "I need more key points to patrol\n"); success = qfalse; } // Check for message match failure if (!success) { BotFreeWaypoints(new_patrol); return qfalse; } // Free old waypoints and use new waypoints BotFreeWaypoints(bs->patrol); bs->patrol = new_patrol; bs->next_patrol = bs->patrol; bs->patrol_flags = flags; return qtrue; }