📄 ai_main.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_main.c
*
* desc: Quake3 bot AI
*
* $Archive: /MissionPack/code/game/ai_main.c $
*
*****************************************************************************/
#include "g_local.h"
#include "q_shared.h"
#include "botlib.h" //bot lib interface
#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_vcmd.h"
//
#include "chars.h"
#include "inv.h"
#include "syn.h"
#define MAX_PATH 144
//bot states
bot_state_t *botstates[MAX_CLIENTS];
//number of bots
int numbots;
//floating point time
float floattime;
//time to do a regular update
float regularupdate_time;
//
int bot_interbreed;
int bot_interbreedmatchcount;
//
vmCvar_t bot_thinktime;
vmCvar_t bot_memorydump;
vmCvar_t bot_saveroutingcache;
vmCvar_t bot_pause;
vmCvar_t bot_report;
vmCvar_t bot_testsolid;
vmCvar_t bot_testclusters;
vmCvar_t bot_developer;
vmCvar_t bot_interbreedchar;
vmCvar_t bot_interbreedbots;
vmCvar_t bot_interbreedcycle;
vmCvar_t bot_interbreedwrite;
void ExitLevel( void );
/*
==================
BotAI_Print
==================
*/
void QDECL BotAI_Print(int type, char *fmt, ...) {
char str[2048];
va_list ap;
va_start(ap, fmt);
vsprintf(str, fmt, ap);
va_end(ap);
switch(type) {
case PRT_MESSAGE: {
G_Printf("%s", str);
break;
}
case PRT_WARNING: {
G_Printf( S_COLOR_YELLOW "Warning: %s", str );
break;
}
case PRT_ERROR: {
G_Printf( S_COLOR_RED "Error: %s", str );
break;
}
case PRT_FATAL: {
G_Printf( S_COLOR_RED "Fatal: %s", str );
break;
}
case PRT_EXIT: {
G_Error( S_COLOR_RED "Exit: %s", str );
break;
}
default: {
G_Printf( "unknown print type\n" );
break;
}
}
}
/*
==================
BotAI_Trace
==================
*/
void BotAI_Trace(bsp_trace_t *bsptrace, vec3_t start, vec3_t mins, vec3_t maxs, vec3_t end, int passent, int contentmask) {
trace_t trace;
trap_Trace(&trace, start, mins, maxs, end, passent, contentmask);
//copy the trace information
bsptrace->allsolid = trace.allsolid;
bsptrace->startsolid = trace.startsolid;
bsptrace->fraction = trace.fraction;
VectorCopy(trace.endpos, bsptrace->endpos);
bsptrace->plane.dist = trace.plane.dist;
VectorCopy(trace.plane.normal, bsptrace->plane.normal);
bsptrace->plane.signbits = trace.plane.signbits;
bsptrace->plane.type = trace.plane.type;
bsptrace->surface.value = trace.surfaceFlags;
bsptrace->ent = trace.entityNum;
bsptrace->exp_dist = 0;
bsptrace->sidenum = 0;
bsptrace->contents = 0;
}
/*
==================
BotAI_GetClientState
==================
*/
int BotAI_GetClientState( int clientNum, playerState_t *state ) {
gentity_t *ent;
ent = &g_entities[clientNum];
if ( !ent->inuse ) {
return qfalse;
}
if ( !ent->client ) {
return qfalse;
}
memcpy( state, &ent->client->ps, sizeof(playerState_t) );
return qtrue;
}
/*
==================
BotAI_GetEntityState
==================
*/
int BotAI_GetEntityState( int entityNum, entityState_t *state ) {
gentity_t *ent;
ent = &g_entities[entityNum];
memset( state, 0, sizeof(entityState_t) );
if (!ent->inuse) return qfalse;
if (!ent->r.linked) return qfalse;
if (ent->r.svFlags & SVF_NOCLIENT) return qfalse;
memcpy( state, &ent->s, sizeof(entityState_t) );
return qtrue;
}
/*
==================
BotAI_GetSnapshotEntity
==================
*/
int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) {
int entNum;
entNum = trap_BotGetSnapshotEntity( clientNum, sequence );
if ( entNum == -1 ) {
memset(state, 0, sizeof(entityState_t));
return -1;
}
BotAI_GetEntityState( entNum, state );
return sequence + 1;
}
/*
==================
BotAI_BotInitialChat
==================
*/
void QDECL BotAI_BotInitialChat( bot_state_t *bs, char *type, ... ) {
int i, mcontext;
va_list ap;
char *p;
char *vars[MAX_MATCHVARIABLES];
memset(vars, 0, sizeof(vars));
va_start(ap, type);
p = va_arg(ap, char *);
for (i = 0; i < MAX_MATCHVARIABLES; i++) {
if( !p ) {
break;
}
vars[i] = p;
p = va_arg(ap, char *);
}
va_end(ap);
mcontext = BotSynonymContext(bs);
trap_BotInitialChat( bs->cs, type, mcontext, vars[0], vars[1], vars[2], vars[3], vars[4], vars[5], vars[6], vars[7] );
}
/*
==================
BotTestAAS
==================
*/
void BotTestAAS(vec3_t origin) {
int areanum;
aas_areainfo_t info;
trap_Cvar_Update(&bot_testsolid);
trap_Cvar_Update(&bot_testclusters);
if (bot_testsolid.integer) {
if (!trap_AAS_Initialized()) return;
areanum = BotPointAreaNum(origin);
if (areanum) BotAI_Print(PRT_MESSAGE, "\remtpy area");
else BotAI_Print(PRT_MESSAGE, "\r^1SOLID area");
}
else if (bot_testclusters.integer) {
if (!trap_AAS_Initialized()) return;
areanum = BotPointAreaNum(origin);
if (!areanum)
BotAI_Print(PRT_MESSAGE, "\r^1Solid! ");
else {
trap_AAS_AreaInfo(areanum, &info);
BotAI_Print(PRT_MESSAGE, "\rarea %d, cluster %d ", areanum, info.cluster);
}
}
}
/*
==================
BotReportStatus
==================
*/
void BotReportStatus(bot_state_t *bs) {
char goalname[MAX_MESSAGE_SIZE];
char netname[MAX_MESSAGE_SIZE];
char *leader, flagstatus[32];
//
ClientName(bs->client, netname, sizeof(netname));
if (Q_stricmp(netname, bs->teamleader) == 0) leader = "L";
else leader = " ";
strcpy(flagstatus, " ");
if (gametype == GT_CTF) {
if (BotCTFCarryingFlag(bs)) {
if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F ");
else strcpy(flagstatus, S_COLOR_BLUE"F ");
}
}
#ifdef MISSIONPACK
else if (gametype == GT_1FCTF) {
if (Bot1FCTFCarryingFlag(bs)) {
if (BotTeam(bs) == TEAM_RED) strcpy(flagstatus, S_COLOR_RED"F ");
else strcpy(flagstatus, S_COLOR_BLUE"F ");
}
}
else if (gametype == GT_HARVESTER) {
if (BotHarvesterCarryingCubes(bs)) {
if (BotTeam(bs) == TEAM_RED) Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_RED"%2d", bs->inventory[INVENTORY_REDCUBE]);
else Com_sprintf(flagstatus, sizeof(flagstatus), S_COLOR_BLUE"%2d", bs->inventory[INVENTORY_BLUECUBE]);
}
}
#endif
switch(bs->ltgtype) {
case LTG_TEAMHELP:
{
EasyClientName(bs->teammate, goalname, sizeof(goalname));
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: helping %s\n", netname, leader, flagstatus, goalname);
break;
}
case LTG_TEAMACCOMPANY:
{
EasyClientName(bs->teammate, goalname, sizeof(goalname));
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: accompanying %s\n", netname, leader, flagstatus, goalname);
break;
}
case LTG_DEFENDKEYAREA:
{
trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: defending %s\n", netname, leader, flagstatus, goalname);
break;
}
case LTG_GETITEM:
{
trap_BotGoalName(bs->teamgoal.number, goalname, sizeof(goalname));
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: getting item %s\n", netname, leader, flagstatus, goalname);
break;
}
case LTG_KILL:
{
ClientName(bs->teamgoal.entitynum, goalname, sizeof(goalname));
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: killing %s\n", netname, leader, flagstatus, goalname);
break;
}
case LTG_CAMP:
case LTG_CAMPORDER:
{
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: camping\n", netname, leader, flagstatus);
break;
}
case LTG_PATROL:
{
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: patrolling\n", netname, leader, flagstatus);
break;
}
case LTG_GETFLAG:
{
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: capturing flag\n", netname, leader, flagstatus);
break;
}
case LTG_RUSHBASE:
{
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: rushing base\n", netname, leader, flagstatus);
break;
}
case LTG_RETURNFLAG:
{
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: returning flag\n", netname, leader, flagstatus);
break;
}
case LTG_ATTACKENEMYBASE:
{
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: attacking the enemy base\n", netname, leader, flagstatus);
break;
}
case LTG_HARVEST:
{
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: harvesting\n", netname, leader, flagstatus);
break;
}
default:
{
BotAI_Print(PRT_MESSAGE, "%-20s%s%s: roaming\n", netname, leader, flagstatus);
break;
}
}
}
/*
==================
BotTeamplayReport
==================
*/
void BotTeamplayReport(void) {
int i;
char buf[MAX_INFO_STRING];
BotAI_Print(PRT_MESSAGE, S_COLOR_RED"RED\n");
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
//
if ( !botstates[i] || !botstates[i]->inuse ) continue;
//
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_RED) {
BotReportStatus(botstates[i]);
}
}
BotAI_Print(PRT_MESSAGE, S_COLOR_BLUE"BLUE\n");
for (i = 0; i < maxclients && i < MAX_CLIENTS; i++) {
//
if ( !botstates[i] || !botstates[i]->inuse ) continue;
//
trap_GetConfigstring(CS_PLAYERS+i, buf, sizeof(buf));
//if no config string or no name
if (!strlen(buf) || !strlen(Info_ValueForKey(buf, "n"))) continue;
//skip spectators
if (atoi(Info_ValueForKey(buf, "t")) == TEAM_BLUE) {
BotReportStatus(botstates[i]);
}
}
}
/*
==================
BotSetInfoConfigString
==================
*/
void BotSetInfoConfigString(bot_state_t *bs) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -