jabot: rearrange bot commands

This commit is contained in:
Denis Pauk 2025-03-19 00:43:23 +02:00
parent 69409594bd
commit 8aea22e6c6
11 changed files with 205 additions and 301 deletions

View file

@ -971,7 +971,6 @@ GAME_OBJS_ = \
src/game/bot/ai_tools.o \
src/game/bot/ai_weapons.o \
src/game/bot/astar.o \
src/game/bot/bot_common.o \
src/game/bot/bot_spawn.o \
src/game/g_ai.o \
src/game/g_chase.o \

View file

@ -329,12 +329,13 @@ void AI_WaterJumpNode( void )
//==========================================
static float last_update=0;
#define NODE_UPDATE_DELAY 0.10;
void AI_PathMap( void )
void
AI_PathMap(void)
{
int closest_node;
int closest_node;
//DROP WATER JUMP NODE (not limited by delayed updates)
if ( !player.ent->is_swim && player.last_node != -1
if (!player.ent->is_swim && player.last_node != -1
&& player.ent->is_swim != player.ent->was_swim)
{
AI_WaterJumpNode();
@ -342,15 +343,19 @@ void AI_PathMap( void )
return;
}
if( level.time < last_update )
if (level.time < last_update)
{
return;
}
last_update = level.time + NODE_UPDATE_DELAY; // slow down updates a bit
//don't drop nodes when riding movers
if( player.ent->groundentity && player.ent->groundentity != world) {
if( player.ent->groundentity->classname ) {
if( !strcmp( player.ent->groundentity->classname, "func_plat")
if (player.ent->groundentity && player.ent->groundentity != world)
{
if (player.ent->groundentity->classname)
{
if (!strcmp( player.ent->groundentity->classname, "func_plat")
|| !strcmp(player.ent->groundentity->classname, "trigger_push")
|| !strcmp(player.ent->groundentity->classname, "func_train")
|| !strcmp(player.ent->groundentity->classname, "func_rotate")
@ -361,7 +366,7 @@ void AI_PathMap( void )
}
// Special check for ladder nodes
if(AI_CheckForLadder(player.ent))
if (AI_CheckForLadder(player.ent))
return;
// Not on ground, and not in the water, so bail (deeper check by using a splitmodels function)

View file

@ -131,17 +131,12 @@ void AI_InitNavigationData(void);
int AI_FlagsForNode( vec3_t origin, edict_t *passent );
float AI_Distance( vec3_t o1, vec3_t o2 );
void AITools_AddBotRoamNode(void);
// ai_tools.c
//----------------------------------------------------------
void AIDebug_SetChased(edict_t *ent);
void AITools_DrawPath(edict_t *self, int node_from, int node_to);
void AITools_DrawLine(vec3_t origin, vec3_t dest);
void AITools_InitEditnodes( void );
void AITools_InitMakenodes( void );
void AITools_SaveNodes( void );
qboolean AI_LoadPLKFile( char *mapname );
// ai_links.c

View file

@ -85,12 +85,15 @@ void G_FreeAI( edict_t *ent )
// G_SpawnAI
// allocate ai_handle_t for this entity
//==========================================
void G_SpawnAI( edict_t *ent )
void
G_SpawnAI(edict_t *ent)
{
if( !ent->ai )
ent->ai = gi.TagMalloc (sizeof(ai_handle_t), TAG_LEVEL);
if(!ent->ai)
{
ent->ai = gi.TagMalloc(sizeof(ai_handle_t), TAG_LEVEL);
}
memset( ent->ai, 0, sizeof(ai_handle_t));
memset(ent->ai, 0, sizeof(ai_handle_t));
}
//==========================================
@ -422,17 +425,20 @@ void AI_PickShortRangeGoal(edict_t *self)
// AI_CategorizePosition
// Categorize waterlevel and groundentity/stepping
//===================
void AI_CategorizePosition (edict_t *ent)
void
AI_CategorizePosition(edict_t *ent)
{
qboolean stepping = AI_IsStep(ent);
ent->was_swim = ent->is_swim;
ent->was_step = ent->is_step;
ent->is_ladder = AI_IsLadder( ent->s.origin, ent->s.angles, ent->mins, ent->maxs, ent );
ent->is_ladder = AI_IsLadder(ent->s.origin, ent->s.angles,
ent->mins, ent->maxs, ent);
M_CatagorizePosition(ent);
if (ent->waterlevel > 2 || (ent->waterlevel && !stepping)) {
if (ent->waterlevel > 2 || (ent->waterlevel && !stepping))
{
ent->is_swim = true;
ent->is_step = false;
return;
@ -447,25 +453,32 @@ void AI_CategorizePosition (edict_t *ent)
// AI_Think
// think funtion for AIs
//==========================================
void AI_Think (edict_t *self)
void
AI_Think(edict_t *self)
{
if( !self->ai ) //jabot092(2)
if (!self->ai ) //jabot092(2)
{
return;
}
AIDebug_SetChased(self); //jal:debug shit
AI_CategorizePosition(self);
//freeze AI when dead
if( self->deadflag ) {
if (self->deadflag)
{
self->ai->pers.deadFrame(self);
return;
}
//if completely stuck somewhere
if(VectorLength(self->velocity) > 37)
if (VectorLength(self->velocity) > 37)
{
self->ai->bloqued_timeout = level.time + 10.0;
}
if( self->ai->bloqued_timeout < level.time ) {
if (self->ai->bloqued_timeout < level.time)
{
self->ai->pers.bloquedTimeout(self);
return;
}
@ -474,23 +487,26 @@ void AI_Think (edict_t *self)
self->ai->pers.UpdateStatus(self);
//update position in path, set up move vector
if( self->ai->state == BOT_STATE_MOVE ) {
if( !AI_FollowPath(self) )
if (self->ai->state == BOT_STATE_MOVE)
{
if (!AI_FollowPath(self))
{
AI_SetUpMoveWander( self );
AI_SetUpMoveWander(self);
self->ai->wander_timeout = level.time - 1; //do it now
}
}
//pick a new long range goal
if( self->ai->state == BOT_STATE_WANDER && self->ai->wander_timeout < level.time)
/* pick a new long range goal */
if (self->ai->state == BOT_STATE_WANDER &&
self->ai->wander_timeout < level.time)
{
AI_PickLongRangeGoal(self);
}
//Find any short range goal
/* Find any short range goal */
AI_PickShortRangeGoal(self);
//run class based states machine
/* run class based states machine */
self->ai->pers.RunFrame(self);
}

View file

@ -33,20 +33,19 @@
static int alist[MAX_NODES]; //list contains all studied nodes, Open and Closed together
static int alist_numNodes;
enum {
typedef enum {
NOLIST,
OPENLIST,
CLOSEDLIST
};
} astarnodelist_e;
typedef struct
{
int parent;
int G;
int H;
int list;
int parent;
int G;
int H;
astarnodelist_e list;
} astarnode_t;
static astarnode_t astarnodes[MAX_NODES];
@ -109,7 +108,7 @@ AStar_nodeIsInOpen(int node)
}
static void
AStar_InitLists(void)
AStar_InitLists(struct astarpath_s *path)
{
size_t i;
@ -121,6 +120,8 @@ AStar_InitLists(void)
astarnodes[i].list = NOLIST;
}
Apath = path;
if(Apath)
{
Apath->numNodes = 0;
@ -326,7 +327,7 @@ AStar_FillLists(void)
}
static qboolean
AStar_ResolvePath(int origin, int goal, int movetypes)
AStar_ResolvePath(int origin, int goal, int movetypes, struct astarpath_s *path)
{
if (origin < 0 || goal < 0)
{
@ -339,7 +340,7 @@ AStar_ResolvePath(int origin, int goal, int movetypes)
ValidLinksMask = DEFAULT_MOVETYPES_MASK;
}
AStar_InitLists();
AStar_InitLists(path);
currentNode = originNode = origin;
goalNode = goal;
@ -360,9 +361,7 @@ AStar_ResolvePath(int origin, int goal, int movetypes)
qboolean
AStar_GetPath(int origin, int goal, int movetypes, struct astarpath_s *path)
{
Apath = path;
if( !AStar_ResolvePath ( origin, goal, movetypes ) )
if (!AStar_ResolvePath(origin, goal, movetypes, path))
{
return false;
}

View file

@ -1,151 +0,0 @@
/*
* Copyright (C) 1997-2001 Id Software, Inc.
* Copyright (C) 2001 Steve Yeager
* Copyright (C) 2001-2004 Pat AfterMoon
* Copyright (c) ZeniMax Media Inc.
*
* This program 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.
*
* This program 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 this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
* 02111-1307, USA.
*
*/
#include "../header/local.h"
#include "ai_local.h"
//ACE
//==========================================
// BOT_ServerCommand
// Special server command processor
//==========================================
qboolean BOT_ServerCommand (void)
{
char *cmd;
cmd = gi.argv(1);
if (Q_stricmp (cmd, "addbot") == 0)
{
if(ctf->value) // name, skin, team
{
BOT_SpawnBot ( gi.argv(2), gi.argv(3), gi.argv(4), NULL );
}
else // name, skin
{
BOT_SpawnBot ( NULL, gi.argv(2), gi.argv(3), NULL );
}
}
else if( !Q_stricmp (cmd, "editnodes") )
{
AITools_InitEditnodes();
}
else if (!Q_stricmp (cmd, "makenodes"))
{
AITools_InitMakenodes();
}
else if (!Q_stricmp (cmd, "savenodes"))
{
AITools_SaveNodes();
}
else if (!Q_stricmp (cmd, "addbotroam"))
{
AITools_AddBotRoamNode();
}
else
{
return false;
}
return true;
}
///////////////////////////////////////////////////////////////////////
// These routines are bot safe print routines, all id code needs to be
// changed to these so the bots do not blow up on messages sent to them.
// Do a find and replace on all code that matches the below criteria.
//
// (Got the basic idea from Ridah)
//
// change: gi.cprintf to safe_cprintf
// change: gi.bprintf to safe_bprintf
// change: gi.centerprintf to safe_centerprintf
//
///////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////
// botsafe cprintf
///////////////////////////////////////////////////////////////////////
void safe_cprintf (edict_t *ent, int printlevel, char *fmt, ...)
{
char bigbuffer[0x10000];
va_list argptr;
if (ent && (!ent->inuse || ent->ai))
return;
va_start (argptr,fmt);
vsprintf (bigbuffer,fmt,argptr);
va_end (argptr);
gi.cprintf(ent, printlevel, bigbuffer);
}
///////////////////////////////////////////////////////////////////////
// botsafe centerprintf
///////////////////////////////////////////////////////////////////////
void safe_centerprintf (edict_t *ent, char *fmt, ...)
{
char bigbuffer[0x10000];
va_list argptr;
if (!ent->inuse || ent->ai)
return;
va_start (argptr,fmt);
vsprintf (bigbuffer,fmt,argptr);
va_end (argptr);
gi.centerprintf(ent, bigbuffer);
}
///////////////////////////////////////////////////////////////////////
// botsafe bprintf
///////////////////////////////////////////////////////////////////////
void safe_bprintf (int printlevel, char *fmt, ...)
{
int i;
char bigbuffer[0x10000];
va_list argptr;
edict_t *cl_ent;
va_start (argptr,fmt);
vsprintf (bigbuffer,fmt,argptr);
va_end (argptr);
if (dedicated->value)
gi.cprintf(NULL, printlevel, bigbuffer);
for (i=0 ; i<maxclients->value ; i++)
{
cl_ent = g_edicts + 1 + i;
if (!cl_ent->inuse || cl_ent->ai)
continue;
gi.cprintf(cl_ent, printlevel, bigbuffer);
}
}

View file

@ -292,17 +292,22 @@ void BOT_StartAsSpectator (edict_t *ent)
// BOT_JoinGame
// 3 for teams and such
//==========================================
void BOT_JoinBlue (edict_t *ent)
static void
BOT_JoinBlue(edict_t *ent)
{
BOT_DMClass_JoinGame( ent, "blue" );
BOT_DMClass_JoinGame(ent, "blue");
}
void BOT_JoinRed (edict_t *ent)
static void
BOT_JoinRed(edict_t *ent)
{
BOT_DMClass_JoinGame( ent, "red" );
BOT_DMClass_JoinGame(ent, "red");
}
void BOT_JoinGame (edict_t *ent)
static void
BOT_JoinGame(edict_t *ent)
{
BOT_DMClass_JoinGame( ent, NULL );
BOT_DMClass_JoinGame(ent, NULL);
}
///////////////////////////////////////////////////////////////////////
@ -310,61 +315,75 @@ void BOT_JoinGame (edict_t *ent)
///////////////////////////////////////////////////////////////////////
void BOT_SpawnBot (char *team, char *name, char *skin, char *userinfo)
{
edict_t *bot;
edict_t *bot;
if( !nav.loaded ) {
if(!nav.loaded)
{
Com_Printf("Can't spawn bots without a valid navigation file\n");
return;
}
bot = BOT_FindFreeClient ();
bot = BOT_FindFreeClient();
if (!bot)
{
safe_bprintf (PRINT_MEDIUM, "Server is full, increase Maxclients.\n");
gi.bprintf(PRINT_MEDIUM, "Server is full, increase Maxclients.\n");
return;
}
//init the bot
/* init the bot */
bot->inuse = true;
bot->yaw_speed = 100;
// To allow bots to respawn
/* To allow bots to respawn */
if(userinfo == NULL)
{
BOT_SetName(bot, name, skin, team);
}
else
ClientConnect (bot, userinfo);
{
ClientConnect(bot, userinfo);
}
G_InitEdict (bot);
G_SpawnAI(bot); //jabot092(2)
G_InitEdict(bot);
G_SpawnAI(bot);
bot->ai->is_bot = true;
InitClientResp (bot->client);
InitClientResp(bot->client);
PutClientInServer(bot);
BOT_StartAsSpectator (bot);
BOT_StartAsSpectator(bot);
//skill
bot->ai->pers.skillLevel = (int)(random()*MAX_BOT_SKILL);
if (bot->ai->pers.skillLevel > MAX_BOT_SKILL) //fix if off-limits
/* skill */
bot->ai->pers.skillLevel = (int)(random() * MAX_BOT_SKILL);
if (bot->ai->pers.skillLevel > MAX_BOT_SKILL)
{
/* fix if off-limits */
bot->ai->pers.skillLevel = MAX_BOT_SKILL;
}
else if (bot->ai->pers.skillLevel < 0)
{
bot->ai->pers.skillLevel = 0;
}
BOT_DMclass_InitPersistant(bot);
AI_ResetWeights(bot);
AI_ResetNavigation(bot);
bot->think = BOT_JoinGame;
bot->nextthink = level.time + (int)(random()*6.0);
if( ctf->value && team != NULL )
bot->nextthink = level.time + (int)(random() * 6.0);
if(ctf->value && team != NULL)
{
if( !Q_stricmp( team, "blue" ) )
if (!Q_stricmp(team, "blue"))
{
bot->think = BOT_JoinBlue;
else if( !Q_stricmp( team, "red" ) )
}
else if (!Q_stricmp( team, "red"))
{
bot->think = BOT_JoinRed;
}
}
AI_EnemyAdded (bot); // let the ai know we added another
AI_EnemyAdded(bot); // let the ai know we added another
}
@ -376,22 +395,27 @@ void BOT_RemoveBot(char *name)
int i;
edict_t *bot;
for(i=0;i<maxclients->value;i++)
for (i = 0; i < maxclients->value; i++)
{
bot = g_edicts + i + 1;
if( !bot->inuse || !bot->ai ) //jabot092(2)
continue;
if( bot->ai->is_bot && (!strcmp(bot->client->pers.netname,name) || !strcmp(name,"all")))
if (!bot->inuse || !bot->ai)
{
continue;
}
if (bot->ai->is_bot &&
(!strcmp(bot->client->pers.netname,name) || !strcmp(name,"all")))
{
bot->health = 0;
player_die (bot, bot, bot, 100000, vec3_origin);
// don't even bother waiting for death frames
/* don't even bother waiting for death frames */
bot->deadflag = DEAD_DEAD;
bot->inuse = false;
AI_EnemyRemoved (bot);
G_FreeAI( bot ); //jabot092(2)
//safe_bprintf (PRINT_MEDIUM, "%s removed\n", bot->client->pers.netname);
AI_EnemyRemoved(bot);
G_FreeAI(bot);
gi.bprintf(PRINT_MEDIUM, "%s removed\n", bot->client->pers.netname);
}
}
}

View file

@ -324,13 +324,6 @@ ServerCommand(void)
{
char *cmd;
// JABot[start]
if (BOT_ServerCommand())
{
return;
}
// [end]
cmd = gi.argv(1);
if (Q_stricmp(cmd, "test") == 0)
@ -354,6 +347,33 @@ ServerCommand(void)
SVCmd_WriteIP_f();
}
/* JABot[start] */
else if (Q_stricmp(cmd, "addbot") == 0)
{
if (ctf->value) // name, skin, team
{
BOT_SpawnBot(gi.argv(2), gi.argv(3), gi.argv(4), NULL);
}
else // name, skin
{
BOT_SpawnBot(NULL, gi.argv(2), gi.argv(3), NULL);
}
}
else if (Q_stricmp(cmd, "editnodes") == 0)
{
AITools_InitEditnodes();
}
else if (!Q_stricmp(cmd, "makenodes"))
{
AITools_InitMakenodes();
}
else if (!Q_stricmp (cmd, "savenodes"))
{
AITools_SaveNodes();
}
else if (!Q_stricmp (cmd, "addbotroam"))
{
AITools_AddBotRoamNode();
}
else if(Q_stricmp(cmd, "removebot") == 0)
{
BOT_RemoveBot(gi.argv(2));

View file

@ -712,6 +712,8 @@ G_InitEdict(edict_t *e)
e->gravityVector[0] = 0.0;
e->gravityVector[1] = 0.0;
e->gravityVector[2] = -1.0;
VectorSet(e->rrs.scale, 1.0, 1.0, 1.0);
}
/*

View file

@ -30,8 +30,9 @@
// declaration of botedict for the game
//----------------------------------------------------------
#define MAX_BOT_ROAMS 128
#define MAX_NODES 2048 //jalToDo: needs dynamic alloc (big terrain maps)
#define MAX_BOT_ROAMS 128
/* jalToDo: needs dynamic alloc (big terrain maps) */
#define MAX_NODES 2048
typedef struct astarpath_s
{
@ -43,89 +44,83 @@ typedef struct astarpath_s
typedef struct
{
qboolean jumpadReached;
qboolean TeleportReached;
float inventoryWeights[MAX_ITEMS];
float playersWeights[MAX_EDICTS];
float broam_timeouts[MAX_BOT_ROAMS]; //revisit bot roams
qboolean jumpadReached;
qboolean TeleportReached;
float inventoryWeights[MAX_ITEMS];
float playersWeights[MAX_EDICTS];
float broam_timeouts[MAX_BOT_ROAMS]; //revisit bot roams
} ai_status_t;
typedef struct
{
char *netname;
int skillLevel; // Affects AIM and fire rate
int moveTypesMask; // bot can perform these moves, to check against required moves for a given path
char *netname;
int skillLevel; // Affects AIM and fire rate
int moveTypesMask; // bot can perform these moves, to check against required moves for a given path
float inventoryWeights[MAX_ITEMS];
float inventoryWeights[MAX_ITEMS];
//class based functions
void (*UpdateStatus)(edict_t *ent);
void (*RunFrame)(edict_t *ent);
void (*bloquedTimeout)(edict_t *ent);
void (*deadFrame)(edict_t *ent);
void (*UpdateStatus)(edict_t *ent);
void (*RunFrame)(edict_t *ent);
void (*bloquedTimeout)(edict_t *ent);
void (*deadFrame)(edict_t *ent);
} ai_pers_t;
typedef struct
{
ai_pers_t pers; //persistant definition (class?)
ai_status_t status; //player (bot, NPC) status for AI frame
ai_pers_t pers; /* persistant definition (class?) */
ai_status_t status; /* player (bot, NPC) status for AI frame */
qboolean is_bot; //used for fakeclient classname determination
qboolean is_bot; /* used for fakeclient classname determination */
//NPC state
int state; // Bot State (WANDER, MOVE, etc)
float state_combat_timeout;
/* NPC state */
int state; /* Bot State (WANDER, MOVE, etc) */
float state_combat_timeout;
// movement
vec3_t move_vector;
float next_move_time;
float wander_timeout;
float bloqued_timeout;
float changeweapon_timeout;
/* movement */
vec3_t move_vector;
float next_move_time;
float wander_timeout;
float bloqued_timeout;
float changeweapon_timeout;
// nodes
int current_node;
int goal_node;
int next_node;
int node_timeout;
/* nodes */
int current_node;
int goal_node;
int next_node;
int node_timeout;
int tries;
int tries;
struct astarpath_s path; //jabot092
struct astarpath_s path; /* jabot092 */
int nearest_node_tries; //for increasing radius of search with each try
int nearest_node_tries; /*for increasing radius of search with each try */
} ai_handle_t;
/* ai_main.c */
void AI_Init(void);
void AI_NewMap(void);
void G_FreeAI( edict_t *ent );
void G_SpawnAI( edict_t *ent );
// bot_cmds.c
qboolean BOT_ServerCommand(void);
/* ai_items.c */
void AI_EnemyAdded(edict_t *ent);
void AI_EnemyRemoved(edict_t *ent);
// ai_main.c
void AI_Init(void);
void AI_NewMap(void);
void G_FreeAI( edict_t *ent );
void G_SpawnAI( edict_t *ent );
/* bot_spawn.c */
void BOT_SpawnBot(char *team, char *name, char *skin, char *userinfo);
void BOT_RemoveBot(char *name);
void BOT_Respawn(edict_t *self);
// ai_items.c
void AI_EnemyAdded(edict_t *ent);
void AI_EnemyRemoved(edict_t *ent);
/* ai_tools.c */
void AIDebug_ToogleBotDebug(void);
void AITools_Frame(void);
void AITools_DropNodes(edict_t *ent);
// bot_spawn.c
void BOT_SpawnBot(char *team, char *name, char *skin, char *userinfo);
void BOT_RemoveBot(char *name);
void BOT_Respawn(edict_t *self);
// ai_tools.c
void AIDebug_ToogleBotDebug(void);
void AITools_Frame(void);
void AITools_DropNodes(edict_t *ent);
// safe **cough** prints
void safe_cprintf(edict_t *ent, int printlevel, char *fmt, ...);
void safe_centerprintf(edict_t *ent, char *fmt, ...);
void safe_bprintf(int printlevel, char *fmt, ...);
/* ai_dropnodes.c */
void AITools_AddBotRoamNode(void);
void AITools_SaveNodes(void);
void AITools_InitEditnodes(void);
void AITools_InitMakenodes(void);

View file

@ -1517,7 +1517,7 @@ struct edict_s
int chasedist1;
int chasedist2;
ai_handle_t *ai; //jabot092(2)
ai_handle_t *ai; /* jabot */
qboolean is_swim; //AI_CategorizePosition
qboolean is_step;
qboolean is_ladder;