📄 ai_cmd.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 Foobar; 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.h"
#include "be_aas.h"
#include "be_ea.h"
#include "be_ai_char.h"
#include "be_ai_chat.h"
#include "be_ai_gen.h"
#include "be_ai_goal.h"
#include "be_ai_move.h"
#include "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;
}
#ifdef MISSIONPACK
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;
}
#endif
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;
}
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
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -