Files
illusion-arena/code/game/ai_cmd.c
2025-07-15 15:01:00 -03:00

2098 lines
52 KiB
C

/*
===========================================================================
Copyright (C) 1999-2005 Id Software, Inc.
This file is part of Quake III Arena source code.
Quake III Arena source code is free software; you can redistribute it
and/or modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the License,
or (at your option) any later version.
Quake III Arena source code is distributed in the hope that it will be
useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Quake III Arena source code; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
===========================================================================
*/
//
/*****************************************************************************
* name: ai_cmd.c
*
* desc: Quake3 bot AI
*
* $Archive: /MissionPack/code/game/ai_cmd.c $
*
*****************************************************************************/
#include "g_local.h"
#include "../botlib/botlib.h"
#include "../botlib/be_aas.h"
#include "../botlib/be_ea.h"
#include "../botlib/be_ai_char.h"
#include "../botlib/be_ai_chat.h"
#include "../botlib/be_ai_gen.h"
#include "../botlib/be_ai_goal.h"
#include "../botlib/be_ai_move.h"
#include "../botlib/be_ai_weap.h"
//
#include "ai_main.h"
#include "ai_dmq3.h"
#include "ai_chat.h"
#include "ai_cmd.h"
#include "ai_dmnet.h"
#include "ai_team.h"
//
#include "chars.h" //characteristics
#include "inv.h" //indexes into the inventory
#include "syn.h" //synonyms
#include "match.h" //string matching types and vars
// for the voice chats
#include "../../ui/menudef.h"
int notleader[MAX_CLIENTS];
#ifdef DEBUG
/*
==================
BotPrintTeamGoal
==================
*/
void BotPrintTeamGoal(bot_state_t *bs) {
char netname[MAX_NETNAME];
float t;
ClientName(bs->client, netname, sizeof(netname));
t = bs->teamgoal_time - FloatTime();
switch(bs->ltgtype) {
case LTG_TEAMHELP:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna help a team mate for %1.0f secs\n", netname, t);
break;
}
case LTG_TEAMACCOMPANY:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna accompany a team mate for %1.0f secs\n", netname, t);
break;
}
case LTG_GETFLAG:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna get the flag for %1.0f secs\n", netname, t);
break;
}
case LTG_RUSHBASE:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna rush to the base for %1.0f secs\n", netname, t);
break;
}
case LTG_RETURNFLAG:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna try to return the flag for %1.0f secs\n", netname, t);
break;
}
case LTG_ATTACKENEMYBASE:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna attack the enemy base for %1.0f secs\n", netname, t);
break;
}
case LTG_HARVEST:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna harvest for %1.0f secs\n", netname, t);
break;
}
case LTG_DEFENDKEYAREA:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna defend a key area for %1.0f secs\n", netname, t);
break;
}
case LTG_GETITEM:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna get an item for %1.0f secs\n", netname, t);
break;
}
case LTG_KILL:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna kill someone for %1.0f secs\n", netname, t);
break;
}
case LTG_CAMP:
case LTG_CAMPORDER:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna camp for %1.0f secs\n", netname, t);
break;
}
case LTG_PATROL:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna patrol for %1.0f secs\n", netname, t);
break;
}
case LTG_POINTA:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna take care of point A for %1.0f secs\n", netname, t);
break;
}
case LTG_POINTB:
{
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna take care of point B for %1.0f secs\n", netname, t);
break;
}
default:
{
if (bs->ctfroam_time > FloatTime()) {
t = bs->ctfroam_time - FloatTime();
BotAI_Print(PRT_MESSAGE, "%s: I'm gonna roam for %1.0f secs\n", netname, t);
}
else {
BotAI_Print(PRT_MESSAGE, "%s: I've got a regular goal\n", netname);
}
}
}
}
#endif //DEBUG
/*
==================
BotGetItemTeamGoal
FIXME: add stuff like "upper rocket launcher"
"the rl near the railgun", "lower grenade launcher" etc.
==================
*/
int BotGetItemTeamGoal(char *goalname, bot_goal_t *goal) {
int i;
if (!strlen(goalname)) return qfalse;
i = -1;
do {
i = trap_BotGetLevelItemGoal(i, goalname, goal);
if (i > 0) {
//do NOT defend dropped items
if (goal->flags & GFL_DROPPED)
continue;
return qtrue;
}
} while(i > 0);
return qfalse;
}
/*
==================
BotGetMessageTeamGoal
==================
*/
int BotGetMessageTeamGoal(bot_state_t *bs, char *goalname, bot_goal_t *goal) {
bot_waypoint_t *cp;
if (BotGetItemTeamGoal(goalname, goal)) return qtrue;
cp = BotFindWayPoint(bs->checkpoints, goalname);
if (cp) {
memcpy(goal, &cp->goal, sizeof(bot_goal_t));
return qtrue;
}
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.0f;
}
else if (timematch.type == MSG_FORAWHILE) {
t = 10 * 60; // 10 minutes
}
else if (timematch.type == MSG_FORALONGTIME) {
t = 30 * 60; // 30 minutes
}
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 FloatTime() + t;
}
}
return 0;
}
/*
==================
FindClientByName
==================
*/
int FindClientByName(char *name) {
int i;
char buf[MAX_INFO_STRING];
static int maxclients;
if (!maxclients)
maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
ClientName(i, buf, sizeof(buf));
if (!Q_stricmp(buf, name)) return i;
}
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
ClientName(i, buf, sizeof(buf));
if (stristr(buf, name)) return i;
}
return -1;
}
/*
==================
FindEnemyByName
==================
*/
int FindEnemyByName(bot_state_t *bs, char *name) {
int i;
char buf[MAX_INFO_STRING];
static int maxclients;
if (!maxclients)
maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
if (BotSameTeam(bs, i)) continue;
ClientName(i, buf, sizeof(buf));
if (!Q_stricmp(buf, name)) return i;
}
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
if (BotSameTeam(bs, i)) continue;
ClientName(i, buf, sizeof(buf));
if (stristr(buf, name)) return i;
}
return -1;
}
/*
==================
NumPlayersOnSameTeam
==================
*/
int NumPlayersOnSameTeam(bot_state_t *bs) {
int i, num;
char buf[MAX_INFO_STRING];
static int maxclients;
if (!maxclients)
maxclients = trap_Cvar_VariableIntegerValue("sv_maxclients");
num = 0;
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
trap_GetConfigstring(CS_PLAYERS+i, buf, MAX_INFO_STRING);
if (strlen(buf)) {
if (BotSameTeam(bs, i+1)) num++;
}
}
return num;
}
/*
==================
TeamPlayIsOn
==================
*/
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, 0, CHAT_TEAM);
BotFreeWaypoints(newpatrolpoints);
bs->patrolpoints = NULL;
return qfalse;
}
//create a new waypoint
newwp = BotCreateWayPoint(keyarea, goal.origin, goal.areanum);
if (!newwp)
break;
//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;
}
/*
==================
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;
}
/*
==================
BotGPSToPosition
==================
*/
int BotGPSToPosition(char *buf, vec3_t position) {
int i, j = 0;
int num, sign;
for (i = 0; i < 3; i++) {
num = 0;
while(buf[j] == ' ') j++;
if (buf[j] == '-') {
j++;
sign = -1;
}
else {
sign = 1;
}
while (buf[j]) {
if (buf[j] >= '0' && buf[j] <= '9') {
num = num * 10 + buf[j] - '0';
j++;
}
else {
j++;
break;
}
}
BotAI_Print(PRT_MESSAGE, "%d\n", sign * num);
position[i] = (float) sign * num;
}
return qtrue;
}
/*
==================
BotMatch_HelpAccompany
==================
*/
void BotMatch_HelpAccompany(bot_state_t *bs, bot_match_t *match) {
int client, other, areanum;
char teammate[MAX_MESSAGE_SIZE];
char 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);
client = ClientFromName(netname);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
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);
client = ClientFromName(netname);
trap_BotEnterChat(bs->cs, client, CHAT_TEAM);
return;
}
//the team mate
bs->teammate = client;
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = ClientFromName(netname);
//the team mate who ordered
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//last time the team mate was assumed visible
bs->teammatevisible_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 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 = FloatTime() + TEAM_HELP_TIME;
}
else {
bs->ltgtype = LTG_TEAMACCOMPANY;
if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_ACCOMPANY_TIME;
bs->formation_dist = 3.5 * 32; //3.5 meter
bs->arrive_time = 0;
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
}
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_DefendKeyArea
==================
*/
void BotMatch_DefendKeyArea(bot_state_t *bs, bot_match_t *match) {
char itemname[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//get the match variable
trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname));
//
if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) {
//BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
//trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
return;
}
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = ClientFromName(netname);
//the team mate who ordered
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_DEFENDKEYAREA;
//get the team goal time
bs->teamgoal_time = BotGetTime(match);
//set the team goal time
if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_DEFENDKEYAREA_TIME;
//away from defending
bs->defendaway_time = 0;
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_TakeA
For Double Domination
==================
*/
void BotMatch_TakeA(bot_state_t *bs, bot_match_t *match) {
// char itemname[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//get the match variable
/*trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname));
//
if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) {
//BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
//trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
return;
}*/
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = ClientFromName(netname);
//the team mate who ordered
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_POINTA;
//get the team goal time
bs->teamgoal_time = BotGetTime(match);
//set the team goal time
if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + DD_POINTA;
//away from defending
bs->defendaway_time = 0;
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_TakeB
For Double Domination
==================
*/
void BotMatch_TakeB(bot_state_t *bs, bot_match_t *match) {
// char itemname[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//get the match variable
/*trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname));
//
if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) {
//BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
//trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
//return;
}*/
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = ClientFromName(netname);
//the team mate who ordered
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_POINTB;
//get the team goal time
bs->teamgoal_time = BotGetTime(match);
//set the team goal time
if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + DD_POINTA;
//away from defending
bs->defendaway_time = 0;
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_GetItem
==================
*/
void BotMatch_GetItem(bot_state_t *bs, bot_match_t *match) {
char itemname[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//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;
}
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientOnSameTeamFromName(bs, netname);
//
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_GETITEM;
//set the team goal time
bs->teamgoal_time = FloatTime() + TEAM_GETITEM_TIME;
//
BotSetTeamStatus(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_Camp
==================
*/
void BotMatch_Camp(bot_state_t *bs, bot_match_t *match) {
int client, areanum;
char netname[MAX_MESSAGE_SIZE];
char itemname[MAX_MESSAGE_SIZE];
aas_entityinfo_t entinfo;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//asked for someone else
client = FindClientByName(netname);
//if there's no valid client with this name
if (client < 0) {
BotAI_BotInitialChat(bs, "whois", netname, NULL);
trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
return;
}
//get the match variable
trap_BotMatchVariable(match, KEYAREA, itemname, sizeof(itemname));
//in CTF it could be the base
if (match->subtype & ST_THERE) {
//camp at the spot the bot is currently standing
bs->teamgoal.entitynum = bs->entitynum;
bs->teamgoal.areanum = bs->areanum;
VectorCopy(bs->origin, bs->teamgoal.origin);
VectorSet(bs->teamgoal.mins, -8, -8, -8);
VectorSet(bs->teamgoal.maxs, 8, 8, 8);
}
else if (match->subtype & ST_HERE) {
//if this is the bot self
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)) {
//NOTE: just assume the bot knows where the person is
//if (BotEntityVisible(bs->entitynum, bs->eye, bs->viewangles, 360, client)) {
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 the other is not visible
if (bs->teamgoal.entitynum < 0) {
BotAI_BotInitialChat(bs, "whereareyou", netname, NULL);
client = ClientFromName(netname);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
return;
}
}
else if (!BotGetMessageTeamGoal(bs, itemname, &bs->teamgoal)) {
//BotAI_BotInitialChat(bs, "cannotfind", itemname, NULL);
//client = ClientFromName(netname);
//trap_BotEnterChat(bs->cs, client, CHAT_TELL);
return;
}
//
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_CAMPORDER;
//get the team goal time
bs->teamgoal_time = BotGetTime(match);
//set the team goal time
if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_CAMP_TIME;
//not arrived yet
bs->arrive_time = 0;
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_Patrol
==================
*/
void BotMatch_Patrol(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//get the patrol waypoints
if (!BotGetPatrolWaypoints(bs, match)) return;
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = FindClientByName(netname);
//
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_PATROL;
//get the team goal time
bs->teamgoal_time = BotGetTime(match);
//set the team goal time if not set already
if (!bs->teamgoal_time) bs->teamgoal_time = FloatTime() + TEAM_PATROL_TIME;
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_GetFlag
==================
*/
void BotMatch_GetFlag(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) {
if (!ctf_redflag.areanum || !ctf_blueflag.areanum)
return;
}
else if (gametype == GT_1FCTF) {
if (!ctf_neutralflag.areanum || !ctf_redflag.areanum || !ctf_blueflag.areanum)
return;
}
else {
return;
}
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = FindClientByName(netname);
//
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_GETFLAG;
//set the team goal time
bs->teamgoal_time = FloatTime() + CTF_GETFLAG_TIME;
// get an alternate route in ctf
if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) {
//get an alternative route goal towards the enemy base
BotGetAlternateRouteGoal(bs, BotOppositeTeam(bs));
}
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_AttackEnemyBase
==================
*/
void BotMatch_AttackEnemyBase(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
if (gametype == GT_CTF|| gametype == GT_CTF_ELIMINATION) {
BotMatch_GetFlag(bs, match);
}
else if (gametype == GT_1FCTF || gametype == GT_OBELISK || gametype == GT_HARVESTER) {
if (!redobelisk.areanum || !blueobelisk.areanum)
return;
}
else {
return;
}
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = FindClientByName(netname);
//
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_ATTACKENEMYBASE;
//set the team goal time
bs->teamgoal_time = FloatTime() + TEAM_ATTACKENEMYBASE_TIME;
bs->attackaway_time = 0;
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_Harvest
==================
*/
void BotMatch_Harvest(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
if (gametype == GT_HARVESTER) {
if (!neutralobelisk.areanum || !redobelisk.areanum || !blueobelisk.areanum)
return;
}
else {
return;
}
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = FindClientByName(netname);
//
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_HARVEST;
//set the team goal time
bs->teamgoal_time = FloatTime() + TEAM_HARVEST_TIME;
bs->harvestaway_time = 0;
//
BotSetTeamStatus(bs);
// remember last ordered task
BotRememberLastOrderedTask(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_RushBase
==================
*/
void BotMatch_RushBase(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
if (gametype == GT_CTF|| gametype == GT_CTF_ELIMINATION) {
if (!ctf_redflag.areanum || !ctf_blueflag.areanum)
return;
}
else if (gametype == GT_1FCTF || gametype == GT_HARVESTER) {
if (!redobelisk.areanum || !blueobelisk.areanum)
return;
}
else {
return;
}
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = FindClientByName(netname);
//
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_RUSHBASE;
//set the team goal time
bs->teamgoal_time = FloatTime() + CTF_RUSHBASE_TIME;
bs->rushbaseaway_time = 0;
//
BotSetTeamStatus(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_TaskPreference
==================
*/
void BotMatch_TaskPreference(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_NETNAME];
char teammatename[MAX_MESSAGE_SIZE];
int teammate, preference;
ClientName(bs->client, netname, sizeof(netname));
if (Q_stricmp(netname, bs->teamleader) != 0) return;
trap_BotMatchVariable(match, NETNAME, teammatename, sizeof(teammatename));
teammate = ClientFromName(teammatename);
if (teammate < 0) return;
preference = BotGetTeamMateTaskPreference(bs, teammate);
switch(match->subtype)
{
case ST_DEFENDER:
{
preference &= ~TEAMTP_ATTACKER;
preference |= TEAMTP_DEFENDER;
break;
}
case ST_ATTACKER:
{
preference &= ~TEAMTP_DEFENDER;
preference |= TEAMTP_ATTACKER;
break;
}
case ST_ROAMER:
{
preference &= ~(TEAMTP_ATTACKER|TEAMTP_DEFENDER);
break;
}
}
BotSetTeamMateTaskPreference(bs, teammate, preference);
//
EasyClientName(teammate, teammatename, sizeof(teammatename));
BotAI_BotInitialChat(bs, "keepinmind", teammatename, NULL);
trap_BotEnterChat(bs->cs, teammate, CHAT_TELL);
BotVoiceChatOnly(bs, teammate, VOICECHAT_YES);
trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
}
/*
==================
BotMatch_ReturnFlag
==================
*/
void BotMatch_ReturnFlag(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
//if not in CTF mode
if (gametype != GT_CTF && gametype != GT_CTF_ELIMINATION && gametype != GT_1FCTF)
return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match))
return;
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
//
client = FindClientByName(netname);
//
bs->decisionmaker = client;
bs->ordered = qtrue;
bs->order_time = FloatTime();
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_RETURNFLAG;
//set the team goal time
bs->teamgoal_time = FloatTime() + CTF_RETURNFLAG_TIME;
bs->rushbaseaway_time = 0;
//
BotSetTeamStatus(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_JoinSubteam
==================
*/
void BotMatch_JoinSubteam(bot_state_t *bs, bot_match_t *match) {
char teammate[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//get the sub team name
trap_BotMatchVariable(match, TEAMNAME, teammate, sizeof(teammate));
//set the sub team name
strncpy(bs->subteam, teammate, 32);
bs->subteam[31] = '\0';
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
BotAI_BotInitialChat(bs, "joinedteam", teammate, NULL);
client = ClientFromName(netname);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
}
/*
==================
BotMatch_LeaveSubteam
==================
*/
void BotMatch_LeaveSubteam(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
if (strlen(bs->subteam))
{
BotAI_BotInitialChat(bs, "leftteam", bs->subteam, NULL);
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
} //end if
strcpy(bs->subteam, "");
}
/*
==================
BotMatch_LeaveSubteam
==================
*/
void BotMatch_WhichTeam(bot_state_t *bs, bot_match_t *match) {
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
if (strlen(bs->subteam)) {
BotAI_BotInitialChat(bs, "inteam", bs->subteam, NULL);
}
else {
BotAI_BotInitialChat(bs, "noteam", NULL);
}
trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
}
/*
==================
BotMatch_CheckPoint
==================
*/
void BotMatch_CheckPoint(bot_state_t *bs, bot_match_t *match) {
int areanum, client;
char buf[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
vec3_t position;
bot_waypoint_t *cp;
if (!TeamPlayIsOn()) return;
//
trap_BotMatchVariable(match, POSITION, buf, MAX_MESSAGE_SIZE);
VectorClear(position);
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
//BotGPSToPosition(buf, position);
sscanf(buf, "%f %f %f", &position[0], &position[1], &position[2]);
position[2] += 0.5;
areanum = BotPointAreaNum(position);
if (!areanum) {
if (BotAddressedToBot(bs, match)) {
BotAI_BotInitialChat(bs, "checkpoint_invalid", NULL);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
}
return;
}
//
trap_BotMatchVariable(match, NAME, buf, MAX_MESSAGE_SIZE);
//check if there already exists a checkpoint with this name
cp = BotFindWayPoint(bs->checkpoints, buf);
if (cp) {
if (cp->next) cp->next->prev = cp->prev;
if (cp->prev) cp->prev->next = cp->next;
else bs->checkpoints = cp->next;
cp->inuse = qfalse;
}
//create a new check point
cp = BotCreateWayPoint(buf, position, areanum);
//add the check point to the bot's known chech points
cp->next = bs->checkpoints;
if (bs->checkpoints) bs->checkpoints->prev = cp;
bs->checkpoints = cp;
//
if (BotAddressedToBot(bs, match)) {
Com_sprintf(buf, sizeof(buf), "%1.0f %1.0f %1.0f", cp->goal.origin[0],
cp->goal.origin[1],
cp->goal.origin[2]);
BotAI_BotInitialChat(bs, "checkpoint_confirm", cp->name, buf, NULL);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
}
}
/*
==================
BotMatch_FormationSpace
==================
*/
void BotMatch_FormationSpace(bot_state_t *bs, bot_match_t *match) {
char buf[MAX_MESSAGE_SIZE];
float space;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
trap_BotMatchVariable(match, NUMBER, buf, MAX_MESSAGE_SIZE);
//if it's the distance in feet
if (match->subtype & ST_FEET) space = 0.3048 * 32 * atof(buf);
//else it's in meters
else space = 32 * atof(buf);
//check if the formation intervening space is valid
if (space < 48 || space > 500) space = 100;
bs->formation_dist = space;
}
/*
==================
BotMatch_Dismiss
==================
*/
void BotMatch_Dismiss(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
//
bs->decisionmaker = client;
//
bs->ltgtype = 0;
bs->lead_time = 0;
bs->lastgoal_ltgtype = 0;
//
BotAI_BotInitialChat(bs, "dismissed", NULL);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
}
/*
==================
BotMatch_Suicide
==================
*/
void BotMatch_Suicide(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
trap_EA_Command(bs->client, "kill");
//
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
//
BotVoiceChat(bs, client, VOICECHAT_TAUNT);
trap_EA_Action(bs->client, ACTION_AFFIRMATIVE);
}
/*
==================
BotMatch_StartTeamLeaderShip
==================
*/
void BotMatch_StartTeamLeaderShip(bot_state_t *bs, bot_match_t *match) {
int client;
char teammate[MAX_MESSAGE_SIZE];
if (!TeamPlayIsOn()) return;
//if chats for him or herself
if (match->subtype & ST_I) {
//get the team mate that will be the team leader
trap_BotMatchVariable(match, NETNAME, teammate, sizeof(teammate));
strncpy(bs->teamleader, teammate, sizeof(bs->teamleader));
bs->teamleader[sizeof(bs->teamleader)-1] = '\0';
}
//chats for someone else
else {
//get the team mate that will be the team leader
trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate));
client = FindClientByName(teammate);
if (client >= 0) ClientName(client, bs->teamleader, sizeof(bs->teamleader));
}
}
/*
==================
BotMatch_StopTeamLeaderShip
==================
*/
void BotMatch_StopTeamLeaderShip(bot_state_t *bs, bot_match_t *match) {
int client;
char teammate[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
if (!TeamPlayIsOn()) return;
//get the team mate that stops being the team leader
trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate));
//if chats for him or herself
if (match->subtype & ST_I) {
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = FindClientByName(netname);
}
//chats for someone else
else {
client = FindClientByName(teammate);
} //end else
if (client >= 0) {
if (!Q_stricmp(bs->teamleader, ClientName(client, netname, sizeof(netname)))) {
bs->teamleader[0] = '\0';
notleader[client] = qtrue;
}
}
}
/*
==================
BotMatch_WhoIsTeamLeader
==================
*/
void BotMatch_WhoIsTeamLeader(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
if (!TeamPlayIsOn()) return;
ClientName(bs->client, netname, sizeof(netname));
//if this bot IS the team leader
if (!Q_stricmp(netname, bs->teamleader)) {
trap_EA_SayTeam(bs->client, "I'm the team leader\n");
}
}
/*
==================
BotMatch_WhatAreYouDoing
==================
*/
void BotMatch_WhatAreYouDoing(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_MESSAGE_SIZE];
char goalname[MAX_MESSAGE_SIZE];
int client;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//
switch(bs->ltgtype) {
case LTG_TEAMHELP:
{
EasyClientName(bs->teammate, netname, sizeof(netname));
BotAI_BotInitialChat(bs, "helping", netname, NULL);
break;
}
case LTG_TEAMACCOMPANY:
{
EasyClientName(bs->teammate, netname, sizeof(netname));
BotAI_BotInitialChat(bs, "accompanying", netname, NULL);
break;
}
case LTG_DEFENDKEYAREA:
{
trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
BotAI_BotInitialChat(bs, "defending", goalname, NULL);
break;
}
case LTG_GETITEM:
{
trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
BotAI_BotInitialChat(bs, "gettingitem", goalname, NULL);
break;
}
case LTG_KILL:
{
ClientName(bs->teamgoal.entitynum, netname, sizeof(netname));
BotAI_BotInitialChat(bs, "killing", netname, NULL);
break;
}
case LTG_CAMP:
case LTG_CAMPORDER:
{
BotAI_BotInitialChat(bs, "camping", NULL);
break;
}
case LTG_PATROL:
{
BotAI_BotInitialChat(bs, "patrolling", NULL);
break;
}
case LTG_GETFLAG:
{
BotAI_BotInitialChat(bs, "capturingflag", NULL);
break;
}
case LTG_RUSHBASE:
{
BotAI_BotInitialChat(bs, "rushingbase", NULL);
break;
}
case LTG_RETURNFLAG:
{
BotAI_BotInitialChat(bs, "returningflag", NULL);
break;
}
case LTG_ATTACKENEMYBASE:
{
BotAI_BotInitialChat(bs, "attackingenemybase", NULL);
break;
}
case LTG_HARVEST:
{
BotAI_BotInitialChat(bs, "harvesting", NULL);
break;
}
//#endif
case LTG_POINTA:
{
BotAI_BotInitialChat(bs, "dd_pointa", NULL);
break;
}
case LTG_POINTB:
{
BotAI_BotInitialChat(bs, "dd_pointb", NULL);
break;
}
default:
{
BotAI_BotInitialChat(bs, "roaming", NULL);
break;
}
}
//chat what the bot is doing
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
}
/*
==================
BotMatch_WhatIsMyCommand
==================
*/
void BotMatch_WhatIsMyCommand(bot_state_t *bs, bot_match_t *match) {
char netname[MAX_NETNAME];
ClientName(bs->client, netname, sizeof(netname));
if (Q_stricmp(netname, bs->teamleader) != 0) return;
bs->forceorders = qtrue;
}
/*
==================
BotNearestVisibleItem
==================
*/
float BotNearestVisibleItem(bot_state_t *bs, char *itemname, bot_goal_t *goal) {
int i;
char name[64];
bot_goal_t tmpgoal;
float dist, bestdist;
vec3_t dir;
bsp_trace_t trace;
bestdist = 999999;
i = -1;
do {
i = trap_BotGetLevelItemGoal(i, itemname, &tmpgoal);
trap_BotGoalName(tmpgoal.number, name, sizeof(name));
if (Q_stricmp(itemname, name) != 0)
continue;
VectorSubtract(tmpgoal.origin, bs->origin, dir);
dist = VectorLength(dir);
if (dist < bestdist) {
//trace from start to end
BotAI_Trace(&trace, bs->eye, NULL, NULL, tmpgoal.origin, bs->client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
if (trace.fraction >= 1.0) {
bestdist = dist;
memcpy(goal, &tmpgoal, sizeof(bot_goal_t));
}
}
} while(i > 0);
return bestdist;
}
/*
==================
BotMatch_WhereAreYou
==================
*/
void BotMatch_WhereAreYou(bot_state_t *bs, bot_match_t *match) {
float dist, bestdist;
int i, bestitem, redtt, bluett, client;
bot_goal_t goal;
char netname[MAX_MESSAGE_SIZE];
char *nearbyitems[] = {
"Shotgun",
"Grenade Launcher",
"Rocket Launcher",
"Plasmagun",
"Railgun",
"Lightning Gun",
"BFG10K",
"Quad Damage",
"Regeneration",
"Battle Suit",
"Speed",
"Invisibility",
"Flight",
"Armor",
"Heavy Armor",
"Red Flag",
"Blue Flag",
"Nailgun",
"Prox Launcher",
"Chaingun",
"Scout",
"Guard",
"Doubler",
"Ammo Regen",
"Neutral Flag",
"Red Obelisk",
"Blue Obelisk",
"Neutral Obelisk",
NULL
};
//
if (!TeamPlayIsOn())
return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match))
return;
bestitem = -1;
bestdist = 999999;
for (i = 0; nearbyitems[i]; i++) {
dist = BotNearestVisibleItem(bs, nearbyitems[i], &goal);
if (dist < bestdist) {
bestdist = dist;
bestitem = i;
}
}
if (bestitem != -1) {
if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION
|| gametype == GT_1FCTF
) {
redtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_redflag.areanum, TFL_DEFAULT);
bluett = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, ctf_blueflag.areanum, TFL_DEFAULT);
if (redtt < (redtt + bluett) * 0.4) {
BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "red", NULL);
}
else if (bluett < (redtt + bluett) * 0.4) {
BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "blue", NULL);
}
else {
BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL);
}
}
else if (gametype == GT_OBELISK || gametype == GT_HARVESTER) {
redtt = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, redobelisk.areanum, TFL_DEFAULT);
bluett = trap_AAS_AreaTravelTimeToGoalArea(bs->areanum, bs->origin, blueobelisk.areanum, TFL_DEFAULT);
if (redtt < (redtt + bluett) * 0.4) {
BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "red", NULL);
}
else if (bluett < (redtt + bluett) * 0.4) {
BotAI_BotInitialChat(bs, "teamlocation", nearbyitems[bestitem], "blue", NULL);
}
else {
BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL);
}
}
else {
BotAI_BotInitialChat(bs, "location", nearbyitems[bestitem], NULL);
}
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
}
}
/*
==================
BotMatch_LeadTheWay
==================
*/
void BotMatch_LeadTheWay(bot_state_t *bs, bot_match_t *match) {
aas_entityinfo_t entinfo;
char netname[MAX_MESSAGE_SIZE], teammate[MAX_MESSAGE_SIZE];
int client, areanum, other;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
//if someone asks for someone else
if (match->subtype & ST_SOMEONE) {
//get the team mate name
trap_BotMatchVariable(match, TEAMMATE, teammate, sizeof(teammate));
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;
}
}
else {
//get the netname
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
other = qfalse;
}
//if the bot doesn't know who to help (FindClientByName returned -1)
if (client < 0) {
BotAI_BotInitialChat(bs, "whois", netname, NULL);
trap_BotEnterChat(bs->cs, bs->client, CHAT_TEAM);
return;
}
//
bs->lead_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->lead_teamgoal.entitynum = client;
bs->lead_teamgoal.areanum = areanum;
VectorCopy(entinfo.origin, bs->lead_teamgoal.origin);
VectorSet(bs->lead_teamgoal.mins, -8, -8, -8);
VectorSet(bs->lead_teamgoal.maxs, 8, 8, 8);
}
}
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;
}
bs->lead_teammate = client;
bs->lead_time = FloatTime() + TEAM_LEAD_TIME;
bs->leadvisible_time = 0;
bs->leadmessage_time = -(FloatTime() + 2 * random());
}
/*
==================
BotMatch_Kill
==================
*/
void BotMatch_Kill(bot_state_t *bs, bot_match_t *match) {
char enemy[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
int client;
if (!TeamPlayIsOn()) return;
//if not addressed to this bot
if (!BotAddressedToBot(bs, match)) return;
trap_BotMatchVariable(match, ENEMY, enemy, sizeof(enemy));
//
client = FindEnemyByName(bs, enemy);
if (client < 0) {
BotAI_BotInitialChat(bs, "whois", enemy, NULL);
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = ClientFromName(netname);
trap_BotEnterChat(bs->cs, client, CHAT_TELL);
return;
}
bs->teamgoal.entitynum = client;
//set the time to send a message to the team mates
bs->teammessage_time = FloatTime() + 2 * random();
//set the ltg type
bs->ltgtype = LTG_KILL;
//set the team goal time
bs->teamgoal_time = FloatTime() + TEAM_KILL_SOMEONE;
//
BotSetTeamStatus(bs);
#ifdef DEBUG
BotPrintTeamGoal(bs);
#endif //DEBUG
}
/*
==================
BotMatch_CTF
==================
*/
void BotMatch_CTF(bot_state_t *bs, bot_match_t *match) {
char flag[128], netname[MAX_NETNAME];
if (gametype == GT_CTF || gametype == GT_CTF_ELIMINATION) {
trap_BotMatchVariable(match, FLAG, flag, sizeof(flag));
if (match->subtype & ST_GOTFLAG) {
if (!Q_stricmp(flag, "red")) {
bs->redflagstatus = 1;
if (BotTeam(bs) == TEAM_BLUE) {
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
bs->flagcarrier = ClientFromName(netname);
}
}
else {
bs->blueflagstatus = 1;
if (BotTeam(bs) == TEAM_RED) {
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
bs->flagcarrier = ClientFromName(netname);
}
}
bs->flagstatuschanged = 1;
bs->lastflagcapture_time = FloatTime();
}
else if (match->subtype & ST_CAPTUREDFLAG) {
bs->redflagstatus = 0;
bs->blueflagstatus = 0;
bs->flagcarrier = 0;
bs->flagstatuschanged = 1;
}
else if (match->subtype & ST_RETURNEDFLAG) {
if (!Q_stricmp(flag, "red")) bs->redflagstatus = 0;
else bs->blueflagstatus = 0;
bs->flagstatuschanged = 1;
}
}
else if (gametype == GT_1FCTF) {
if (match->subtype & ST_1FCTFGOTFLAG) {
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
bs->flagcarrier = ClientFromName(netname);
}
}
}
void BotMatch_EnterGame(bot_state_t *bs, bot_match_t *match) {
int client;
char netname[MAX_NETNAME];
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = FindClientByName(netname);
if (client >= 0) {
notleader[client] = qfalse;
}
//NOTE: eliza chats will catch this
//Com_sprintf(buf, sizeof(buf), "heya %s", netname);
//EA_Say(bs->client, buf);
}
void BotMatch_NewLeader(bot_state_t *bs, bot_match_t *match) {
int client;
char netname[MAX_NETNAME];
trap_BotMatchVariable(match, NETNAME, netname, sizeof(netname));
client = FindClientByName(netname);
if (!BotSameTeam(bs, client))
return;
Q_strncpyz(bs->teamleader, netname, sizeof(bs->teamleader));
}
/*
==================
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
|MTCONTEXT_CTF
|MTCONTEXT_DD)) {
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;
}
//CTF & 1FCTF
case MSG_GETFLAG: //ctf get the enemy flag
{
BotMatch_GetFlag(bs, &match);
break;
}
//CTF & 1FCTF & Obelisk & Harvester
case MSG_ATTACKENEMYBASE:
{
BotMatch_AttackEnemyBase(bs, &match);
break;
}
//Harvester
case MSG_HARVEST:
{
BotMatch_Harvest(bs, &match);
break;
}
//CTF & 1FCTF & Harvester
case MSG_RUSHBASE: //ctf rush to the base
{
BotMatch_RushBase(bs, &match);
break;
}
//CTF & 1FCTF
case MSG_RETURNFLAG:
{
BotMatch_ReturnFlag(bs, &match);
break;
}
//CTF & 1FCTF & Obelisk & Harvester
case MSG_TASKPREFERENCE:
{
BotMatch_TaskPreference(bs, &match);
break;
}
//CTF & 1FCTF
case MSG_CTF:
{
BotMatch_CTF(bs, &match);
break;
}
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
{
BotMatch_EnterGame(bs, &match);
break;
}
case MSG_NEWLEADER:
{
BotMatch_NewLeader(bs, &match);
break;
}
case MSG_WAIT:
{
break;
}
case MSG_SUICIDE:
{
BotMatch_Suicide(bs, &match);
break;
}
case MSG_TAKEA:
{
BotMatch_TakeA(bs, &match);
break;
}
case MSG_TAKEB:
{
BotMatch_TakeB(bs, &match);
break;
}
default:
{
BotAI_Print(PRT_MESSAGE, "unknown match type\n");
break;
}
}
return qtrue;
}