codemp/game/ai_main.c File Reference

#include "g_local.h"
#include "q_shared.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 "w_saber.h"
#include "chars.h"
#include "inv.h"
#include "syn.h"

Go to the source code of this file.

Defines

#define MAX_PATH   144
#define BOT_THINK_TIME   0
#define BOT_STRAFE_AVOIDANCE
#define STRAFEAROUND_RIGHT   1
#define STRAFEAROUND_LEFT   2

Functions

void BotStraightTPOrderCheck (gentity_t *ent, int ordernum, bot_state_t *bs)
void BotSelectWeapon (int client, int weapon)
void BotReportStatus (bot_state_t *bs)
void BotOrder (gentity_t *ent, int clientnum, int ordernum)
int BotMindTricked (int botClient, int enemyClient)
int BotGetWeaponRange (bot_state_t *bs)
int PassLovedOneCheck (bot_state_t *bs, gentity_t *ent)
void ExitLevel (void)
void QDECL BotAI_Print (int type, char *fmt,...)
qboolean WP_ForcePowerUsable (gentity_t *self, forcePowers_t forcePower)
int IsTeamplay (void)
int BotAI_GetClientState (int clientNum, playerState_t *state)
int BotAI_GetEntityState (int entityNum, entityState_t *state)
int BotAI_GetSnapshotEntity (int clientNum, int sequence, entityState_t *state)
void BotEntityInfo (int entnum, aas_entityinfo_t *info)
int NumBots (void)
float AngleDifference (float ang1, float ang2)
float BotChangeViewAngle (float angle, float ideal_angle, float speed)
void BotChangeViewAngles (bot_state_t *bs, float thinktime)
void BotInputToUserCommand (bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time, int useTime)
void BotUpdateInput (bot_state_t *bs, int time, int elapsed_time)
void BotAIRegularUpdate (void)
void RemoveColorEscapeSequences (char *text)
int BotAI (int client, float thinktime)
void BotScheduleBotThink (void)
int PlayersInGame (void)
int BotAISetupClient (int client, struct bot_settings_s *settings, qboolean restart)
int BotAIShutdownClient (int client, qboolean restart)
void BotResetState (bot_state_t *bs)
int BotAILoadMap (int restart)
int OrgVisible (vec3_t org1, vec3_t org2, int ignore)
int WPOrgVisible (gentity_t *bot, vec3_t org1, vec3_t org2, int ignore)
int OrgVisibleBox (vec3_t org1, vec3_t mins, vec3_t maxs, vec3_t org2, int ignore)
int CheckForFunc (vec3_t org, int ignore)
qboolean BotPVSCheck (const vec3_t p1, const vec3_t p2)
int GetNearestVisibleWP (vec3_t org, int ignore)
int PassWayCheck (bot_state_t *bs, int windex)
float TotalTrailDistance (int start, int end, bot_state_t *bs)
void CheckForShorterRoutes (bot_state_t *bs, int newwpindex)
void WPConstantRoutine (bot_state_t *bs)
qboolean BotCTFGuardDuty (bot_state_t *bs)
void WPTouchRoutine (bot_state_t *bs)
void MoveTowardIdealAngles (bot_state_t *bs)
int BotTrace_Strafe (bot_state_t *bs, vec3_t traceto)
int BotTrace_Jump (bot_state_t *bs, vec3_t traceto)
int BotTrace_Duck (bot_state_t *bs, vec3_t traceto)
int PassStandardEnemyChecks (bot_state_t *bs, gentity_t *en)
void BotDamageNotification (gclient_t *bot, gentity_t *attacker)
int BotCanHear (bot_state_t *bs, gentity_t *en, float endist)
void UpdateEventTracker (void)
int InFieldOfVision (vec3_t viewangles, float fov, vec3_t angles)
qboolean G_ThereIsAMaster (void)
int ScanForEnemies (bot_state_t *bs)
int WaitingForNow (bot_state_t *bs, vec3_t goalpos)
int BotIsAChickenWuss (bot_state_t *bs)
gentity_tGetNearestBadThing (bot_state_t *bs)
int BotDefendFlag (bot_state_t *bs)
int BotGetEnemyFlag (bot_state_t *bs)
int BotGetFlagBack (bot_state_t *bs)
int BotGuardFlagCarrier (bot_state_t *bs)
int BotGetFlagHome (bot_state_t *bs)
void GetNewFlagPoint (wpobject_t *wp, gentity_t *flagEnt, int team)
int CTFTakesPriority (bot_state_t *bs)
int EntityVisibleBox (vec3_t org1, vec3_t mins, vec3_t maxs, vec3_t org2, int ignore, int ignore2)
int Siege_TargetClosestObjective (bot_state_t *bs, int flag)
void Siege_DefendFromAttackers (bot_state_t *bs)
int Siege_CountDefenders (bot_state_t *bs)
int Siege_CountTeammates (bot_state_t *bs)
int SiegeTakesPriority (bot_state_t *bs)
int JMTakesPriority (bot_state_t *bs)
int BotHasAssociated (bot_state_t *bs, wpobject_t *wp)
int GetBestIdleGoal (bot_state_t *bs)
void GetIdealDestination (bot_state_t *bs)
void CommanderBotCTFAI (bot_state_t *bs)
void CommanderBotSiegeAI (bot_state_t *bs)
void BotDoTeamplayAI (bot_state_t *bs)
void CommanderBotTeamplayAI (bot_state_t *bs)
void CommanderBotAI (bot_state_t *bs)
void MeleeCombatHandling (bot_state_t *bs)
void SaberCombatHandling (bot_state_t *bs)
float BotWeaponCanLead (bot_state_t *bs)
void BotAimLeading (bot_state_t *bs, vec3_t headlevel, float leadAmount)
void BotAimOffsetGoalAngles (bot_state_t *bs)
int ShouldSecondaryFire (bot_state_t *bs)
int CombatBotAI (bot_state_t *bs, float thinktime)
int BotFallbackNavigation (bot_state_t *bs)
int BotTryAnotherWeapon (bot_state_t *bs)
qboolean BotWeaponSelectable (bot_state_t *bs, int weapon)
int BotSelectIdealWeapon (bot_state_t *bs)
int BotSelectChoiceWeapon (bot_state_t *bs, int weapon, int doselection)
int BotSelectMelee (bot_state_t *bs)
int GetLoveLevel (bot_state_t *bs, bot_state_t *love)
void BotLovedOneDied (bot_state_t *bs, bot_state_t *loved, int lovelevel)
void BotDeathNotify (bot_state_t *bs)
void StrafeTracing (bot_state_t *bs)
int PrimFiring (bot_state_t *bs)
int KeepPrimFromFiring (bot_state_t *bs)
int AltFiring (bot_state_t *bs)
int KeepAltFromFiring (bot_state_t *bs)
gentity_tCheckForFriendInLOF (bot_state_t *bs)
void BotScanForLeader (bot_state_t *bs)
void BotReplyGreetings (bot_state_t *bs)
void CTFFlagMovement (bot_state_t *bs)
void BotCheckDetPacks (bot_state_t *bs)
int BotUseInventoryItem (bot_state_t *bs)
int BotSurfaceNear (bot_state_t *bs)
int BotWeaponBlockable (int weapon)
void Cmd_EngageDuel_f (gentity_t *ent)
void Cmd_ToggleSaber_f (gentity_t *ent)
void Bot_SetForcedMovement (int bot, int forward, int right, int up)
void StandardBotAI (bot_state_t *bs, float thinktime)
int BotAIStartFrame (int time)
int BotAISetup (int restart)
int BotAIShutdown (int restart)

Variables

bot_state_tbotstates [MAX_CLIENTS]
int numbots
float floattime
float regularupdate_time
int rebel_attackers
int imperial_attackers
boteventtracker_t gBotEventTracker [MAX_CLIENTS]
vmCvar_t bot_forcepowers
vmCvar_t bot_forgimmick
vmCvar_t bot_honorableduelacceptance
vmCvar_t bot_pvstype
vmCvar_t bot_normgpath
vmCvar_t bot_getinthecarrr
vmCvar_t bot_attachments
vmCvar_t bot_camp
vmCvar_t bot_wp_info
vmCvar_t bot_wp_edit
vmCvar_t bot_wp_clearweight
vmCvar_t bot_wp_distconnect
vmCvar_t bot_wp_visconnect
wpobject_tflagRed
wpobject_toFlagRed
wpobject_tflagBlue
wpobject_toFlagBlue
gentity_teFlagRed
gentity_tdroppedRedFlag
gentity_teFlagBlue
gentity_tdroppedBlueFlag
char * ctfStateNames []
char * ctfStateDescriptions []
char * siegeStateDescriptions []
char * teamplayStateDescriptions []
int gUpdateVars = 0


Define Documentation

#define BOT_STRAFE_AVOIDANCE
 

Definition at line 1548 of file ai_main.c.

#define BOT_THINK_TIME   0
 

Definition at line 43 of file ai_main.c.

Referenced by BotAIStartFrame(), and BotScheduleBotThink().

#define MAX_PATH   144
 

Definition at line 41 of file ai_main.c.

#define STRAFEAROUND_LEFT   2
 

Definition at line 1552 of file ai_main.c.

Referenced by BotTrace_Strafe(), and StandardBotAI().

#define STRAFEAROUND_RIGHT   1
 

Definition at line 1551 of file ai_main.c.

Referenced by BotTrace_Strafe(), and StandardBotAI().


Function Documentation

int AltFiring bot_state_t bs  ) 
 

Definition at line 5502 of file ai_main.c.

References bot_state_t, bot_state_s::cur_ps, bot_state_s::doAltAttack, WEAPON_CHARGING_ALT, and playerState_s::weaponstate.

Referenced by StandardBotAI().

05503 {
05504         if (bs->cur_ps.weaponstate != WEAPON_CHARGING_ALT &&
05505                 bs->doAltAttack)
05506         {
05507                 return 1;
05508         }
05509 
05510         if (bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT &&
05511                 !bs->doAltAttack)
05512         {
05513                 return 1;
05514         }
05515 
05516         return 0;
05517 }

float AngleDifference float  ang1,
float  ang2
 

Definition at line 425 of file ai_main.c.

Referenced by BotChangeViewAngles(), and BotTrace_Strafe().

00425                                               {
00426         float diff;
00427 
00428         diff = ang1 - ang2;
00429         if (ang1 > ang2) {
00430                 if (diff > 180.0) diff -= 360.0;
00431         }
00432         else {
00433                 if (diff < -180.0) diff += 360.0;
00434         }
00435         return diff;
00436 }

void Bot_SetForcedMovement int  bot,
int  forward,
int  right,
int  up
 

Definition at line 5883 of file ai_main.c.

References bot_state_t, botstates, bot_state_s::forceMove_Forward, bot_state_s::forceMove_Right, and bot_state_s::forceMove_Up.

Referenced by ClientCommand().

05884 {
05885         bot_state_t *bs;
05886 
05887         bs = botstates[bot];
05888 
05889         if (!bs)
05890         { //not a bot
05891                 return;
05892         }
05893 
05894         if (forward != -1)
05895         {
05896                 if (bs->forceMove_Forward)
05897                 {
05898                         bs->forceMove_Forward = 0;
05899                 }
05900                 else
05901                 {
05902                         bs->forceMove_Forward = forward;
05903                 }
05904         }
05905         if (right != -1)
05906         {
05907                 if (bs->forceMove_Right)
05908                 {
05909                         bs->forceMove_Right = 0;
05910                 }
05911                 else
05912                 {
05913                         bs->forceMove_Right = right;
05914                 }
05915         }
05916         if (up != -1)
05917         {
05918                 if (bs->forceMove_Up)
05919                 {
05920                         bs->forceMove_Up = 0;
05921                 }
05922                 else
05923                 {
05924                         bs->forceMove_Up = up;
05925                 }
05926         }
05927 }

int BotAI int  client,
float  thinktime
 

Definition at line 696 of file ai_main.c.

References AngleMod(), bot_state_t, BotAI_GetClientState(), BotAI_Print(), botstates, client, Com_Printf(), bot_state_s::cur_ps, playerState_s::delta_angles, bot_state_s::eye, vmCvar_t::integer, bot_state_s::inuse, bot_state_s::ltime, bot_state_s::origin, playerState_s::origin, PRT_FATAL, Q_stricmp(), qfalse, qtrue, RemoveColorEscapeSequences(), SHORT2ANGLE, StandardBotAI(), strchr(), bot_state_s::thinktime, trap_BotGetServerCommand(), trap_Cvar_Update(), trap_EA_ResetInput(), trap_Milliseconds(), VectorCopy, bot_state_s::viewangles, and playerState_s::viewheight.

Referenced by BotAIStartFrame().

00696                                        {
00697         bot_state_t *bs;
00698         char buf[1024], *args;
00699         int j;
00700 #ifdef _DEBUG
00701         int start = 0;
00702         int end = 0;
00703 #endif
00704 
00705         trap_EA_ResetInput(client);
00706         //
00707         bs = botstates[client];
00708         if (!bs || !bs->inuse) {
00709                 BotAI_Print(PRT_FATAL, "BotAI: client %d is not setup\n", client);
00710                 return qfalse;
00711         }
00712 
00713         //retrieve the current client state
00714         BotAI_GetClientState( client, &bs->cur_ps );
00715 
00716         //retrieve any waiting server commands
00717         while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) {
00718                 //have buf point to the command and args to the command arguments
00719                 args = strchr( buf, ' ');
00720                 if (!args) continue;
00721                 *args++ = '\0';
00722 
00723                 //remove color espace sequences from the arguments
00724                 RemoveColorEscapeSequences( args );
00725 
00726                 if (!Q_stricmp(buf, "cp "))
00727                         { /*CenterPrintf*/ }
00728                 else if (!Q_stricmp(buf, "cs"))
00729                         { /*ConfigStringModified*/ }
00730                 else if (!Q_stricmp(buf, "scores"))
00731                         { /*FIXME: parse scores?*/ }
00732                 else if (!Q_stricmp(buf, "clientLevelShot"))
00733                         { /*ignore*/ }
00734         }
00735         //add the delta angles to the bot's current view angles
00736         for (j = 0; j < 3; j++) {
00737                 bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00738         }
00739         //increase the local time of the bot
00740         bs->ltime += thinktime;
00741         //
00742         bs->thinktime = thinktime;
00743         //origin of the bot
00744         VectorCopy(bs->cur_ps.origin, bs->origin);
00745         //eye coordinates of the bot
00746         VectorCopy(bs->cur_ps.origin, bs->eye);
00747         bs->eye[2] += bs->cur_ps.viewheight;
00748         //get the area the bot is in
00749 
00750 #ifdef _DEBUG
00751         start = trap_Milliseconds();
00752 #endif
00753         StandardBotAI(bs, thinktime);
00754 #ifdef _DEBUG
00755         end = trap_Milliseconds();
00756 
00757         trap_Cvar_Update(&bot_debugmessages);
00758 
00759         if (bot_debugmessages.integer)
00760         {
00761                 Com_Printf("Single AI frametime: %i\n", (end - start));
00762         }
00763 #endif
00764 
00765         //subtract the delta angles
00766         for (j = 0; j < 3; j++) {
00767                 bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00768         }
00769         //everything was ok
00770         return qtrue;
00771 }

int BotAI_GetClientState int  clientNum,
playerState_t state
 

Definition at line 351 of file ai_main.c.

References gentity_s::client, g_entities, gentity_t, gentity_s::inuse, memcpy(), playerState_t, gclient_s::ps, qfalse, and qtrue.

Referenced by BotAI().

00351                                                                 {
00352         gentity_t       *ent;
00353 
00354         ent = &g_entities[clientNum];
00355         if ( !ent->inuse ) {
00356                 return qfalse;
00357         }
00358         if ( !ent->client ) {
00359                 return qfalse;
00360         }
00361 
00362         memcpy( state, &ent->client->ps, sizeof(playerState_t) );
00363         return qtrue;
00364 }

int BotAI_GetEntityState int  entityNum,
entityState_t state
 

Definition at line 371 of file ai_main.c.

References entityState_t, g_entities, gentity_t, gentity_s::inuse, entityShared_t::linked, memcpy(), memset(), qfalse, qtrue, gentity_s::r, gentity_s::s, SVF_NOCLIENT, and entityShared_t::svFlags.

Referenced by BotAI_GetSnapshotEntity().

00371                                                                 {
00372         gentity_t       *ent;
00373 
00374         ent = &g_entities[entityNum];
00375         memset( state, 0, sizeof(entityState_t) );
00376         if (!ent->inuse) return qfalse;
00377         if (!ent->r.linked) return qfalse;
00378         if (ent->r.svFlags & SVF_NOCLIENT) return qfalse;
00379         memcpy( state, &ent->s, sizeof(entityState_t) );
00380         return qtrue;
00381 }

int BotAI_GetSnapshotEntity int  clientNum,
int  sequence,
entityState_t state
 

Definition at line 388 of file ai_main.c.

References BotAI_GetEntityState(), entityState_t, memset(), and trap_BotGetSnapshotEntity().

00388                                                                                  {
00389         int             entNum;
00390 
00391         entNum = trap_BotGetSnapshotEntity( clientNum, sequence );
00392         if ( entNum == -1 ) {
00393                 memset(state, 0, sizeof(entityState_t));
00394                 return -1;
00395         }
00396 
00397         BotAI_GetEntityState( entNum, state );
00398 
00399         return sequence + 1;
00400 }

void QDECL BotAI_Print int  type,
char *  fmt,
... 
 

Definition at line 332 of file ai_main.c.

References QDECL.

Referenced by BotAI(), and BotAISetupClient().

00332 { return; }

int BotAILoadMap int  restart  ) 
 

Definition at line 966 of file ai_main.c.

References BotResetState(), botstates, bot_state_s::inuse, MAX_CLIENTS, qtrue, and bot_state_s::setupcount.

Referenced by G_InitGame().

00966                                 {
00967         int                     i;
00968 
00969         for (i = 0; i < MAX_CLIENTS; i++) {
00970                 if (botstates[i] && botstates[i]->inuse) {
00971                         BotResetState( botstates[i] );
00972                         botstates[i]->setupcount = 4;
00973                 }
00974         }
00975 
00976         return qtrue;
00977 }

void BotAimLeading bot_state_t bs,
vec3_t  headlevel,
float  leadAmount
 

Definition at line 4607 of file ai_main.c.

References bot_state_t, gentity_s::client, bot_state_s::currentEnemy, bot_state_s::eye, bot_state_s::frame_Enemy_Len, bot_state_s::goalAngles, gclient_s::ps, vec3_t, vectoangles(), VectorCopy, VectorNormalize(), VectorSubtract, and playerState_s::velocity.

Referenced by StandardBotAI().

04608 {
04609         int x;
04610         vec3_t predictedSpot;
04611         vec3_t movementVector;
04612         vec3_t a, ang;
04613         float vtotal;
04614 
04615         if (!bs->currentEnemy ||
04616                 !bs->currentEnemy->client)
04617         {
04618                 return;
04619         }
04620 
04621         if (!bs->frame_Enemy_Len)
04622         {
04623                 return;
04624         }
04625 
04626         vtotal = 0;
04627 
04628         if (bs->currentEnemy->client->ps.velocity[0] < 0)
04629         {
04630                 vtotal += -bs->currentEnemy->client->ps.velocity[0];
04631         }
04632         else
04633         {
04634                 vtotal += bs->currentEnemy->client->ps.velocity[0];
04635         }
04636 
04637         if (bs->currentEnemy->client->ps.velocity[1] < 0)
04638         {
04639                 vtotal += -bs->currentEnemy->client->ps.velocity[1];
04640         }
04641         else
04642         {
04643                 vtotal += bs->currentEnemy->client->ps.velocity[1];
04644         }
04645 
04646         if (bs->currentEnemy->client->ps.velocity[2] < 0)
04647         {
04648                 vtotal += -bs->currentEnemy->client->ps.velocity[2];
04649         }
04650         else
04651         {
04652                 vtotal += bs->currentEnemy->client->ps.velocity[2];
04653         }
04654 
04655         //G_Printf("Leadin target with a velocity total of %f\n", vtotal);
04656 
04657         VectorCopy(bs->currentEnemy->client->ps.velocity, movementVector);
04658 
04659         VectorNormalize(movementVector);
04660 
04661         x = bs->frame_Enemy_Len*leadAmount; //hardly calculated with an exact science, but it works
04662 
04663         if (vtotal > 400)
04664         {
04665                 vtotal = 400;
04666         }
04667 
04668         if (vtotal)
04669         {
04670                 x = (bs->frame_Enemy_Len*0.9)*leadAmount*(vtotal*0.0012); //hardly calculated with an exact science, but it works
04671         }
04672         else
04673         {
04674                 x = (bs->frame_Enemy_Len*0.9)*leadAmount; //hardly calculated with an exact science, but it works
04675         }
04676 
04677         predictedSpot[0] = headlevel[0] + (movementVector[0]*x);
04678         predictedSpot[1] = headlevel[1] + (movementVector[1]*x);
04679         predictedSpot[2] = headlevel[2] + (movementVector[2]*x);
04680 
04681         VectorSubtract(predictedSpot, bs->eye, a);
04682         vectoangles(a, ang);
04683         VectorCopy(ang, bs->goalAngles);
04684 }

void BotAimOffsetGoalAngles bot_state_t bs  ) 
 

Definition at line 4687 of file ai_main.c.

References botskills_s::accuracy, bot_state_s::aimOffsetAmtPitch, bot_state_s::aimOffsetAmtYaw, bot_state_s::aimOffsetTime, bot_state_t, BotMindTricked(), bot_state_s::client, bot_state_s::currentEnemy, bot_state_s::frame_Enemy_Vis, g_entities, bot_state_s::goalAngles, level, entityState_s::number, botskills_s::perfectaim, PITCH, entityState_s::pos, rand(), bot_state_s::revengeEnemy, bot_state_s::revengeHateLevel, gentity_s::s, bot_state_s::settings, bot_settings_s::skill, bot_state_s::skills, level_locals_t::time, trajectory_t::trDelta, and YAW.

Referenced by StandardBotAI().

04688 {
04689         int i;
04690         float accVal;
04691         i = 0;
04692 
04693         if (bs->skills.perfectaim)
04694         {
04695                 return;
04696         }
04697 
04698         if (bs->aimOffsetTime > level.time)
04699         {
04700                 if (bs->aimOffsetAmtYaw)
04701                 {
04702                         bs->goalAngles[YAW] += bs->aimOffsetAmtYaw;
04703                 }
04704 
04705                 if (bs->aimOffsetAmtPitch)
04706                 {
04707                         bs->goalAngles[PITCH] += bs->aimOffsetAmtPitch;
04708                 }
04709                 
04710                 while (i <= 2)
04711                 {
04712                         if (bs->goalAngles[i] > 360)
04713                         {
04714                                 bs->goalAngles[i] -= 360;
04715                         }
04716 
04717                         if (bs->goalAngles[i] < 0)
04718                         {
04719                                 bs->goalAngles[i] += 360;
04720                         }
04721 
04722                         i++;
04723                 }
04724                 return;
04725         }
04726 
04727         accVal = bs->skills.accuracy/bs->settings.skill;
04728 
04729         if (bs->currentEnemy && BotMindTricked(bs->client, bs->currentEnemy->s.number))
04730         { //having to judge where they are by hearing them, so we should be quite inaccurate here
04731                 accVal *= 7;
04732 
04733                 if (accVal < 30)
04734                 {
04735                         accVal = 30;
04736                 }
04737         }
04738 
04739         if (bs->revengeEnemy && bs->revengeHateLevel &&
04740                 bs->currentEnemy == bs->revengeEnemy)
04741         { //bot becomes more skilled as anger level raises
04742                 accVal = accVal/bs->revengeHateLevel;
04743         }
04744 
04745         if (bs->currentEnemy && bs->frame_Enemy_Vis)
04746         { //assume our goal is aiming at the enemy, seeing as he's visible and all
04747                 if (!bs->currentEnemy->s.pos.trDelta[0] &&
04748                         !bs->currentEnemy->s.pos.trDelta[1] &&
04749                         !bs->currentEnemy->s.pos.trDelta[2])
04750                 {
04751                         accVal = 0; //he's not even moving, so he shouldn't really be hard to hit.
04752                 }
04753                 else
04754                 {
04755                         accVal += accVal*0.25; //if he's moving he's this much harder to hit
04756                 }
04757 
04758                 if (g_entities[bs->client].s.pos.trDelta[0] ||
04759                         g_entities[bs->client].s.pos.trDelta[1] ||
04760                         g_entities[bs->client].s.pos.trDelta[2])
04761                 {
04762                         accVal += accVal*0.15; //make it somewhat harder to aim if we're moving also
04763                 }
04764         }
04765 
04766         if (accVal > 90)
04767         {
04768                 accVal = 90;
04769         }
04770         if (accVal < 1)
04771         {
04772                 accVal = 0;
04773         }
04774 
04775         if (!accVal)
04776         {
04777                 bs->aimOffsetAmtYaw = 0;
04778                 bs->aimOffsetAmtPitch = 0;
04779                 return;
04780         }
04781 
04782         if (rand()%10 <= 5)
04783         {
04784                 bs->aimOffsetAmtYaw = rand()%(int)accVal;
04785         }
04786         else
04787         {
04788                 bs->aimOffsetAmtYaw = -(rand()%(int)accVal);
04789         }
04790 
04791         if (rand()%10 <= 5)
04792         {
04793                 bs->aimOffsetAmtPitch = rand()%(int)accVal;
04794         }
04795         else
04796         {
04797                 bs->aimOffsetAmtPitch = -(rand()%(int)accVal);
04798         }
04799 
04800         bs->aimOffsetTime = level.time + rand()%500 + 200;
04801 }

void BotAIRegularUpdate void   ) 
 

Definition at line 662 of file ai_main.c.

References FloatTime, regularupdate_time, and trap_BotUpdateEntityItems().

00662                               {
00663         if (regularupdate_time < FloatTime()) {
00664                 trap_BotUpdateEntityItems();
00665                 regularupdate_time = FloatTime() + 0.3;
00666         }
00667 }

int BotAISetup int  restart  ) 
 

Definition at line 7575 of file ai_main.c.

References bot_attachments, bot_camp, bot_forcepowers, bot_forgimmick, bot_getinthecarrr, bot_honorableduelacceptance, bot_pvstype, bot_wp_clearweight, bot_wp_distconnect, bot_wp_edit, bot_wp_info, bot_wp_visconnect, botstates, CVAR_CHEAT, memset(), qfalse, qtrue, trap_BotLibSetup(), trap_Cvar_Register(), and trap_Cvar_Update().

Referenced by G_InitGame().

07575                               {
07576         //rww - new bot cvars..
07577         trap_Cvar_Register(&bot_forcepowers, "bot_forcepowers", "1", CVAR_CHEAT);
07578         trap_Cvar_Register(&bot_forgimmick, "bot_forgimmick", "0", CVAR_CHEAT);
07579         trap_Cvar_Register(&bot_honorableduelacceptance, "bot_honorableduelacceptance", "0", CVAR_CHEAT);
07580         trap_Cvar_Register(&bot_pvstype, "bot_pvstype", "1", CVAR_CHEAT);
07581 #ifndef FINAL_BUILD
07582         trap_Cvar_Register(&bot_getinthecarrr, "bot_getinthecarrr", "0", 0);
07583 #endif
07584 
07585 #ifdef _DEBUG
07586         trap_Cvar_Register(&bot_nogoals, "bot_nogoals", "0", CVAR_CHEAT);
07587         trap_Cvar_Register(&bot_debugmessages, "bot_debugmessages", "0", CVAR_CHEAT);
07588 #endif
07589 
07590         trap_Cvar_Register(&bot_attachments, "bot_attachments", "1", 0);
07591         trap_Cvar_Register(&bot_camp, "bot_camp", "1", 0);
07592 
07593         trap_Cvar_Register(&bot_wp_info, "bot_wp_info", "1", 0);
07594         trap_Cvar_Register(&bot_wp_edit, "bot_wp_edit", "0", CVAR_CHEAT);
07595         trap_Cvar_Register(&bot_wp_clearweight, "bot_wp_clearweight", "1", 0);
07596         trap_Cvar_Register(&bot_wp_distconnect, "bot_wp_distconnect", "1", 0);
07597         trap_Cvar_Register(&bot_wp_visconnect, "bot_wp_visconnect", "1", 0);
07598 
07599         trap_Cvar_Update(&bot_forcepowers);
07600         //end rww
07601 
07602         //if the game is restarted for a tournament
07603         if (restart) {
07604                 return qtrue;
07605         }
07606 
07607         //initialize the bot states
07608         memset( botstates, 0, sizeof(botstates) );
07609 
07610         if (!trap_BotLibSetup())
07611         {
07612                 return qfalse; //wts?!
07613         }
07614 
07615         return qtrue;
07616 }

int BotAISetupClient int  client,
struct bot_settings_s settings,
qboolean  restart
 

Definition at line 819 of file ai_main.c.

References B_Alloc(), bot_settings_t, bot_state_t, BotAI_Print(), BotDoChat(), BotScheduleBotThink(), botstates, BotUtilizePersonality(), bot_state_s::botWeaponWeights, client, bot_state_s::client, bot_state_s::entergame_time, bot_state_s::entitynum, FloatTime, g_gametype, bot_state_s::gs, GT_DUEL, GT_POWERDUEL, vmCvar_t::integer, bot_state_s::inuse, memcpy(), memset(), bot_state_s::ms, numbots, PlayersInGame(), PRT_FATAL, qfalse, qtrue, bot_state_s::settings, bot_state_s::setupcount, trap_BotAllocGoalState(), trap_BotAllocMoveState(), trap_BotAllocWeaponState(), WP_BLASTER, WP_BOWCASTER, WP_BRYAR_PISTOL, WP_DEMP2, WP_DET_PACK, WP_DISRUPTOR, WP_FLECHETTE, WP_MELEE, WP_NONE, WP_REPEATER, WP_ROCKET_LAUNCHER, WP_SABER, WP_STUN_BATON, WP_THERMAL, WP_TRIP_MINE, and bot_state_s::ws.

Referenced by G_BotConnect().

00819                                                                                     {
00820         bot_state_t *bs;
00821 
00822         if (!botstates[client]) botstates[client] = (bot_state_t *) B_Alloc(sizeof(bot_state_t)); //G_Alloc(sizeof(bot_state_t));
00823                                                                                                                                                           //rww - G_Alloc bad! B_Alloc good.
00824 
00825         memset(botstates[client], 0, sizeof(bot_state_t));
00826 
00827         bs = botstates[client];
00828 
00829         if (bs && bs->inuse) {
00830                 BotAI_Print(PRT_FATAL, "BotAISetupClient: client %d already setup\n", client);
00831                 return qfalse;
00832         }
00833 
00834         memcpy(&bs->settings, settings, sizeof(bot_settings_t));
00835 
00836         bs->client = client; //need to know the client number before doing personality stuff
00837 
00838         //initialize weapon weight defaults..
00839         bs->botWeaponWeights[WP_NONE] = 0;
00840         bs->botWeaponWeights[WP_STUN_BATON] = 1;
00841         bs->botWeaponWeights[WP_SABER] = 10;
00842         bs->botWeaponWeights[WP_BRYAR_PISTOL] = 11;
00843         bs->botWeaponWeights[WP_BLASTER] = 12;
00844         bs->botWeaponWeights[WP_DISRUPTOR] = 13;
00845         bs->botWeaponWeights[WP_BOWCASTER] = 14;
00846         bs->botWeaponWeights[WP_REPEATER] = 15;
00847         bs->botWeaponWeights[WP_DEMP2] = 16;
00848         bs->botWeaponWeights[WP_FLECHETTE] = 17;
00849         bs->botWeaponWeights[WP_ROCKET_LAUNCHER] = 18;
00850         bs->botWeaponWeights[WP_THERMAL] = 14;
00851         bs->botWeaponWeights[WP_TRIP_MINE] = 0;
00852         bs->botWeaponWeights[WP_DET_PACK] = 0;
00853         bs->botWeaponWeights[WP_MELEE] = 1;
00854 
00855         BotUtilizePersonality(bs);
00856 
00857         if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
00858         {
00859                 bs->botWeaponWeights[WP_SABER] = 13;
00860         }
00861 
00862         //allocate a goal state
00863         bs->gs = trap_BotAllocGoalState(client);
00864 
00865         //allocate a weapon state
00866         bs->ws = trap_BotAllocWeaponState();
00867 
00868         bs->inuse = qtrue;
00869         bs->entitynum = client;
00870         bs->setupcount = 4;
00871         bs->entergame_time = FloatTime();
00872         bs->ms = trap_BotAllocMoveState();
00873         numbots++;
00874 
00875         //NOTE: reschedule the bot thinking
00876         BotScheduleBotThink();
00877 
00878         if (PlayersInGame())
00879         { //don't talk to yourself
00880                 BotDoChat(bs, "GeneralGreetings", 0);
00881         }
00882 
00883         return qtrue;
00884 }

int BotAIShutdown int  restart  ) 
 

Definition at line 7623 of file ai_main.c.

References BotAIShutdownClient(), botstates, client, bot_state_s::inuse, MAX_CLIENTS, qtrue, and trap_BotLibShutdown().

Referenced by G_ShutdownGame().

07623                                  {
07624 
07625         int i;
07626 
07627         //if the game is restarted for a tournament
07628         if ( restart ) {
07629                 //shutdown all the bots in the botlib
07630                 for (i = 0; i < MAX_CLIENTS; i++) {
07631                         if (botstates[i] && botstates[i]->inuse) {
07632                                 BotAIShutdownClient(botstates[i]->client, restart);
07633                         }
07634                 }
07635                 //don't shutdown the bot library
07636         }
07637         else {
07638                 trap_BotLibShutdown();
07639         }
07640         return qtrue;
07641 }

int BotAIShutdownClient int  client,
qboolean  restart
 

Definition at line 891 of file ai_main.c.

References bot_state_t, botstates, client, bot_state_s::gs, bot_state_s::inuse, memset(), bot_state_s::ms, numbots, qfalse, qtrue, trap_BotFreeGoalState(), trap_BotFreeMoveState(), trap_BotFreeWeaponState(), and bot_state_s::ws.

Referenced by BotAIShutdown(), and ClientDisconnect().

00891                                                       {
00892         bot_state_t *bs;
00893 
00894         bs = botstates[client];
00895         if (!bs || !bs->inuse) {
00896                 //BotAI_Print(PRT_ERROR, "BotAIShutdownClient: client %d already shutdown\n", client);
00897                 return qfalse;
00898         }
00899 
00900         trap_BotFreeMoveState(bs->ms);
00901         //free the goal state`                  
00902         trap_BotFreeGoalState(bs->gs);
00903         //free the weapon weights
00904         trap_BotFreeWeaponState(bs->ws);
00905         //
00906         //clear the bot state
00907         memset(bs, 0, sizeof(bot_state_t));
00908         //set the inuse flag to qfalse
00909         bs->inuse = qfalse;
00910         //there's one bot less
00911         numbots--;
00912         //everything went ok
00913         return qtrue;
00914 }

int BotAIStartFrame int  time  ) 
 

Definition at line 7492 of file ai_main.c.

References bot_attachments, bot_camp, bot_forgimmick, bot_getinthecarrr, bot_honorableduelacceptance, bot_pvstype, BOT_THINK_TIME, bot_wp_info, BotAI(), BotScheduleBotThink(), botstates, bot_state_s::botthink_residual, BotUpdateInput(), BotWaypointRender(), gentity_s::client, client, CON_CONNECTED, clientPersistant_t::connected, G_CheckBotSpawn(), g_entities, gBotEdit, gUpdateVars, bot_state_s::inuse, level, MAX_CLIENTS, gclient_s::pers, qtrue, level_locals_t::time, trap_BotUserCommand(), trap_Cvar_Update(), and UpdateEventTracker().

Referenced by vmMain().

07492                               {
07493         int i;
07494         int elapsed_time, thinktime;
07495         static int local_time;
07496         static int botlib_residual;
07497         static int lastbotthink_time;
07498 
07499         if (gUpdateVars < level.time)
07500         {
07501                 trap_Cvar_Update(&bot_pvstype);
07502                 trap_Cvar_Update(&bot_camp);
07503                 trap_Cvar_Update(&bot_attachments);
07504                 trap_Cvar_Update(&bot_forgimmick);
07505                 trap_Cvar_Update(&bot_honorableduelacceptance);
07506 #ifndef FINAL_BUILD
07507                 trap_Cvar_Update(&bot_getinthecarrr);
07508 #endif
07509                 gUpdateVars = level.time + 1000;
07510         }
07511 
07512         G_CheckBotSpawn();
07513 
07514         //rww - addl bot frame functions
07515         if (gBotEdit)
07516         {
07517                 trap_Cvar_Update(&bot_wp_info);
07518                 BotWaypointRender();
07519         }
07520 
07521         UpdateEventTracker();
07522         //end rww
07523 
07524         //cap the bot think time
07525         //if the bot think time changed we should reschedule the bots
07526         if (BOT_THINK_TIME != lastbotthink_time) {
07527                 lastbotthink_time = BOT_THINK_TIME;
07528                 BotScheduleBotThink();
07529         }
07530 
07531         elapsed_time = time - local_time;
07532         local_time = time;
07533 
07534         if (elapsed_time > BOT_THINK_TIME) thinktime = elapsed_time;
07535         else thinktime = BOT_THINK_TIME;
07536 
07537         // execute scheduled bot AI
07538         for( i = 0; i < MAX_CLIENTS; i++ ) {
07539                 if( !botstates[i] || !botstates[i]->inuse ) {
07540                         continue;
07541                 }
07542                 //
07543                 botstates[i]->botthink_residual += elapsed_time;
07544                 //
07545                 if ( botstates[i]->botthink_residual >= thinktime ) {
07546                         botstates[i]->botthink_residual -= thinktime;
07547 
07548                         if (g_entities[i].client->pers.connected == CON_CONNECTED) {
07549                                 BotAI(i, (float) thinktime / 1000);
07550                         }
07551                 }
07552         }
07553 
07554         // execute bot user commands every frame
07555         for( i = 0; i < MAX_CLIENTS; i++ ) {
07556                 if( !botstates[i] || !botstates[i]->inuse ) {
07557                         continue;
07558                 }
07559                 if( g_entities[i].client->pers.connected != CON_CONNECTED ) {
07560                         continue;
07561                 }
07562 
07563                 BotUpdateInput(botstates[i], time, elapsed_time);
07564                 trap_BotUserCommand(botstates[i]->client, &botstates[i]->lastucmd);
07565         }
07566 
07567         return qtrue;
07568 }

int BotCanHear bot_state_t bs,
gentity_t en,
float  endist
 

Definition at line 1945 of file ai_main.c.

References bot_state_t, BotMindTricked(), bot_state_s::client, gentity_s::client, EV_ALT_FIRE, EV_FIRE_WEAPON, EV_FOOTSTEP, EV_FOOTSTEP_METAL, EV_FOOTWADE, EV_GLOBAL_SOUND, EV_JUMP, EV_ROLL, EV_SABER_ATTACK, EV_STEP_12, EV_STEP_16, EV_STEP_4, EV_STEP_8, playerState_s::footstepTime, gBotEventTracker, gentity_t, level, MAX_PS_EVENTS, entityState_s::number, playerState_s::otherSoundLen, playerState_s::otherSoundTime, gclient_s::ps, gentity_s::s, and level_locals_t::time.

Referenced by ScanForEnemies().

01946 {
01947         float minlen;
01948 
01949         if (!en || !en->client)
01950         {
01951                 return 0;
01952         }
01953 
01954         if (en && en->client && en->client->ps.otherSoundTime > level.time)
01955         { //they made a noise in recent time
01956                 minlen = en->client->ps.otherSoundLen;
01957                 goto checkStep;
01958         }
01959 
01960         if (en && en->client && en->client->ps.footstepTime > level.time)
01961         { //they made a footstep
01962                 minlen = 256;
01963                 goto checkStep;
01964         }
01965 
01966         if (gBotEventTracker[en->s.number].eventTime < level.time)
01967         { //no recent events to check
01968                 return 0;
01969         }
01970 
01971         switch(gBotEventTracker[en->s.number].events[gBotEventTracker[en->s.number].eventSequence & (MAX_PS_EVENTS-1)])
01972         { //did the last event contain a sound?
01973         case EV_GLOBAL_SOUND:
01974                 minlen = 256;
01975                 break;
01976         case EV_FIRE_WEAPON:
01977         case EV_ALT_FIRE:
01978         case EV_SABER_ATTACK:
01979                 minlen = 512;
01980                 break;
01981         case EV_STEP_4:
01982         case EV_STEP_8:
01983         case EV_STEP_12:
01984         case EV_STEP_16:
01985         case EV_FOOTSTEP:
01986         case EV_FOOTSTEP_METAL:
01987         case EV_FOOTWADE:
01988                 minlen = 256;
01989                 break;
01990         case EV_JUMP:
01991         case EV_ROLL:
01992                 minlen = 256;
01993                 break;
01994         default:
01995                 minlen = 999999;
01996                 break;
01997         }
01998 checkStep:
01999         if (BotMindTricked(bs->client, en->s.number))
02000         { //if mindtricked by this person, cut down on the minlen so they can't "hear" as well
02001                 minlen /= 4;
02002         }
02003 
02004         if (endist <= minlen)
02005         { //we heard it
02006                 return 1;
02007         }
02008 
02009         return 0;
02010 }

float BotChangeViewAngle float  angle,
float  ideal_angle,
float  speed
 

Definition at line 443 of file ai_main.c.

References AngleMod().

00443                                                                       {
00444         float move;
00445 
00446         angle = AngleMod(angle);
00447         ideal_angle = AngleMod(ideal_angle);
00448         if (angle == ideal_angle) return angle;
00449         move = ideal_angle - angle;
00450         if (ideal_angle > angle) {
00451                 if (move > 180.0) move -= 360.0;
00452         }
00453         else {
00454                 if (move < -180.0) move += 360.0;
00455         }
00456         if (move > 0) {
00457                 if (move > speed) move = speed;
00458         }
00459         else {
00460                 if (move < -speed) move = -speed;
00461         }
00462         return AngleMod(angle + move);
00463 }

void BotChangeViewAngles bot_state_t bs,
float  thinktime
 

Definition at line 470 of file ai_main.c.

References AngleDifference(), AngleMod(), bot_state_t, bot_state_s::client, bot_state_s::currentEnemy, bot_state_s::frame_Enemy_Vis, bot_state_s::ideal_viewangles, botskills_s::maxturn, PITCH, bot_state_s::settings, bot_settings_s::skill, bot_state_s::skills, trap_EA_View(), botskills_s::turnspeed, botskills_s::turnspeed_combat, bot_state_s::viewangles, and bot_state_s::viewanglespeed.

Referenced by BotUpdateInput().

00470                                                            {
00471         float diff, factor, maxchange, anglespeed, disired_speed;
00472         int i;
00473 
00474         if (bs->ideal_viewangles[PITCH] > 180) bs->ideal_viewangles[PITCH] -= 360;
00475         
00476         if (bs->currentEnemy && bs->frame_Enemy_Vis)
00477         {
00478                 if (bs->settings.skill <= 1)
00479                 {
00480                         factor = (bs->skills.turnspeed_combat*0.4f)*bs->settings.skill;
00481                 }
00482                 else if (bs->settings.skill <= 2)
00483                 {
00484                         factor = (bs->skills.turnspeed_combat*0.6f)*bs->settings.skill;
00485                 }
00486                 else if (bs->settings.skill <= 3)
00487                 {
00488                         factor = (bs->skills.turnspeed_combat*0.8f)*bs->settings.skill;
00489                 }
00490                 else
00491                 {
00492                         factor = bs->skills.turnspeed_combat*bs->settings.skill;
00493                 }
00494         }
00495         else
00496         {
00497                 factor = bs->skills.turnspeed;
00498         }
00499 
00500         if (factor > 1)
00501         {
00502                 factor = 1;
00503         }
00504         if (factor < 0.001)
00505         {
00506                 factor = 0.001f;
00507         }
00508 
00509         maxchange = bs->skills.maxturn;
00510 
00511         //if (maxchange < 240) maxchange = 240;
00512         maxchange *= thinktime;
00513         for (i = 0; i < 2; i++) {
00514                 bs->viewangles[i] = AngleMod(bs->viewangles[i]);
00515                 bs->ideal_viewangles[i] = AngleMod(bs->ideal_viewangles[i]);
00516                 diff = AngleDifference(bs->viewangles[i], bs->ideal_viewangles[i]);
00517                 disired_speed = diff * factor;
00518                 bs->viewanglespeed[i] += (bs->viewanglespeed[i] - disired_speed);
00519                 if (bs->viewanglespeed[i] > 180) bs->viewanglespeed[i] = maxchange;
00520                 if (bs->viewanglespeed[i] < -180) bs->viewanglespeed[i] = -maxchange;
00521                 anglespeed = bs->viewanglespeed[i];
00522                 if (anglespeed > maxchange) anglespeed = maxchange;
00523                 if (anglespeed < -maxchange) anglespeed = -maxchange;
00524                 bs->viewangles[i] += anglespeed;
00525                 bs->viewangles[i] = AngleMod(bs->viewangles[i]);
00526                 bs->viewanglespeed[i] *= 0.45 * (1 - factor);
00527         }
00528         if (bs->viewangles[PITCH] > 180) bs->viewangles[PITCH] -= 360;
00529         trap_EA_View(bs->client, bs->viewangles);
00530 }

void BotCheckDetPacks bot_state_t bs  ) 
 

Definition at line 5726 of file ai_main.c.

References BOT_PLANT_BLOW_DISTANCE, bot_state_t, gentity_s::client, bot_state_s::client, bot_state_s::currentEnemy, FOFS, bot_state_s::frame_Enemy_Vis, G_Find(), gentity_t, level, NULL, entityState_s::number, OrgVisible(), bot_state_s::origin, playerState_s::origin, gentity_s::parent, bot_state_s::plantContinue, bot_state_s::plantKillEmAll, entityState_s::pos, gclient_s::ps, gentity_s::s, level_locals_t::time, trajectory_t::trBase, vec3_t, and VectorSubtract.

Referenced by StandardBotAI().

05727 {
05728         gentity_t *dp = NULL;
05729         gentity_t *myDet = NULL;
05730         vec3_t a;
05731         float enLen;
05732         float myLen;
05733 
05734         while ( (dp = G_Find( dp, FOFS(classname), "detpack") ) != NULL )
05735         {
05736                 if (dp && dp->parent && dp->parent->s.number == bs->client)
05737                 {
05738                         myDet = dp;
05739                         break;
05740                 }
05741         }
05742 
05743         if (!myDet)
05744         {
05745                 return;
05746         }
05747 
05748         if (!bs->currentEnemy || !bs->currentEnemy->client || !bs->frame_Enemy_Vis)
05749         { //require the enemy to be visilbe just to be fair..
05750 
05751                 //unless..
05752                 if (bs->currentEnemy && bs->currentEnemy->client &&
05753                         (level.time - bs->plantContinue) < 5000)
05754                 { //it's a fresh plant (within 5 seconds) so we should be able to guess
05755                         goto stillmadeit;
05756                 }
05757                 return;
05758         }
05759 
05760 stillmadeit:
05761 
05762         VectorSubtract(bs->currentEnemy->client->ps.origin, myDet->s.pos.trBase, a);
05763         enLen = VectorLength(a);
05764 
05765         VectorSubtract(bs->origin, myDet->s.pos.trBase, a);
05766         myLen = VectorLength(a);
05767 
05768         if (enLen > myLen)
05769         {
05770                 return;
05771         }
05772 
05773         if (enLen < BOT_PLANT_BLOW_DISTANCE && OrgVisible(bs->currentEnemy->client->ps.origin, myDet->s.pos.trBase, bs->currentEnemy->s.number))
05774         { //we could just call the "blow all my detpacks" function here, but I guess that's cheating.
05775                 bs->plantKillEmAll = level.time + 500;
05776         }
05777 }

qboolean BotCTFGuardDuty bot_state_t bs  ) 
 

Definition at line 1432 of file ai_main.c.

References bot_state_t, bot_state_s::ctfState, CTFSTATE_DEFENDER, g_gametype, GT_CTF, GT_CTY, vmCvar_t::integer, qboolean, qfalse, and qtrue.

Referenced by WPTouchRoutine().

01433 {
01434         if (g_gametype.integer != GT_CTF &&
01435                 g_gametype.integer != GT_CTY)
01436         {
01437                 return qfalse;
01438         }
01439 
01440         if (bs->ctfState == CTFSTATE_DEFENDER)
01441         {
01442                 return qtrue;
01443         }
01444 
01445         return qfalse;
01446 }

void BotDamageNotification gclient_t bot,
gentity_t attacker
 

Definition at line 1861 of file ai_main.c.

References bot_state_t, botstates, gentity_s::client, bot_state_s::client, playerState_s::clientNum, bot_state_s::currentEnemy, ENEMY_FORGET_MS, bot_state_s::enemySeenTime, g_entities, gclient_t, gentity_t, bot_state_s::lastAttacked, bot_state_s::lastHurt, level, MAX_CLIENTS, NULL, entityState_s::number, PassLovedOneCheck(), PassStandardEnemyChecks(), gclient_s::ps, gentity_s::s, and level_locals_t::time.

Referenced by G_Damage().

01862 {
01863         bot_state_t *bs;
01864         bot_state_t *bs_a;
01865         int i;
01866 
01867         if (!bot || !attacker || !attacker->client)
01868         {
01869                 return;
01870         }
01871 
01872         if (bot->ps.clientNum >= MAX_CLIENTS)
01873         { //an NPC.. do nothing for them.
01874                 return;
01875         }
01876 
01877         if (attacker->s.number >= MAX_CLIENTS)
01878         { //if attacker is an npc also don't care I suppose.
01879                 return;
01880         }
01881 
01882         bs_a = botstates[attacker->s.number];
01883 
01884         if (bs_a)
01885         { //if the client attacking us is a bot as well
01886                 bs_a->lastAttacked = &g_entities[bot->ps.clientNum];
01887                 i = 0;
01888 
01889                 while (i < MAX_CLIENTS)
01890                 {
01891                         if (botstates[i] &&
01892                                 i != bs_a->client &&
01893                                 botstates[i]->lastAttacked == &g_entities[bot->ps.clientNum])
01894                         {
01895                                 botstates[i]->lastAttacked = NULL;
01896                         }
01897 
01898                         i++;
01899                 }
01900         }
01901         else //got attacked by a real client, so no one gets rights to lastAttacked
01902         {
01903                 i = 0;
01904 
01905                 while (i < MAX_CLIENTS)
01906                 {
01907                         if (botstates[i] &&
01908                                 botstates[i]->lastAttacked == &g_entities[bot->ps.clientNum])
01909                         {
01910                                 botstates[i]->lastAttacked = NULL;
01911                         }
01912 
01913                         i++;
01914                 }
01915         }
01916 
01917         bs = botstates[bot->ps.clientNum];
01918 
01919         if (!bs)
01920         {
01921                 return;
01922         }
01923 
01924         bs->lastHurt = attacker;
01925 
01926         if (bs->currentEnemy)
01927         { //we don't care about the guy attacking us if we have an enemy already
01928                 return;
01929         }
01930 
01931         if (!PassStandardEnemyChecks(bs, attacker))
01932         { //the person that hurt us is not a valid enemy
01933                 return;
01934         }
01935 
01936         if (PassLovedOneCheck(bs, attacker))
01937         { //the person that hurt us is the one we love!
01938                 bs->currentEnemy = attacker;
01939                 bs->enemySeenTime = level.time + ENEMY_FORGET_MS;
01940         }
01941 }

void BotDeathNotify bot_state_t bs  ) 
 

Definition at line 5390 of file ai_main.c.

References bot_state_t, BotLovedOneDied(), botstates, bot_state_s::client, level_locals_t::clients, level, bot_state_s::loved, bot_state_s::lovednum, MAX_CLIENTS, botattachment_s::name, and strcmp().

Referenced by StandardBotAI().

05391 { //in case someone has an emotional attachment to us, we'll notify them
05392         int i = 0;
05393         int ltest = 0;
05394 
05395         while (i < MAX_CLIENTS)
05396         {
05397                 if (botstates[i] && botstates[i]->lovednum)
05398                 {
05399                         ltest = 0;
05400                         while (ltest < botstates[i]->lovednum)
05401                         {
05402                                 if (strcmp(level.clients[bs->client].pers.netname, botstates[i]->loved[ltest].name) == 0)
05403                                 {
05404                                         BotLovedOneDied(botstates[i], bs, botstates[i]->loved[ltest].level);
05405                                         break;
05406                                 }
05407 
05408                                 ltest++;
05409                         }
05410                 }
05411 
05412                 i++;
05413         }
05414 }

int BotDefendFlag bot_state_t bs  ) 
 

Definition at line 2507 of file ai_main.c.

References BASE_GUARD_DISTANCE, bot_state_t, bot_state_s::client, level_locals_t::clients, flagBlue, flagRed, level, wpobject_s::origin, bot_state_s::origin, TEAM_BLUE, TEAM_RED, vec3_t, VectorSubtract, bot_state_s::wpDestination, and wpobject_t.

Referenced by CTFTakesPriority().

02508 {
02509         wpobject_t *flagPoint;
02510         vec3_t a;
02511 
02512         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
02513         {
02514                 flagPoint = flagRed;
02515         }
02516         else if (level.clients[bs->client].sess.sessionTeam == TEAM_BLUE)
02517         {
02518                 flagPoint = flagBlue;
02519         }
02520         else
02521         {
02522                 return 0;
02523         }
02524 
02525         if (!flagPoint)
02526         {
02527                 return 0;
02528         }
02529 
02530         VectorSubtract(bs->origin, flagPoint->origin, a);
02531 
02532         if (VectorLength(a) > BASE_GUARD_DISTANCE)
02533         {
02534                 bs->wpDestination = flagPoint;
02535         }
02536 
02537         return 1;
02538 }

void BotDoTeamplayAI bot_state_t bs  ) 
 

Definition at line 4143 of file ai_main.c.

References bot_state_t, bot_state_s::isSquadLeader, NULL, bot_state_s::squadLeader, bot_state_s::state_Forced, bot_state_s::teamplayState, and TEAMPLAYSTATE_REGROUP.

Referenced by StandardBotAI().

04144 {
04145         if (bs->state_Forced)
04146         {
04147                 bs->teamplayState = bs->state_Forced;
04148         }
04149 
04150         if (bs->teamplayState == TEAMPLAYSTATE_REGROUP)
04151         { //force to find a new leader
04152                 bs->squadLeader = NULL;
04153                 bs->isSquadLeader = 0;
04154         }
04155 }

void BotEntityInfo int  entnum,
aas_entityinfo_t info
 

Definition at line 407 of file ai_main.c.

References aas_entityinfo_t, and trap_AAS_EntityInfo().

00407                                                        {
00408         trap_AAS_EntityInfo(entnum, info);
00409 }

int BotFallbackNavigation bot_state_t bs  ) 
 

Definition at line 5029 of file ai_main.c.

References AngleVectors(), bot_state_t, bot_state_s::currentEnemy, ENTITYNUM_NONE, trace_t::fraction, bot_state_s::frame_Enemy_Vis, bot_state_s::goalAngles, bot_state_s::goalPosition, MASK_SOLID, NULL, bot_state_s::origin, PITCH, rand(), ROLL, trap_Trace(), vec3_t, VectorCopy, and YAW.

Referenced by StandardBotAI().

05030 {
05031         vec3_t b_angle, fwd, trto, mins, maxs;
05032         trace_t tr;
05033 
05034         if (bs->currentEnemy && bs->frame_Enemy_Vis)
05035         {
05036                 return 2; //we're busy
05037         }
05038 
05039         mins[0] = -15;
05040         mins[1] = -15;
05041         mins[2] = 0;
05042         maxs[0] = 15;
05043         maxs[1] = 15;
05044         maxs[2] = 32;
05045 
05046         bs->goalAngles[PITCH] = 0;
05047         bs->goalAngles[ROLL] = 0;
05048 
05049         VectorCopy(bs->goalAngles, b_angle);
05050 
05051         AngleVectors(b_angle, fwd, NULL, NULL);
05052 
05053         trto[0] = bs->origin[0] + fwd[0]*16;
05054         trto[1] = bs->origin[1] + fwd[1]*16;
05055         trto[2] = bs->origin[2] + fwd[2]*16;
05056 
05057         trap_Trace(&tr, bs->origin, mins, maxs, trto, ENTITYNUM_NONE, MASK_SOLID);
05058 
05059         if (tr.fraction == 1)
05060         {
05061                 VectorCopy(trto, bs->goalPosition);
05062                 return 1; //success!
05063         }
05064         else
05065         {
05066                 bs->goalAngles[YAW] = rand()%360;
05067         }
05068 
05069         return 0;
05070 }

int BotGetEnemyFlag bot_state_t bs  ) 
 

Definition at line 2541 of file ai_main.c.

References BASE_GETENEMYFLAG_DISTANCE, bot_state_t, bot_state_s::client, level_locals_t::clients, flagBlue, flagRed, level, wpobject_s::origin, bot_state_s::origin, TEAM_BLUE, TEAM_RED, vec3_t, VectorSubtract, bot_state_s::wpDestination, and wpobject_t.

Referenced by CTFTakesPriority().

02542 {
02543         wpobject_t *flagPoint;
02544         vec3_t a;
02545 
02546         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
02547         {
02548                 flagPoint = flagBlue;
02549         }
02550         else if (level.clients[bs->client].sess.sessionTeam == TEAM_BLUE)
02551         {
02552                 flagPoint = flagRed;
02553         }
02554         else
02555         {
02556                 return 0;
02557         }
02558 
02559         if (!flagPoint)
02560         {
02561                 return 0;
02562         }
02563 
02564         VectorSubtract(bs->origin, flagPoint->origin, a);
02565 
02566         if (VectorLength(a) > BASE_GETENEMYFLAG_DISTANCE)
02567         {
02568                 bs->wpDestination = flagPoint;
02569         }
02570 
02571         return 1;
02572 }

int BotGetFlagBack bot_state_t bs  ) 
 

Definition at line 2575 of file ai_main.c.

References bot_state_t, gentity_s::client, bot_state_s::client, level_locals_t::clients, g_entities, gentity_t, GetNearestVisibleWP(), gWPArray, wpobject_s::index, level, MAX_CLIENTS, NULL, OnSameTeam(), entityState_s::origin, playerState_s::origin, playerState_s::powerups, gclient_s::ps, PW_BLUEFLAG, PW_REDFLAG, Q_irand(), gentity_s::s, TEAM_RED, level_locals_t::time, TotalTrailDistance(), vec3_t, VectorCopy, bot_state_s::wpCurrent, bot_state_s::wpDestination, and bot_state_s::wpDestSwitchTime.

Referenced by CTFTakesPriority().

02576 {
02577         int i = 0;
02578         int myFlag = 0;
02579         int foundCarrier = 0;
02580         int tempInt = 0;
02581         gentity_t *ent = NULL;
02582         vec3_t usethisvec;
02583 
02584         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
02585         {
02586                 myFlag = PW_REDFLAG;
02587         }
02588         else
02589         {
02590                 myFlag = PW_BLUEFLAG;
02591         }
02592 
02593         while (i < MAX_CLIENTS)
02594         {
02595                 ent = &g_entities[i];
02596 
02597                 if (ent && ent->client && ent->client->ps.powerups[myFlag] && !OnSameTeam(&g_entities[bs->client], ent))
02598                 {
02599                         foundCarrier = 1;
02600                         break;
02601                 }
02602 
02603                 i++;
02604         }
02605 
02606         if (!foundCarrier)
02607         {
02608                 return 0;
02609         }
02610 
02611         if (!ent)
02612         {
02613                 return 0;
02614         }
02615 
02616         if (bs->wpDestSwitchTime < level.time)
02617         {
02618                 if (ent->client)
02619                 {
02620                         VectorCopy(ent->client->ps.origin, usethisvec);
02621                 }
02622                 else
02623                 {
02624                         VectorCopy(ent->s.origin, usethisvec);
02625                 }
02626 
02627                 tempInt = GetNearestVisibleWP(usethisvec, 0);
02628 
02629                 if (tempInt != -1 && TotalTrailDistance(bs->wpCurrent->index, tempInt, bs) != -1)
02630                 {
02631                         bs->wpDestination = gWPArray[tempInt];
02632                         bs->wpDestSwitchTime = level.time + Q_irand(1000, 5000);
02633                 }
02634         }
02635 
02636         return 1;
02637 }

int BotGetFlagHome bot_state_t bs  ) 
 

Definition at line 2706 of file ai_main.c.

References BASE_FLAGWAIT_DISTANCE, bot_state_t, bot_state_s::client, level_locals_t::clients, flagBlue, flagRed, level, wpobject_s::origin, bot_state_s::origin, TEAM_BLUE, TEAM_RED, vec3_t, VectorSubtract, bot_state_s::wpDestination, and wpobject_t.

Referenced by CTFTakesPriority().

02707 {
02708         wpobject_t *flagPoint;
02709         vec3_t a;
02710 
02711         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
02712         {
02713                 flagPoint = flagRed;
02714         }
02715         else if (level.clients[bs->client].sess.sessionTeam == TEAM_BLUE)
02716         {
02717                 flagPoint = flagBlue;
02718         }
02719         else
02720         {
02721                 return 0;
02722         }
02723 
02724         if (!flagPoint)
02725         {
02726                 return 0;
02727         }
02728 
02729         VectorSubtract(bs->origin, flagPoint->origin, a);
02730 
02731         if (VectorLength(a) > BASE_FLAGWAIT_DISTANCE)
02732         {
02733                 bs->wpDestination = flagPoint;
02734         }
02735 
02736         return 1;
02737 }

int BotGetWeaponRange bot_state_t bs  ) 
 

Definition at line 2264 of file ai_main.c.

References bot_state_t, BWEAPONRANGE_LONG, BWEAPONRANGE_MELEE, BWEAPONRANGE_MID, BWEAPONRANGE_SABER, bot_state_s::cur_ps, playerState_s::weapon, WP_BLASTER, WP_BOWCASTER, WP_BRYAR_PISTOL, WP_DEMP2, WP_DET_PACK, WP_DISRUPTOR, WP_FLECHETTE, WP_MELEE, WP_REPEATER, WP_ROCKET_LAUNCHER, WP_SABER, WP_STUN_BATON, WP_THERMAL, and WP_TRIP_MINE.

Referenced by BotIsAChickenWuss(), BotTrace_Jump(), CombatBotAI(), GetIdealDestination(), Siege_TargetClosestObjective(), and StandardBotAI().

02265 {
02266         switch (bs->cur_ps.weapon)
02267         {
02268         case WP_STUN_BATON:
02269         case WP_MELEE:
02270                 return BWEAPONRANGE_MELEE;
02271         case WP_SABER:
02272                 return BWEAPONRANGE_SABER;
02273         case WP_BRYAR_PISTOL:
02274                 return BWEAPONRANGE_MID;
02275         case WP_BLASTER:
02276                 return BWEAPONRANGE_MID;
02277         case WP_DISRUPTOR:
02278                 return BWEAPONRANGE_MID;
02279         case WP_BOWCASTER:
02280                 return BWEAPONRANGE_LONG;
02281         case WP_REPEATER:
02282                 return BWEAPONRANGE_MID;
02283         case WP_DEMP2:
02284                 return BWEAPONRANGE_LONG;
02285         case WP_FLECHETTE:
02286                 return BWEAPONRANGE_LONG;
02287         case WP_ROCKET_LAUNCHER:
02288                 return BWEAPONRANGE_LONG;
02289         case WP_THERMAL:
02290                 return BWEAPONRANGE_LONG;
02291         case WP_TRIP_MINE:
02292                 return BWEAPONRANGE_LONG;
02293         case WP_DET_PACK:
02294                 return BWEAPONRANGE_LONG;
02295         default:
02296                 return BWEAPONRANGE_MID;
02297         }
02298 }

int BotGuardFlagCarrier bot_state_t bs  ) 
 

Definition at line 2641 of file ai_main.c.

References bot_state_t, gentity_s::client, bot_state_s::client, level_locals_t::clients, g_entities, gentity_t, GetNearestVisibleWP(), gWPArray, wpobject_s::index, level, MAX_CLIENTS, NULL, OnSameTeam(), entityState_s::origin, playerState_s::origin, playerState_s::powerups, gclient_s::ps, PW_BLUEFLAG, PW_REDFLAG, Q_irand(), gentity_s::s, TEAM_RED, level_locals_t::time, TotalTrailDistance(), vec3_t, VectorCopy, bot_state_s::wpCurrent, bot_state_s::wpDestination, and bot_state_s::wpDestSwitchTime.

Referenced by CTFTakesPriority().

02642 {
02643         int i = 0;
02644         int enemyFlag = 0;
02645         int foundCarrier = 0;
02646         int tempInt = 0;
02647         gentity_t *ent = NULL;
02648         vec3_t usethisvec;
02649 
02650         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
02651         {
02652                 enemyFlag = PW_BLUEFLAG;
02653         }
02654         else
02655         {
02656                 enemyFlag = PW_REDFLAG;
02657         }
02658 
02659         while (i < MAX_CLIENTS)
02660         {
02661                 ent = &g_entities[i];
02662 
02663                 if (ent && ent->client && ent->client->ps.powerups[enemyFlag] && OnSameTeam(&g_entities[bs->client], ent))
02664                 {
02665                         foundCarrier = 1;
02666                         break;
02667                 }
02668 
02669                 i++;
02670         }
02671 
02672         if (!foundCarrier)
02673         {
02674                 return 0;
02675         }
02676 
02677         if (!ent)
02678         {
02679                 return 0;
02680         }
02681 
02682         if (bs->wpDestSwitchTime < level.time)
02683         {
02684                 if (ent->client)
02685                 {
02686                         VectorCopy(ent->client->ps.origin, usethisvec);
02687                 }
02688                 else
02689                 {
02690                         VectorCopy(ent->s.origin, usethisvec);
02691                 }
02692 
02693                 tempInt = GetNearestVisibleWP(usethisvec, 0);
02694 
02695                 if (tempInt != -1 && TotalTrailDistance(bs->wpCurrent->index, tempInt, bs) != -1)
02696                 {
02697                         bs->wpDestination = gWPArray[tempInt];
02698                         bs->wpDestSwitchTime = level.time + Q_irand(1000, 5000);
02699                 }
02700         }
02701 
02702         return 1;
02703 }

int BotHasAssociated bot_state_t bs,
wpobject_t wp
 

Definition at line 3500 of file ai_main.c.

References playerState_s::ammo, wpobject_s::associated_entity, bot_state_t, bot_state_s::cur_ps, ENTITYNUM_NONE, g_entities, gentity_t, gitem_s::giTag, gitem_s::giType, IT_AMMO, IT_HOLDABLE, IT_POWERUP, IT_WEAPON, gentity_s::item, playerState_s::powerups, STAT_HOLDABLE_ITEMS, STAT_WEAPONS, playerState_s::stats, and wpobject_t.

Referenced by GetBestIdleGoal().

03501 {
03502         gentity_t *as;
03503 
03504         if (wp->associated_entity == ENTITYNUM_NONE)
03505         { //make it think this is an item we have so we don't go after nothing
03506                 return 1;
03507         }
03508 
03509         as = &g_entities[wp->associated_entity];
03510 
03511         if (!as || !as->item)
03512         {
03513                 return 0;
03514         }
03515 
03516         if (as->item->giType == IT_WEAPON)
03517         {
03518                 if (bs->cur_ps.stats[STAT_WEAPONS] & (1 << as->item->giTag))
03519                 {
03520                         return 1;
03521                 }
03522 
03523                 return 0;
03524         }
03525         else if (as->item->giType == IT_HOLDABLE)
03526         {
03527                 if (bs->cur_ps.stats[STAT_HOLDABLE_ITEMS] & (1 << as->item->giTag))
03528                 {
03529                         return 1;
03530                 }
03531 
03532                 return 0;
03533         }
03534         else if (as->item->giType == IT_POWERUP)
03535         {
03536                 if (bs->cur_ps.powerups[as->item->giTag])
03537                 {
03538                         return 1;
03539                 }
03540 
03541                 return 0;
03542         }
03543         else if (as->item->giType == IT_AMMO)
03544         {
03545                 if (bs->cur_ps.ammo[as->item->giTag] > 10) //hack
03546                 {
03547                         return 1;
03548                 }
03549 
03550                 return 0;
03551         }
03552 
03553         return 0;
03554 }

void BotInputToUserCommand bot_input_t bi,
usercmd_t ucmd,
int  delta_angles[3],
int  time,
int  useTime
 

Definition at line 537 of file ai_main.c.

References abs(), ACTION_ALT_ATTACK, ACTION_ATTACK, ACTION_CROUCH, ACTION_DELAYEDJUMP, ACTION_FORCEPOWER, ACTION_GESTURE, ACTION_JUMP, ACTION_MOVEBACK, ACTION_MOVEFORWARD, ACTION_MOVELEFT, ACTION_MOVERIGHT, ACTION_RESPAWN, ACTION_USE, ACTION_WALK, bot_input_s::actionflags, ANGLE2SHORT, usercmd_s::angles, AngleVectors(), bot_input_t, BUTTON_ALT_ATTACK, BUTTON_ATTACK, BUTTON_FORCEPOWER, BUTTON_GESTURE, BUTTON_USE, BUTTON_USE_HOLDABLE, BUTTON_WALKING, usercmd_s::buttons, bot_input_s::dir, DotProduct, usercmd_s::forwardmove, level, memset(), NULL, PITCH, Q_irand(), usercmd_s::rightmove, ROLL, usercmd_s::serverTime, bot_input_s::speed, level_locals_t::time, ucmd, usercmd_s::upmove, usercmd_t, vec3_t, bot_input_s::viewangles, usercmd_s::weapon, bot_input_s::weapon, WP_BRYAR_PISTOL, WP_NONE, and YAW.

Referenced by BotUpdateInput().

00537                                                                                                          {
00538         vec3_t angles, forward, right;
00539         short temp;
00540         int j;
00541 
00542         //clear the whole structure
00543         memset(ucmd, 0, sizeof(usercmd_t));
00544         //
00545         //Com_Printf("dir = %f %f %f speed = %f\n", bi->dir[0], bi->dir[1], bi->dir[2], bi->speed);
00546         //the duration for the user command in milli seconds
00547         ucmd->serverTime = time;
00548         //
00549         if (bi->actionflags & ACTION_DELAYEDJUMP) {
00550                 bi->actionflags |= ACTION_JUMP;
00551                 bi->actionflags &= ~ACTION_DELAYEDJUMP;
00552         }
00553         //set the buttons
00554         if (bi->actionflags & ACTION_RESPAWN) ucmd->buttons = BUTTON_ATTACK;
00555         if (bi->actionflags & ACTION_ATTACK) ucmd->buttons |= BUTTON_ATTACK;
00556         if (bi->actionflags & ACTION_ALT_ATTACK) ucmd->buttons |= BUTTON_ALT_ATTACK;
00557 //      if (bi->actionflags & ACTION_TALK) ucmd->buttons |= BUTTON_TALK;
00558         if (bi->actionflags & ACTION_GESTURE) ucmd->buttons |= BUTTON_GESTURE;
00559         if (bi->actionflags & ACTION_USE) ucmd->buttons |= BUTTON_USE_HOLDABLE;
00560         if (bi->actionflags & ACTION_WALK) ucmd->buttons |= BUTTON_WALKING;
00561 
00562         if (bi->actionflags & ACTION_FORCEPOWER) ucmd->buttons |= BUTTON_FORCEPOWER;
00563 
00564         if (useTime < level.time && Q_irand(1, 10) < 5)
00565         { //for now just hit use randomly in case there's something useable around
00566                 ucmd->buttons |= BUTTON_USE;
00567         }
00568 #if 0
00569 // Here's an interesting bit.  The bots in TA used buttons to do additional gestures.
00570 // I ripped them out because I didn't want too many buttons given the fact that I was already adding some for JK2.
00571 // We can always add some back in if we want though.
00572         if (bi->actionflags & ACTION_AFFIRMATIVE) ucmd->buttons |= BUTTON_AFFIRMATIVE;
00573         if (bi->actionflags & ACTION_NEGATIVE) ucmd->buttons |= BUTTON_NEGATIVE;
00574         if (bi->actionflags & ACTION_GETFLAG) ucmd->buttons |= BUTTON_GETFLAG;
00575         if (bi->actionflags & ACTION_GUARDBASE) ucmd->buttons |= BUTTON_GUARDBASE;
00576         if (bi->actionflags & ACTION_PATROL) ucmd->buttons |= BUTTON_PATROL;
00577         if (bi->actionflags & ACTION_FOLLOWME) ucmd->buttons |= BUTTON_FOLLOWME;
00578 #endif //0
00579 
00580         if (bi->weapon == WP_NONE)
00581         {
00582 #ifdef _DEBUG
00583 //              Com_Printf("WARNING: Bot tried to use WP_NONE!\n");
00584 #endif
00585                 bi->weapon = WP_BRYAR_PISTOL;
00586         }
00587 
00588         //
00589         ucmd->weapon = bi->weapon;
00590         //set the view angles
00591         //NOTE: the ucmd->angles are the angles WITHOUT the delta angles
00592         ucmd->angles[PITCH] = ANGLE2SHORT(bi->viewangles[PITCH]);
00593         ucmd->angles[YAW] = ANGLE2SHORT(bi->viewangles[YAW]);
00594         ucmd->angles[ROLL] = ANGLE2SHORT(bi->viewangles[ROLL]);
00595         //subtract the delta angles
00596         for (j = 0; j < 3; j++) {
00597                 temp = ucmd->angles[j] - delta_angles[j];
00598                 ucmd->angles[j] = temp;
00599         }
00600         //NOTE: movement is relative to the REAL view angles
00601         //get the horizontal forward and right vector
00602         //get the pitch in the range [-180, 180]
00603         if (bi->dir[2]) angles[PITCH] = bi->viewangles[PITCH];
00604         else angles[PITCH] = 0;
00605         angles[YAW] = bi->viewangles[YAW];
00606         angles[ROLL] = 0;
00607         AngleVectors(angles, forward, right, NULL);
00608         //bot input speed is in the range [0, 400]
00609         bi->speed = bi->speed * 127 / 400;
00610         //set the view independent movement
00611         ucmd->forwardmove = DotProduct(forward, bi->dir) * bi->speed;
00612         ucmd->rightmove = DotProduct(right, bi->dir) * bi->speed;
00613         ucmd->upmove = abs((int)(forward[2])) * bi->dir[2] * bi->speed;
00614         //normal keyboard movement
00615         if (bi->actionflags & ACTION_MOVEFORWARD) ucmd->forwardmove += 127;
00616         if (bi->actionflags & ACTION_MOVEBACK) ucmd->forwardmove -= 127;
00617         if (bi->actionflags & ACTION_MOVELEFT) ucmd->rightmove -= 127;
00618         if (bi->actionflags & ACTION_MOVERIGHT) ucmd->rightmove += 127;
00619         //jump/moveup
00620         if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127;
00621         //crouch/movedown
00622         if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127;
00623         //
00624         //Com_Printf("forward = %d right = %d up = %d\n", ucmd.forwardmove, ucmd.rightmove, ucmd.upmove);
00625         //Com_Printf("ucmd->serverTime = %d\n", ucmd->serverTime);
00626 }

int BotIsAChickenWuss bot_state_t bs  ) 
 

Definition at line 2301 of file ai_main.c.

References BOT_RUN_HEALTH, bot_state_t, BotGetWeaponRange(), BWEAPONRANGE_MELEE, BWEAPONRANGE_SABER, bot_state_s::chickenWussCalculationTime, gentity_s::client, bot_state_s::client, bot_state_s::cur_ps, bot_state_s::currentEnemy, playerState_s::electrifyTime, playerState_s::fd, forcedata_s::forcePowersActive, FP_RAGE, bot_state_s::frame_Enemy_Len, g_entities, g_gametype, gLevelFlags, GT_CTF, GT_JEDIMASTER, GT_SINGLE_PLAYER, gentity_s::health, vmCvar_t::integer, playerState_s::isJediMaster, level, LEVELFLAG_IMUSTNTRUNAWAY, MAX_CHICKENWUSS_TIME, playerState_s::powerups, gclient_s::ps, PW_BLUEFLAG, PW_REDFLAG, bot_state_s::saberSpecialist, level_locals_t::time, playerState_s::weapon, WP_BRYAR_PISTOL, WP_ROCKET_LAUNCHER, and WP_SABER.

Referenced by GetIdealDestination(), and WPTouchRoutine().

02302 {
02303         int bWRange;
02304 
02305         if (gLevelFlags & LEVELFLAG_IMUSTNTRUNAWAY)
02306         { //The level says we mustn't run away!
02307                 return 0;
02308         }
02309 
02310         if (g_gametype.integer == GT_SINGLE_PLAYER)
02311         { //"coop" (not really)
02312                 return 0;
02313         }
02314 
02315         if (g_gametype.integer == GT_JEDIMASTER && !bs->cur_ps.isJediMaster)
02316         { //Then you may know no fear.
02317                 //Well, unless he's strong.
02318                 if (bs->currentEnemy && bs->currentEnemy->client &&
02319                         bs->currentEnemy->client->ps.isJediMaster &&
02320                         bs->currentEnemy->health > 40 &&
02321                         bs->cur_ps.weapon < WP_ROCKET_LAUNCHER)
02322                 { //explosive weapons are most effective against the Jedi Master
02323                         goto jmPass;
02324                 }
02325                 return 0;
02326         }
02327 
02328         if (g_gametype.integer == GT_CTF && bs->currentEnemy && bs->currentEnemy->client)
02329         {
02330                 if (bs->currentEnemy->client->ps.powerups[PW_REDFLAG] ||
02331                         bs->currentEnemy->client->ps.powerups[PW_BLUEFLAG])
02332                 { //don't be afraid of flag carriers, they must die!
02333                         return 0;
02334                 }
02335         }
02336 
02337 jmPass:
02338         if (bs->chickenWussCalculationTime > level.time)
02339         {
02340                 return 2; //don't want to keep going between two points...
02341         }
02342 
02343         if (bs->cur_ps.fd.forcePowersActive & (1 << FP_RAGE))
02344         { //don't run while raging
02345                 return 0;
02346         }
02347 
02348         if (g_gametype.integer == GT_JEDIMASTER && !bs->cur_ps.isJediMaster)
02349         { //be frightened of the jedi master? I guess in this case.
02350                 return 1;
02351         }
02352 
02353         bs->chickenWussCalculationTime = level.time + MAX_CHICKENWUSS_TIME;
02354 
02355         if (g_entities[bs->client].health < BOT_RUN_HEALTH)
02356         { //we're low on health, let's get away
02357                 return 1;
02358         }
02359 
02360         bWRange = BotGetWeaponRange(bs);
02361 
02362         if (bWRange == BWEAPONRANGE_MELEE || bWRange == BWEAPONRANGE_SABER)
02363         {
02364                 if (bWRange != BWEAPONRANGE_SABER || !bs->saberSpecialist)
02365                 { //run away if we're using melee, or if we're using a saber and not a "saber specialist"
02366                         return 1;
02367                 }
02368         }
02369 
02370         if (bs->cur_ps.weapon == WP_BRYAR_PISTOL)
02371         { //the bryar is a weak weapon, so just try to find a new one if it's what you're having to use
02372                 return 1;
02373         }
02374 
02375         if (bs->currentEnemy && bs->currentEnemy->client &&
02376                 bs->currentEnemy->client->ps.weapon == WP_SABER &&
02377                 bs->frame_Enemy_Len < 512 && bs->cur_ps.weapon != WP_SABER)
02378         { //if close to an enemy with a saber and not using a saber, then try to back off
02379                 return 1;
02380         }
02381 
02382         if ((level.time-bs->cur_ps.electrifyTime) < 16000)
02383         { //lightning is dangerous.
02384                 return 1;
02385         }
02386 
02387         //didn't run, reset the timer
02388         bs->chickenWussCalculationTime = 0;
02389 
02390         return 0;
02391 }

void BotLovedOneDied bot_state_t bs,
bot_state_t loved,
int  lovelevel
 

Definition at line 5315 of file ai_main.c.

References bot_attachments, bot_state_t, BotDoChat(), bot_state_s::chatAltObject, bot_state_s::chatObject, bot_state_s::client, gentity_s::client, g_entities, g_gametype, GT_DUEL, GT_POWERDUEL, vmCvar_t::integer, IsTeamplay(), bot_state_s::lastHurt, bot_state_s::loved_death_thresh, NULL, entityState_s::number, OnSameTeam(), PassLovedOneCheck(), bot_state_s::revengeEnemy, bot_state_s::revengeHateLevel, and gentity_s::s.

Referenced by BotDeathNotify().

05316 {
05317         if (!loved->lastHurt || !loved->lastHurt->client ||
05318                 loved->lastHurt->s.number == loved->client)
05319         {
05320                 return;
05321         }
05322 
05323         if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
05324         { //There is no love in 1-on-1
05325                 return;
05326         }
05327 
05328         if (!IsTeamplay())
05329         {
05330                 if (lovelevel < 2)
05331                 {
05332                         return;
05333                 }
05334         }
05335         else if (OnSameTeam(&g_entities[bs->client], loved->lastHurt))
05336         { //don't hate teammates no matter what
05337                 return;
05338         }
05339 
05340         if (loved->client == loved->lastHurt->s.number)
05341         {
05342                 return;
05343         }
05344 
05345         if (bs->client == loved->lastHurt->s.number)
05346         { //oops!
05347                 return;
05348         }
05349         
05350         if (!bot_attachments.integer)
05351         {
05352                 return;
05353         }
05354 
05355         if (!PassLovedOneCheck(bs, loved->lastHurt))
05356         { //a loved one killed a loved one.. you cannot hate them
05357                 bs->chatObject = loved->lastHurt;
05358                 bs->chatAltObject = &g_entities[loved->client];
05359                 BotDoChat(bs, "LovedOneKilledLovedOne", 0);
05360                 return;
05361         }
05362 
05363         if (bs->revengeEnemy == loved->lastHurt)
05364         {
05365                 if (bs->revengeHateLevel < bs->loved_death_thresh)
05366                 {
05367                         bs->revengeHateLevel++;
05368 
05369                         if (bs->revengeHateLevel == bs->loved_death_thresh)
05370                         {
05371                                 //broke into the highest anger level
05372                                 //CHAT: Hatred section
05373                                 bs->chatObject = loved->lastHurt;
05374                                 bs->chatAltObject = NULL;
05375                                 BotDoChat(bs, "Hatred", 1);
05376                         }
05377                 }
05378         }
05379         else if (bs->revengeHateLevel < bs->loved_death_thresh-1)
05380         { //only switch hatred if we don't hate the existing revenge-enemy too much
05381                 //CHAT: BelovedKilled section
05382                 bs->chatObject = &g_entities[loved->client];
05383                 bs->chatAltObject = loved->lastHurt;
05384                 BotDoChat(bs, "BelovedKilled", 0);
05385                 bs->revengeHateLevel = 0;
05386                 bs->revengeEnemy = loved->lastHurt;
05387         }
05388 }

int BotMindTricked int  botClient,
int  enemyClient
 

Definition at line 279 of file ai_main.c.

References gentity_s::client, playerState_s::fd, forcedata_t, forcedata_s::forceMindtrickTargetIndex, forcedata_s::forceMindtrickTargetIndex2, forcedata_s::forceMindtrickTargetIndex3, forcedata_s::forceMindtrickTargetIndex4, g_entities, and gclient_s::ps.

Referenced by BotAimOffsetGoalAngles(), BotCanHear(), PassStandardEnemyChecks(), ScanForEnemies(), and StandardBotAI().

00280 {
00281         forcedata_t *fd;
00282 
00283         if (!g_entities[enemyClient].client)
00284         {
00285                 return 0;
00286         }
00287         
00288         fd = &g_entities[enemyClient].client->ps.fd;
00289 
00290         if (!fd)
00291         {
00292                 return 0;
00293         }
00294 
00295         if (botClient > 47)
00296         {
00297                 if (fd->forceMindtrickTargetIndex4 & (1 << (botClient-48)))
00298                 {
00299                         return 1;
00300                 }
00301         }
00302         else if (botClient > 31)
00303         {
00304                 if (fd->forceMindtrickTargetIndex3 & (1 << (botClient-32)))
00305                 {
00306                         return 1;
00307                 }
00308         }
00309         else if (botClient > 15)
00310         {
00311                 if (fd->forceMindtrickTargetIndex2 & (1 << (botClient-16)))
00312                 {
00313                         return 1;
00314                 }
00315         }
00316         else
00317         {
00318                 if (fd->forceMindtrickTargetIndex & (1 << botClient))
00319                 {
00320                         return 1;
00321                 }
00322         }
00323 
00324         return 0;
00325 }

void BotOrder gentity_t ent,
int  clientnum,
int  ordernum
 

Definition at line 184 of file ai_main.c.

References BotDoChat(), BotReportStatus(), botstates, BotStraightTPOrderCheck(), bot_state_s::chatAltObject, bot_state_s::chatObject, bot_state_s::chatTeam, gentity_s::client, CTFSTATE_MAXCTFSTATES, CTFSTATE_NONE, g_entities, g_gametype, gentity_t, GT_CTF, GT_CTY, GT_SIEGE, GT_TEAM, vmCvar_t::integer, MAX_CLIENTS, NULL, OnSameTeam(), gclient_s::sess, SIEGESTATE_MAXSIEGESTATES, SIEGESTATE_NONE, bot_state_s::state_Forced, clientSession_t::teamLeader, TEAMPLAYSTATE_MAXTPSTATES, and TEAMPLAYSTATE_NONE.

00185 {
00186         int stateMin = 0;
00187         int stateMax = 0;
00188         int i = 0;
00189 
00190         if (!ent || !ent->client || !ent->client->sess.teamLeader)
00191         {
00192                 return;
00193         }
00194 
00195         if (clientnum != -1 && !botstates[clientnum])
00196         {
00197                 return;
00198         }
00199 
00200         if (clientnum != -1 && !OnSameTeam(ent, &g_entities[clientnum]))
00201         {
00202                 return;
00203         }
00204 
00205         if (g_gametype.integer != GT_CTF && g_gametype.integer != GT_CTY && g_gametype.integer != GT_SIEGE &&
00206                 g_gametype.integer != GT_TEAM)
00207         {
00208                 return;
00209         }
00210 
00211         if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY)
00212         {
00213                 stateMin = CTFSTATE_NONE;
00214                 stateMax = CTFSTATE_MAXCTFSTATES;
00215         }
00216         else if (g_gametype.integer == GT_SIEGE)
00217         {
00218                 stateMin = SIEGESTATE_NONE;
00219                 stateMax = SIEGESTATE_MAXSIEGESTATES;
00220         }
00221         else if (g_gametype.integer == GT_TEAM)
00222         {
00223                 stateMin = TEAMPLAYSTATE_NONE;
00224                 stateMax = TEAMPLAYSTATE_MAXTPSTATES;
00225         }
00226 
00227         if ((ordernum < stateMin && ordernum != -1) || ordernum >= stateMax)
00228         {
00229                 return;
00230         }
00231 
00232         if (clientnum != -1)
00233         {
00234                 if (ordernum == -1)
00235                 {
00236                         BotReportStatus(botstates[clientnum]);
00237                 }
00238                 else
00239                 {
00240                         BotStraightTPOrderCheck(ent, ordernum, botstates[clientnum]);
00241                         botstates[clientnum]->state_Forced = ordernum;
00242                         botstates[clientnum]->chatObject = ent;
00243                         botstates[clientnum]->chatAltObject = NULL;
00244                         if (BotDoChat(botstates[clientnum], "OrderAccepted", 1))
00245                         {
00246                                 botstates[clientnum]->chatTeam = 1;
00247                         }
00248                 }
00249         }
00250         else
00251         {
00252                 while (i < MAX_CLIENTS)
00253                 {
00254                         if (botstates[i] && OnSameTeam(ent, &g_entities[i]))
00255                         {
00256                                 if (ordernum == -1)
00257                                 {
00258                                         BotReportStatus(botstates[i]);
00259                                 }
00260                                 else
00261                                 {
00262                                         BotStraightTPOrderCheck(ent, ordernum, botstates[i]);
00263                                         botstates[i]->state_Forced = ordernum;
00264                                         botstates[i]->chatObject = ent;
00265                                         botstates[i]->chatAltObject = NULL;
00266                                         if (BotDoChat(botstates[i], "OrderAccepted", 0))
00267                                         {
00268                                                 botstates[i]->chatTeam = 1;
00269                                         }
00270                                 }
00271                         }
00272 
00273                         i++;
00274                 }
00275         }
00276 }

qboolean BotPVSCheck const vec3_t  p1,
const vec3_t  p2
 

Definition at line 1086 of file ai_main.c.

References bot_pvstype, g_RMG, vmCvar_t::integer, qboolean, qfalse, qtrue, trap_InPVS(), vec3_t, and VectorSubtract.

Referenced by GetNearestBadThing(), GetNearestVisibleWP(), ScanForEnemies(), and SiegeTakesPriority().

01087 {
01088         if (g_RMG.integer && bot_pvstype.integer)
01089         {
01090                 vec3_t subPoint;
01091                 VectorSubtract(p1, p2, subPoint);
01092 
01093                 if (VectorLength(subPoint) > 5000)
01094                 {
01095                         return qfalse;
01096                 }
01097                 return qtrue;
01098         }
01099 
01100         return trap_InPVS(p1, p2);
01101 }

void BotReplyGreetings bot_state_t bs  ) 
 

Definition at line 5618 of file ai_main.c.

References bot_state_t, BotDoChat(), botstates, bot_state_s::canChat, bot_state_s::chatAltObject, bot_state_s::chatObject, bot_state_s::client, g_entities, MAX_CLIENTS, and NULL.

Referenced by StandardBotAI().

05619 {
05620         int i = 0;
05621         int numhello = 0;
05622 
05623         while (i < MAX_CLIENTS)
05624         {
05625                 if (botstates[i] &&
05626                         botstates[i]->canChat &&
05627                         i != bs->client)
05628                 {
05629                         botstates[i]->chatObject = &g_entities[bs->client];
05630                         botstates[i]->chatAltObject = NULL;
05631                         if (BotDoChat(botstates[i], "ResponseGreetings", 0))
05632                         {
05633                                 numhello++;
05634                         }
05635                 }
05636 
05637                 if (numhello > 3)
05638                 { //don't let more than 4 bots say hello at once
05639                         return;
05640                 }
05641 
05642                 i++;
05643         }
05644 }

void BotReportStatus bot_state_t bs  ) 
 

Definition at line 167 of file ai_main.c.

References bot_state_t, bot_state_s::client, bot_state_s::ctfState, ctfStateDescriptions, g_gametype, GT_CTF, GT_CTY, GT_SIEGE, GT_TEAM, vmCvar_t::integer, bot_state_s::siegeState, siegeStateDescriptions, bot_state_s::teamplayState, teamplayStateDescriptions, and trap_EA_SayTeam().

Referenced by BotOrder().

00168 {
00169         if (g_gametype.integer == GT_TEAM)
00170         {
00171                 trap_EA_SayTeam(bs->client, teamplayStateDescriptions[bs->teamplayState]);
00172         }
00173         else if (g_gametype.integer == GT_SIEGE)
00174         {
00175                 trap_EA_SayTeam(bs->client, siegeStateDescriptions[bs->siegeState]);
00176         }
00177         else if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY)
00178         {
00179                 trap_EA_SayTeam(bs->client, ctfStateDescriptions[bs->ctfState]);
00180         }
00181 }

void BotResetState bot_state_t bs  ) 
 

Definition at line 924 of file ai_main.c.

References bot_settings_t, bot_state_t, client, bot_state_s::client, bot_state_s::cur_ps, bot_state_s::entergame_time, bot_state_s::entitynum, bot_state_s::gs, bot_state_s::inuse, memcpy(), memset(), bot_state_s::ms, playerState_t, bot_state_s::settings, trap_BotResetAvoidGoals(), trap_BotResetAvoidReach(), trap_BotResetGoalState(), trap_BotResetMoveState(), trap_BotResetWeaponState(), and bot_state_s::ws.

Referenced by BotAILoadMap().

00924                                     {
00925         int client, entitynum, inuse;
00926         int movestate, goalstate, weaponstate;
00927         bot_settings_t settings;
00928         playerState_t ps;                                                       //current player state
00929         float entergame_time;
00930 
00931         //save some things that should not be reset here
00932         memcpy(&settings, &bs->settings, sizeof(bot_settings_t));
00933         memcpy(&ps, &bs->cur_ps, sizeof(playerState_t));
00934         inuse = bs->inuse;
00935         client = bs->client;
00936         entitynum = bs->entitynum;
00937         movestate = bs->ms;
00938         goalstate = bs->gs;
00939         weaponstate = bs->ws;
00940         entergame_time = bs->entergame_time;
00941         //reset the whole state
00942         memset(bs, 0, sizeof(bot_state_t));
00943         //copy back some state stuff that should not be reset
00944         bs->ms = movestate;
00945         bs->gs = goalstate;
00946         bs->ws = weaponstate;
00947         memcpy(&bs->cur_ps, &ps, sizeof(playerState_t));
00948         memcpy(&bs->settings, &settings, sizeof(bot_settings_t));
00949         bs->inuse = inuse;
00950         bs->client = client;
00951         bs->entitynum = entitynum;
00952         bs->entergame_time = entergame_time;
00953         //reset several states
00954         if (bs->ms) trap_BotResetMoveState(bs->ms);
00955         if (bs->gs) trap_BotResetGoalState(bs->gs);
00956         if (bs->ws) trap_BotResetWeaponState(bs->ws);
00957         if (bs->gs) trap_BotResetAvoidGoals(bs->gs);
00958         if (bs->ms) trap_BotResetAvoidReach(bs->ms);
00959 }

void BotScanForLeader bot_state_t bs  ) 
 

Definition at line 5585 of file ai_main.c.

References bot_state_t, botstates, bot_state_s::client, gentity_s::client, g_entities, gentity_t, GetLoveLevel(), bot_state_s::isSquadLeader, IsTeamplay(), MAX_CLIENTS, OnSameTeam(), and bot_state_s::squadLeader.

Referenced by StandardBotAI().

05586 { //bots will only automatically obtain a leader if it's another bot using this method.
05587         int i = 0;
05588         gentity_t *ent;
05589 
05590         if (bs->isSquadLeader)
05591         {
05592                 return;
05593         }
05594 
05595         while (i < MAX_CLIENTS)
05596         {
05597                 ent = &g_entities[i];
05598 
05599                 if (ent && ent->client && botstates[i] && botstates[i]->isSquadLeader && bs->client != i)
05600                 {
05601                         if (OnSameTeam(&g_entities[bs->client], ent))
05602                         {
05603                                 bs->squadLeader = ent;
05604                                 break;
05605                         }
05606                         if (GetLoveLevel(bs, botstates[i]) > 1 && !IsTeamplay())
05607                         { //ignore love status regarding squad leaders if we're in teamplay
05608                                 bs->squadLeader = ent;
05609                                 break;
05610                         }
05611                 }
05612 
05613                 i++;
05614         }
05615 }

void BotScheduleBotThink void   ) 
 

Definition at line 778 of file ai_main.c.

References BOT_THINK_TIME, botstates, bot_state_s::botthink_residual, bot_state_s::inuse, MAX_CLIENTS, and numbots.

Referenced by BotAISetupClient(), and BotAIStartFrame().

00778                                {
00779         int i, botnum;
00780 
00781         botnum = 0;
00782 
00783         for( i = 0; i < MAX_CLIENTS; i++ ) {
00784                 if( !botstates[i] || !botstates[i]->inuse ) {
00785                         continue;
00786                 }
00787                 //initialize the bot think residual time
00788                 botstates[i]->botthink_residual = BOT_THINK_TIME * botnum / numbots;
00789                 botnum++;
00790         }
00791 }

int BotSelectChoiceWeapon bot_state_t bs,
int  weapon,
int  doselection
 

Definition at line 5216 of file ai_main.c.

References playerState_s::ammo, weaponData_s::ammoIndex, bot_state_t, BotSelectWeapon(), bot_state_s::client, bot_state_s::cur_ps, weaponData_s::energyPerShot, STAT_WEAPONS, playerState_s::stats, bot_state_s::virtualWeapon, playerState_s::weapon, weaponData, and WP_NUM_WEAPONS.

Referenced by StandardBotAI().

05217 { //if !doselection then bot will only check if he has the specified weapon and return 1 (yes) or 0 (no)
05218         int i;
05219         int hasit = 0;
05220 
05221         i = 0;
05222 
05223         while (i < WP_NUM_WEAPONS)
05224         {
05225                 if (bs->cur_ps.ammo[weaponData[i].ammoIndex] > weaponData[i].energyPerShot &&
05226                         i == weapon &&
05227                         (bs->cur_ps.stats[STAT_WEAPONS] & (1 << i)))
05228                 {
05229                         hasit = 1;
05230                         break;
05231                 }
05232 
05233                 i++;
05234         }
05235 
05236         if (hasit && bs->cur_ps.weapon != weapon && doselection && bs->virtualWeapon != weapon)
05237         {
05238                 bs->virtualWeapon = weapon;
05239                 BotSelectWeapon(bs->client, weapon);
05240                 //bs->cur_ps.weapon = weapon;
05241                 //level.clients[bs->client].ps.weapon = weapon;
05242                 return 2;
05243         }
05244 
05245         if (hasit)
05246         {
05247                 return 1;
05248         }
05249 
05250         return 0;
05251 }

int BotSelectIdealWeapon bot_state_t bs  ) 
 

Definition at line 5123 of file ai_main.c.

References playerState_s::ammo, weaponData_s::ammoIndex, bot_state_t, BotSelectWeapon(), BotWeaponSelectable(), bot_state_s::botWeaponWeights, bot_state_s::client, gentity_s::client, bot_state_s::cur_ps, bot_state_s::currentEnemy, weaponData_s::energyPerShot, bot_state_s::frame_Enemy_Len, gclient_s::ps, STAT_WEAPONS, playerState_s::stats, bot_state_s::virtualWeapon, playerState_s::weapon, weaponData, WP_BLASTER, WP_BOWCASTER, WP_BRYAR_PISTOL, WP_DEMP2, WP_DISRUPTOR, WP_NUM_WEAPONS, WP_REPEATER, WP_ROCKET_LAUNCHER, WP_SABER, and WP_THERMAL.

Referenced by StandardBotAI().

05124 {
05125         int i;
05126         int bestweight = -1;
05127         int bestweapon = 0;
05128 
05129         i = 0;
05130 
05131         while (i < WP_NUM_WEAPONS)
05132         {
05133                 if (bs->cur_ps.ammo[weaponData[i].ammoIndex] >= weaponData[i].energyPerShot &&
05134                         bs->botWeaponWeights[i] > bestweight &&
05135                         (bs->cur_ps.stats[STAT_WEAPONS] & (1 << i)))
05136                 {
05137                         if (i == WP_THERMAL)
05138                         { //special case..
05139                                 if (bs->currentEnemy && bs->frame_Enemy_Len < 700)
05140                                 {
05141                                         bestweight = bs->botWeaponWeights[i];
05142                                         bestweapon = i;
05143                                 }
05144                         }
05145                         else
05146                         {
05147                                 bestweight = bs->botWeaponWeights[i];
05148                                 bestweapon = i;
05149                         }
05150                 }
05151 
05152                 i++;
05153         }
05154 
05155         if ( bs->currentEnemy && bs->frame_Enemy_Len < 300 &&
05156                 (bestweapon == WP_BRYAR_PISTOL || bestweapon == WP_BLASTER || bestweapon == WP_BOWCASTER) &&
05157                 (bs->cur_ps.stats[STAT_WEAPONS] & (1 << WP_SABER)) )
05158         {
05159                 bestweapon = WP_SABER;
05160                 bestweight = 1;
05161         }
05162 
05163         if ( bs->currentEnemy && bs->frame_Enemy_Len > 300 &&
05164                 bs->currentEnemy->client && bs->currentEnemy->client->ps.weapon != WP_SABER &&
05165                 (bestweapon == WP_SABER) )
05166         { //if the enemy is far away, and we have our saber selected, see if we have any good distance weapons instead
05167                 if (BotWeaponSelectable(bs, WP_DISRUPTOR))
05168                 {
05169                         bestweapon = WP_DISRUPTOR;
05170                         bestweight = 1;
05171                 }
05172                 else if (BotWeaponSelectable(bs, WP_ROCKET_LAUNCHER))
05173                 {
05174                         bestweapon = WP_ROCKET_LAUNCHER;
05175                         bestweight = 1;
05176                 }
05177                 else if (BotWeaponSelectable(bs, WP_BOWCASTER))
05178                 {
05179                         bestweapon = WP_BOWCASTER;
05180                         bestweight = 1;
05181                 }
05182                 else if (BotWeaponSelectable(bs, WP_BLASTER))
05183                 {
05184                         bestweapon = WP_BLASTER;
05185                         bestweight = 1;
05186                 }
05187                 else if (BotWeaponSelectable(bs, WP_REPEATER))
05188                 {
05189                         bestweapon = WP_REPEATER;
05190                         bestweight = 1;
05191                 }
05192                 else if (BotWeaponSelectable(bs, WP_DEMP2))
05193                 {
05194                         bestweapon = WP_DEMP2;
05195                         bestweight = 1;
05196                 }
05197         }
05198 
05199         //assert(bs->cur_ps.weapon > 0 && bestweapon > 0);
05200 
05201         if (bestweight != -1 && bs->cur_ps.weapon != bestweapon && bs->virtualWeapon != bestweapon)
05202         {
05203                 bs->virtualWeapon = bestweapon;
05204                 BotSelectWeapon(bs->client, bestweapon);
05205                 //bs->cur_ps.weapon = bestweapon;
05206                 //level.clients[bs->client].ps.weapon = bestweapon;
05207                 return 1;
05208         }
05209 
05210         //assert(bs->cur_ps.weapon > 0);
05211 
05212         return 0;
05213 }

int BotSelectMelee bot_state_t bs  ) 
 

Definition at line 5254 of file ai_main.c.

References bot_state_t, BotSelectWeapon(), bot_state_s::client, bot_state_s::cur_ps, bot_state_s::virtualWeapon, and playerState_s::weapon.

05255 {
05256         if (bs->cur_ps.weapon != 1 && bs->virtualWeapon != 1)
05257         {
05258                 bs->virtualWeapon = 1;
05259                 BotSelectWeapon(bs->client, 1);
05260                 //bs->cur_ps.weapon = 1;
05261                 //level.clients[bs->client].ps.weapon = 1;
05262                 return 1;
05263         }
05264 
05265         return 0;
05266 }

void BotSelectWeapon int  client,
int  weapon
 

Definition at line 157 of file ai_main.c.

References client, trap_EA_SelectWeapon(), and WP_NONE.

Referenced by BotSelectChoiceWeapon(), BotSelectIdealWeapon(), BotSelectMelee(), and BotTryAnotherWeapon().

00158 {
00159         if (weapon <= WP_NONE)
00160         {
00161 //              assert(0);
00162                 return;
00163         }
00164         trap_EA_SelectWeapon(client, weapon);
00165 }

void BotStraightTPOrderCheck gentity_t ent,
int  ordernum,
bot_state_t bs
 

Definition at line 128 of file ai_main.c.

References bot_state_t, gentity_t, bot_state_s::isSquadLeader, NULL, bot_state_s::squadLeader, bot_state_s::teamplayState, TEAMPLAYSTATE_ASSISTING, TEAMPLAYSTATE_FOLLOWING, and bot_state_s::wpDestSwitchTime.

Referenced by BotOrder().

00129 {
00130         switch (ordernum)
00131         {
00132         case 0:
00133                 if (bs->squadLeader == ent)
00134                 {
00135                         bs->teamplayState = 0;
00136                         bs->squadLeader = NULL;
00137                 }
00138                 break;
00139         case TEAMPLAYSTATE_FOLLOWING:
00140                 bs->teamplayState = ordernum;
00141                 bs->isSquadLeader = 0;
00142                 bs->squadLeader = ent;
00143                 bs->wpDestSwitchTime = 0;
00144                 break;
00145         case TEAMPLAYSTATE_ASSISTING:
00146                 bs->teamplayState = ordernum;
00147                 bs->isSquadLeader = 0;
00148                 bs->squadLeader = ent;
00149                 bs->wpDestSwitchTime = 0;
00150                 break;
00151         default:
00152                 bs->teamplayState = ordernum;
00153                 break;
00154         }
00155 }

int BotSurfaceNear bot_state_t bs  ) 
 

Definition at line 5833 of file ai_main.c.

References AngleVectors(), bot_state_t, bot_state_s::client, trace_t::fraction, MASK_SOLID, NULL, bot_state_s::origin, trap_Trace(), vec3_t, and bot_state_s::viewangles.

Referenced by StandardBotAI().

05834 {
05835         trace_t tr;
05836         vec3_t fwd;
05837 
05838         AngleVectors(bs->viewangles, fwd, NULL, NULL);
05839 
05840         fwd[0] = bs->origin[0]+(fwd[0]*64);
05841         fwd[1] = bs->origin[1]+(fwd[1]*64);
05842         fwd[2] = bs->origin[2]+(fwd[2]*64);
05843 
05844         trap_Trace(&tr, bs->origin, NULL, NULL, fwd, bs->client, MASK_SOLID);
05845 
05846         if (tr.fraction != 1)
05847         {
05848                 return 1;
05849         }
05850 
05851         return 0;
05852 }

int BotTrace_Duck bot_state_t bs,
vec3_t  traceto
 

Definition at line 1702 of file ai_main.c.

References AngleVectors(), bot_state_t, bot_state_s::client, trace_t::fraction, MASK_PLAYERSOLID, NULL, bot_state_s::origin, trap_Trace(), vec3_t, vectoangles(), VectorCopy, and VectorSubtract.

Referenced by StandardBotAI().

01703 {
01704         vec3_t mins, maxs, a, fwd, traceto_mod, tracefrom_mod;
01705         trace_t tr;
01706 
01707         VectorSubtract(traceto, bs->origin, a);
01708         vectoangles(a, a);
01709 
01710         AngleVectors(a, fwd, NULL, NULL);
01711 
01712         traceto_mod[0] = bs->origin[0] + fwd[0]*4;
01713         traceto_mod[1] = bs->origin[1] + fwd[1]*4;
01714         traceto_mod[2] = bs->origin[2] + fwd[2]*4;
01715 
01716         mins[0] = -15;
01717         mins[1] = -15;
01718         mins[2] = -23;
01719         maxs[0] = 15;
01720         maxs[1] = 15;
01721         maxs[2] = 8;
01722 
01723         trap_Trace(&tr, bs->origin, mins, maxs, traceto_mod, bs->client, MASK_PLAYERSOLID);
01724 
01725         if (tr.fraction != 1)
01726         {
01727                 return 0;
01728         }
01729 
01730         VectorCopy(bs->origin, tracefrom_mod);
01731 
01732         tracefrom_mod[2] += 31;//33;
01733         traceto_mod[2] += 31;//33;
01734 
01735         mins[0] = -15;
01736         mins[1] = -15;
01737         mins[2] = 0;
01738         maxs[0] = 15;
01739         maxs[1] = 15;
01740         maxs[2] = 32;
01741 
01742         trap_Trace(&tr, tracefrom_mod, mins, maxs, traceto_mod, bs->client, MASK_PLAYERSOLID);
01743 
01744         if (tr.fraction != 1)
01745         {
01746                 return 1;
01747         }
01748 
01749         return 0;
01750 }

int BotTrace_Jump bot_state_t bs,
vec3_t  traceto
 

Definition at line 1638 of file ai_main.c.

References AngleVectors(), bot_state_t, BotGetWeaponRange(), botstates, BWEAPONRANGE_MELEE, BWEAPONRANGE_SABER, bot_state_s::client, bot_state_s::currentEnemy, trace_t::entityNum, trace_t::fraction, bot_state_s::jumpTime, level, MASK_PLAYERSOLID, MAX_CLIENTS, NULL, entityState_s::number, bot_state_s::origin, gentity_s::s, level_locals_t::time, trap_Trace(), vec3_t, vectoangles(), VectorCopy, and VectorSubtract.

Referenced by StandardBotAI().

01639 {
01640         vec3_t mins, maxs, a, fwd, traceto_mod, tracefrom_mod;
01641         trace_t tr;
01642         int orTr;
01643 
01644         VectorSubtract(traceto, bs->origin, a);
01645         vectoangles(a, a);
01646 
01647         AngleVectors(a, fwd, NULL, NULL);
01648 
01649         traceto_mod[0] = bs->origin[0] + fwd[0]*4;
01650         traceto_mod[1] = bs->origin[1] + fwd[1]*4;
01651         traceto_mod[2] = bs->origin[2] + fwd[2]*4;
01652 
01653         mins[0] = -15;
01654         mins[1] = -15;
01655         mins[2] = -18;
01656         maxs[0] = 15;
01657         maxs[1] = 15;
01658         maxs[2] = 32;
01659 
01660         trap_Trace(&tr, bs->origin, mins, maxs, traceto_mod, bs->client, MASK_PLAYERSOLID);
01661 
01662         if (tr.fraction == 1)
01663         {
01664                 return 0;
01665         }
01666 
01667         orTr = tr.entityNum;
01668 
01669         VectorCopy(bs->origin, tracefrom_mod);
01670 
01671         tracefrom_mod[2] += 41;
01672         traceto_mod[2] += 41;
01673 
01674         mins[0] = -15;
01675         mins[1] = -15;
01676         mins[2] = 0;
01677         maxs[0] = 15;
01678         maxs[1] = 15;
01679         maxs[2] = 8;
01680 
01681         trap_Trace(&tr, tracefrom_mod, mins, maxs, traceto_mod, bs->client, MASK_PLAYERSOLID);
01682 
01683         if (tr.fraction == 1)
01684         {
01685                 if (orTr >= 0 && orTr < MAX_CLIENTS && botstates[orTr] && botstates[orTr]->jumpTime > level.time)
01686                 {
01687                         return 0; //so bots don't try to jump over each other at the same time
01688                 }
01689 
01690                 if (bs->currentEnemy && bs->currentEnemy->s.number == orTr && (BotGetWeaponRange(bs) == BWEAPONRANGE_SABER || BotGetWeaponRange(bs) == BWEAPONRANGE_MELEE))
01691                 {
01692                         return 0;
01693                 }
01694 
01695                 return 1;
01696         }
01697 
01698         return 0;
01699 }

int BotTrace_Strafe bot_state_t bs,
vec3_t  traceto
 

Definition at line 1556 of file ai_main.c.

References AngleDifference(), AngleVectors(), bot_state_t, bot_state_s::client, bot_state_s::cur_ps, DEFAULT_MAXS_2, ENTITYNUM_NONE, trace_t::fraction, playerState_s::groundEntityNum, MASK_PLAYERSOLID, bot_state_s::origin, playerMaxs, playerMins, STRAFEAROUND_LEFT, STRAFEAROUND_RIGHT, trap_Trace(), vec3_t, vectoangles(), VectorCopy, VectorNormalize(), VectorSubtract, bot_state_s::viewangles, and YAW.

Referenced by StandardBotAI().

01557 {
01558         vec3_t playerMins = {-15, -15, /*DEFAULT_MINS_2*/-8};
01559         vec3_t playerMaxs = {15, 15, DEFAULT_MAXS_2};
01560         vec3_t from, to;
01561         vec3_t dirAng, dirDif;
01562         vec3_t forward, right;
01563         trace_t tr;
01564 
01565         if (bs->cur_ps.groundEntityNum == ENTITYNUM_NONE)
01566         { //don't do this in the air, it can be.. dangerous.
01567                 return 0;
01568         }
01569 
01570         VectorSubtract(traceto, bs->origin, dirAng);
01571         VectorNormalize(dirAng);
01572         vectoangles(dirAng, dirAng);
01573 
01574         if (AngleDifference(bs->viewangles[YAW], dirAng[YAW]) > 60 ||
01575                 AngleDifference(bs->viewangles[YAW], dirAng[YAW]) < -60)
01576         { //If we aren't facing the direction we're going here, then we've got enough excuse to be too stupid to strafe around anyway
01577                 return 0;
01578         }
01579 
01580         VectorCopy(bs->origin, from);
01581         VectorCopy(traceto, to);
01582 
01583         VectorSubtract(to, from, dirDif);
01584         VectorNormalize(dirDif);
01585         vectoangles(dirDif, dirDif);
01586 
01587         AngleVectors(dirDif, forward, 0, 0);
01588 
01589         to[0] = from[0] + forward[0]*32;
01590         to[1] = from[1] + forward[1]*32;
01591         to[2] = from[2] + forward[2]*32;
01592 
01593         trap_Trace(&tr, from, playerMins, playerMaxs, to, bs->client, MASK_PLAYERSOLID);
01594 
01595         if (tr.fraction == 1)
01596         {
01597                 return 0;
01598         }
01599 
01600         AngleVectors(dirAng, 0, right, 0);
01601 
01602         from[0] += right[0]*32;
01603         from[1] += right[1]*32;
01604         from[2] += right[2]*16;
01605 
01606         to[0] += right[0]*32;
01607         to[1] += right[1]*32;
01608         to[2] += right[2]*32;
01609 
01610         trap_Trace(&tr, from, playerMins, playerMaxs, to, bs->client, MASK_PLAYERSOLID);
01611 
01612         if (tr.fraction == 1)
01613         {
01614                 return STRAFEAROUND_RIGHT;
01615         }
01616 
01617         from[0] -= right[0]*64;
01618         from[1] -= right[1]*64;
01619         from[2] -= right[2]*64;
01620 
01621         to[0] -= right[0]*64;
01622         to[1] -= right[1]*64;
01623         to[2] -= right[2]*64;
01624 
01625         trap_Trace(&tr, from, playerMins, playerMaxs, to, bs->client, MASK_PLAYERSOLID);
01626 
01627         if (tr.fraction == 1)
01628         {
01629                 return STRAFEAROUND_LEFT;
01630         }
01631 
01632         return 0;
01633 }

int BotTryAnotherWeapon bot_state_t bs  ) 
 

Definition at line 5072 of file ai_main.c.

References playerState_s::ammo, weaponData_s::ammoIndex, bot_state_t, BotSelectWeapon(), bot_state_s::client, bot_state_s::cur_ps, weaponData_s::energyPerShot, STAT_WEAPONS, playerState_s::stats, bot_state_s::virtualWeapon, playerState_s::weapon, weaponData, and WP_NUM_WEAPONS.

Referenced by StandardBotAI().

05073 { //out of ammo, resort to the first weapon we come across that has ammo
05074         int i;
05075 
05076         i = 1;
05077 
05078         while (i < WP_NUM_WEAPONS)
05079         {
05080                 if (bs->cur_ps.ammo[weaponData[i].ammoIndex] >= weaponData[i].energyPerShot &&
05081                         (bs->cur_ps.stats[STAT_WEAPONS] & (1 << i)))
05082                 {
05083                         bs->virtualWeapon = i;
05084                         BotSelectWeapon(bs->client, i);
05085                         //bs->cur_ps.weapon = i;
05086                         //level.clients[bs->client].ps.weapon = i;
05087                         return 1;
05088                 }
05089 
05090                 i++;
05091         }
05092 
05093         if (bs->cur_ps.weapon != 1 && bs->virtualWeapon != 1)
05094         { //should always have this.. shouldn't we?
05095                 bs->virtualWeapon = 1;
05096                 BotSelectWeapon(bs->client, 1);
05097                 //bs->cur_ps.weapon = 1;
05098                 //level.clients[bs->client].ps.weapon = 1;
05099                 return 1;
05100         }
05101 
05102         return 0;
05103 }

void BotUpdateInput bot_state_t bs,
int  time,
int  elapsed_time
 

Definition at line 633 of file ai_main.c.

References ACTION_ATTACK, ACTION_RESPAWN, bot_input_s::actionflags, AngleMod(), bot_input_t, bot_state_t, BotChangeViewAngles(), BotInputToUserCommand(), BUTTON_ATTACK, usercmd_s::buttons, bot_state_s::client, bot_state_s::cur_ps, playerState_s::delta_angles, bot_state_s::lastucmd, bot_state_s::noUseTime, SHORT2ANGLE, trap_EA_GetInput(), and bot_state_s::viewangles.

Referenced by BotAIStartFrame().

00633                                                                  {
00634         bot_input_t bi;
00635         int j;
00636 
00637         //add the delta angles to the bot's current view angles
00638         for (j = 0; j < 3; j++) {
00639                 bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00640         }
00641         //change the bot view angles
00642         BotChangeViewAngles(bs, (float) elapsed_time / 1000);
00643         //retrieve the bot input
00644         trap_EA_GetInput(bs->client, (float) time / 1000, &bi);
00645         //respawn hack
00646         if (bi.actionflags & ACTION_RESPAWN) {
00647                 if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK);
00648         }
00649         //convert the bot input to a usercmd
00650         BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time, bs->noUseTime);
00651         //subtract the delta angles
00652         for (j = 0; j < 3; j++) {
00653                 bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00654         }
00655 }

int BotUseInventoryItem bot_state_t bs  ) 
 

Definition at line 5780 of file ai_main.c.

References BG_GetItemIndexByTag(), bot_state_t, bot_state_s::client, level_locals_t::clients, bot_state_s::cur_ps, bot_state_s::currentEnemy, bot_state_s::frame_Enemy_Vis, g_entities, HI_MEDPAC, HI_MEDPAC_BIG, HI_SEEKER, HI_SENTRY_GUN, HI_SHIELD, IT_HOLDABLE, level, bot_state_s::runningToEscapeThreat, STAT_HOLDABLE_ITEM, STAT_HOLDABLE_ITEMS, and playerState_s::stats.

Referenced by StandardBotAI().

05781 {
05782         if (bs->cur_ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC))
05783         {
05784                 if (g_entities[bs->client].health <= 75)
05785                 {
05786                         bs->cur_ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_MEDPAC, IT_HOLDABLE);
05787                         goto wantuseitem;
05788                 }
05789         }
05790         if (bs->cur_ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC_BIG))
05791         {
05792                 if (g_entities[bs->client].health <= 50)
05793                 {
05794                         bs->cur_ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_MEDPAC_BIG, IT_HOLDABLE);
05795                         goto wantuseitem;
05796                 }
05797         }
05798         if (bs->cur_ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SEEKER))
05799         {
05800                 if (bs->currentEnemy && bs->frame_Enemy_Vis)
05801                 {
05802                         bs->cur_ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_SEEKER, IT_HOLDABLE);
05803                         goto wantuseitem;
05804                 }
05805         }
05806         if (bs->cur_ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SENTRY_GUN))
05807         {
05808                 if (bs->currentEnemy && bs->frame_Enemy_Vis)
05809                 {
05810                         bs->cur_ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_SENTRY_GUN, IT_HOLDABLE);
05811                         goto wantuseitem;
05812                 }
05813         }
05814         if (bs->cur_ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SHIELD))
05815         {
05816                 if (bs->currentEnemy && bs->frame_Enemy_Vis && bs->runningToEscapeThreat)
05817                 { //this will (hopefully) result in the bot placing the shield down while facing
05818                   //the enemy and running away
05819                         bs->cur_ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_SHIELD, IT_HOLDABLE);
05820                         goto wantuseitem;
05821                 }
05822         }
05823 
05824         return 0;
05825 
05826 wantuseitem:
05827         level.clients[bs->client].ps.stats[STAT_HOLDABLE_ITEM] = bs->cur_ps.stats[STAT_HOLDABLE_ITEM];
05828 
05829         return 1;
05830 }

int BotWeaponBlockable int  weapon  ) 
 

Definition at line 5855 of file ai_main.c.

References WP_DEMP2, WP_DET_PACK, WP_DISRUPTOR, WP_MELEE, WP_ROCKET_LAUNCHER, WP_STUN_BATON, WP_THERMAL, and WP_TRIP_MINE.

Referenced by StandardBotAI().

05856 {
05857         switch (weapon)
05858         {
05859         case WP_STUN_BATON:
05860         case WP_MELEE:
05861                 return 0;
05862         case WP_DISRUPTOR:
05863                 return 0;
05864         case WP_DEMP2:
05865                 return 0;
05866         case WP_ROCKET_LAUNCHER:
05867                 return 0;
05868         case WP_THERMAL:
05869                 return 0;
05870         case WP_TRIP_MINE:
05871                 return 0;
05872         case WP_DET_PACK:
05873                 return 0;
05874         default:
05875                 return 1;
05876         }
05877 }

float BotWeaponCanLead bot_state_t bs  ) 
 

Definition at line 4570 of file ai_main.c.

References bot_state_t, bot_state_s::cur_ps, playerState_s::weapon, WP_BLASTER, WP_BOWCASTER, WP_BRYAR_PISTOL, WP_DEMP2, WP_REPEATER, WP_ROCKET_LAUNCHER, and WP_THERMAL.

Referenced by StandardBotAI().

04571 {
04572         int weap = bs->cur_ps.weapon;
04573 
04574         if (weap == WP_BRYAR_PISTOL)
04575         {
04576                 return 0.5;
04577         }
04578         if (weap == WP_BLASTER)
04579         {
04580                 return 0.35;
04581         }
04582         if (weap == WP_BOWCASTER)
04583         {
04584                 return 0.5;
04585         }
04586         if (weap == WP_REPEATER)
04587         {
04588                 return 0.45;
04589         }
04590         if (weap == WP_THERMAL)
04591         {
04592                 return 0.5;
04593         }
04594         if (weap == WP_DEMP2)
04595         {
04596                 return 0.35;
04597         }
04598         if (weap == WP_ROCKET_LAUNCHER)
04599         {
04600                 return 0.7;
04601         }
04602         
04603         return 0;
04604 }

qboolean BotWeaponSelectable bot_state_t bs,
int  weapon
 

Definition at line 5106 of file ai_main.c.

References playerState_s::ammo, weaponData_s::ammoIndex, bot_state_t, bot_state_s::cur_ps, weaponData_s::energyPerShot, qboolean, qfalse, qtrue, STAT_WEAPONS, playerState_s::stats, weaponData, and WP_NONE.

Referenced by BotSelectIdealWeapon().

05107 {
05108         if (weapon == WP_NONE)
05109         {
05110                 return qfalse;
05111         }
05112 
05113         if (bs->cur_ps.ammo[weaponData[weapon].ammoIndex] >= weaponData[weapon].energyPerShot &&
05114                 (bs->cur_ps.stats[STAT_WEAPONS] & (1 << weapon)))
05115         {
05116                 return qtrue;
05117         }
05118         
05119         return qfalse;
05120 }

gentity_t* CheckForFriendInLOF bot_state_t bs  ) 
 

Definition at line 5538 of file ai_main.c.

References AngleVectors(), bot_state_t, botstates, gentity_s::client, bot_state_s::client, trace_t::entityNum, bot_state_s::eye, trace_t::fraction, g_entities, gentity_t, GetLoveLevel(), IsTeamplay(), MASK_PLAYERSOLID, MAX_CLIENTS, NULL, entityState_s::number, OnSameTeam(), gentity_s::s, trap_Trace(), vec3_t, VectorCopy, and bot_state_s::viewangles.

Referenced by StandardBotAI().

05539 {
05540         vec3_t fwd;
05541         vec3_t trfrom, trto;
05542         vec3_t mins, maxs;
05543         gentity_t *trent;
05544         trace_t tr;
05545 
05546         mins[0] = -3;
05547         mins[1] = -3;
05548         mins[2] = -3;
05549 
05550         maxs[0] = 3;
05551         maxs[1] = 3;
05552         maxs[2] = 3;
05553 
05554         AngleVectors(bs->viewangles, fwd, NULL, NULL);
05555 
05556         VectorCopy(bs->eye, trfrom);
05557 
05558         trto[0] = trfrom[0] + fwd[0]*2048;
05559         trto[1] = trfrom[1] + fwd[1]*2048;
05560         trto[2] = trfrom[2] + fwd[2]*2048;
05561 
05562         trap_Trace(&tr, trfrom, mins, maxs, trto, bs->client, MASK_PLAYERSOLID);
05563 
05564         if (tr.fraction != 1 && tr.entityNum <= MAX_CLIENTS)
05565         {
05566                 trent = &g_entities[tr.entityNum];
05567 
05568                 if (trent && trent->client)
05569                 {
05570                         if (IsTeamplay() && OnSameTeam(&g_entities[bs->client], trent))
05571                         {
05572                                 return trent;
05573                         }
05574 
05575                         if (botstates[trent->s.number] && GetLoveLevel(bs, botstates[trent->s.number]) > 1)
05576                         {
05577                                 return trent;
05578                         }
05579                 }
05580         }
05581 
05582         return NULL;
05583 }

int CheckForFunc vec3_t  org,
int  ignore
 

Definition at line 1053 of file ai_main.c.

References gentity_s::classname, trace_t::entityNum, trace_t::fraction, g_entities, gentity_t, MASK_SOLID, NULL, strstr(), trap_Trace(), vec3_t, and VectorCopy.

Referenced by StandardBotAI(), and WaitingForNow().

01054 {
01055         gentity_t *fent;
01056         vec3_t under;
01057         trace_t tr;
01058 
01059         VectorCopy(org, under);
01060 
01061         under[2] -= 64;
01062 
01063         trap_Trace(&tr, org, NULL, NULL, under, ignore, MASK_SOLID);
01064 
01065         if (tr.fraction == 1)
01066         {
01067                 return 0;
01068         }
01069 
01070         fent = &g_entities[tr.entityNum];
01071 
01072         if (!fent)
01073         {
01074                 return 0;
01075         }
01076 
01077         if (strstr(fent->classname, "func_"))
01078         {
01079                 return 1; //there's a func brush here
01080         }
01081 
01082         return 0;
01083 }

void CheckForShorterRoutes bot_state_t bs,
int  newwpindex
 

Definition at line 1263 of file ai_main.c.

References bot_state_s::beStill, bot_state_t, bot_state_s::cur_ps, playerState_s::fd, bot_state_s::forceJumpChargeTime, bot_state_s::forceJumping, wpneighbor_s::forceJumpTo, forcedata_s::forcePowerLevel, FP_LEVITATION, gWPArray, wpobject_s::index, bot_state_s::jDelay, bot_state_s::jumpTime, level, wpobject_s::neighbornum, wpobject_s::neighbors, wpneighbor_s::num, level_locals_t::time, TotalTrailDistance(), bot_state_s::wpCurrent, bot_state_s::wpDestination, bot_state_s::wpDirection, and bot_state_s::wpSwitchTime.

Referenced by WPTouchRoutine().

01264 {
01265         float bestlen;
01266         float checklen;
01267         int bestindex;
01268         int i;
01269         int fj;
01270 
01271         i = 0;
01272         fj = 0;
01273 
01274         if (!bs->wpDestination)
01275         {
01276                 return;
01277         }
01278 
01279         //set our traversal direction based on the index of the point
01280         if (newwpindex < bs->wpDestination->index)
01281         {
01282                 bs->wpDirection = 0;
01283         }
01284         else if (newwpindex > bs->wpDestination->index)
01285         {
01286                 bs->wpDirection = 1;
01287         }
01288 
01289         //can't switch again yet
01290         if (bs->wpSwitchTime > level.time)
01291         {
01292                 return;
01293         }
01294 
01295         //no neighboring points to check off of
01296         if (!gWPArray[newwpindex]->neighbornum)
01297         {
01298                 return;
01299         }
01300 
01301         //get the trail distance for our wp
01302         bestindex = newwpindex;
01303         bestlen = TotalTrailDistance(newwpindex, bs->wpDestination->index, bs);
01304 
01305         while (i < gWPArray[newwpindex]->neighbornum)
01306         { //now go through the neighbors and check the distance to the desired point from each neighbor
01307                 checklen = TotalTrailDistance(gWPArray[newwpindex]->neighbors[i].num, bs->wpDestination->index, bs);
01308 
01309                 if (checklen < bestlen-64 || bestlen == -1)
01310                 { //this path covers less distance, let's take it instead
01311                         if (bs->cur_ps.fd.forcePowerLevel[FP_LEVITATION] >= gWPArray[newwpindex]->neighbors[i].forceJumpTo)
01312                         {
01313                                 bestlen = checklen;
01314                                 bestindex = gWPArray[newwpindex]->neighbors[i].num;
01315 
01316                                 if (gWPArray[newwpindex]->neighbors[i].forceJumpTo)
01317                                 {
01318                                         fj = gWPArray[newwpindex]->neighbors[i].forceJumpTo;
01319                                 }
01320                                 else
01321                                 {
01322                                         fj = 0;
01323                                 }
01324                         }
01325                 }
01326 
01327                 i++;
01328         }
01329 
01330         if (bestindex != newwpindex && bestindex != -1)
01331         { //we found a path we want to switch to, let's do it
01332                 bs->wpCurrent = gWPArray[bestindex];
01333                 bs->wpSwitchTime = level.time + 3000;
01334 
01335                 if (fj)
01336                 { //do we have to force jump to get to this neighbor?
01337 #ifndef FORCEJUMP_INSTANTMETHOD
01338                         bs->forceJumpChargeTime = level.time + 1000;
01339                         bs->beStill = level.time + 1000;
01340                         bs->forceJumping = bs->forceJumpChargeTime;
01341 #else
01342                         bs->beStill = level.time + 500;
01343                         bs->jumpTime = level.time + fj*1200;
01344                         bs->jDelay = level.time + 200;
01345                         bs->forceJumping = bs->jumpTime;
01346 #endif
01347                 }
01348         }
01349 }

void Cmd_EngageDuel_f gentity_t ent  ) 
 

Definition at line 2766 of file g_cmds.c.

02767 {
02768         trace_t tr;
02769         vec3_t forward, fwdOrg;
02770 
02771         if (!g_privateDuel.integer)
02772         {
02773                 return;
02774         }
02775 
02776         if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
02777         { //rather pointless in this mode..
02778                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NODUEL_GAMETYPE")) );
02779                 return;
02780         }
02781 
02782         //if (g_gametype.integer >= GT_TEAM && g_gametype.integer != GT_SIEGE)
02783         if (g_gametype.integer >= GT_TEAM)
02784         { //no private dueling in team modes
02785                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "NODUEL_GAMETYPE")) );
02786                 return;
02787         }
02788 
02789         if (ent->client->ps.duelTime >= level.time)
02790         {
02791                 return;
02792         }
02793 
02794         if (ent->client->ps.weapon != WP_SABER)
02795         {
02796                 return;
02797         }
02798 
02799         /*
02800         if (!ent->client->ps.saberHolstered)
02801         { //must have saber holstered at the start of the duel
02802                 return;
02803         }
02804         */
02805         //NOTE: No longer doing this..
02806 
02807         if (ent->client->ps.saberInFlight)
02808         {
02809                 return;
02810         }
02811 
02812         if (ent->client->ps.duelInProgress)
02813         {
02814                 return;
02815         }
02816 
02817         //New: Don't let a player duel if he just did and hasn't waited 10 seconds yet (note: If someone challenges him, his duel timer will reset so he can accept)
02818         if (ent->client->ps.fd.privateDuelTime > level.time)
02819         {
02820                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "CANTDUEL_JUSTDID")) );
02821                 return;
02822         }
02823 
02824         if (G_OtherPlayersDueling())
02825         {
02826                 trap_SendServerCommand( ent-g_entities, va("print \"%s\n\"", G_GetStringEdString("MP_SVGAME", "CANTDUEL_BUSY")) );
02827                 return;
02828         }
02829 
02830         AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );
02831 
02832         fwdOrg[0] = ent->client->ps.origin[0] + forward[0]*256;
02833         fwdOrg[1] = ent->client->ps.origin[1] + forward[1]*256;
02834         fwdOrg[2] = (ent->client->ps.origin[2]+ent->client->ps.viewheight) + forward[2]*256;
02835 
02836         trap_Trace(&tr, ent->client->ps.origin, NULL, NULL, fwdOrg, ent->s.number, MASK_PLAYERSOLID);
02837 
02838         if (tr.fraction != 1 && tr.entityNum < MAX_CLIENTS)
02839         {
02840                 gentity_t *challenged = &g_entities[tr.entityNum];
02841 
02842                 if (!challenged || !challenged->client || !challenged->inuse ||
02843                         challenged->health < 1 || challenged->client->ps.stats[STAT_HEALTH] < 1 ||
02844                         challenged->client->ps.weapon != WP_SABER || challenged->client->ps.duelInProgress ||
02845                         challenged->client->ps.saberInFlight)
02846                 {
02847                         return;
02848                 }
02849 
02850                 if (g_gametype.integer >= GT_TEAM && OnSameTeam(ent, challenged))
02851                 {
02852                         return;
02853                 }
02854 
02855                 if (challenged->client->ps.duelIndex == ent->s.number && challenged->client->ps.duelTime >= level.time)
02856                 {
02857                         trap_SendServerCommand( /*challenged-g_entities*/-1, va("print \"%s %s %s!\n\"", challenged->client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLDUELACCEPT"), ent->client->pers.netname) );
02858 
02859                         ent->client->ps.duelInProgress = qtrue;
02860                         challenged->client->ps.duelInProgress = qtrue;
02861 
02862                         ent->client->ps.duelTime = level.time + 2000;
02863                         challenged->client->ps.duelTime = level.time + 2000;
02864 
02865                         G_AddEvent(ent, EV_PRIVATE_DUEL, 1);
02866                         G_AddEvent(challenged, EV_PRIVATE_DUEL, 1);
02867 
02868                         //Holster their sabers now, until the duel starts (then they'll get auto-turned on to look cool)
02869 
02870                         if (!ent->client->ps.saberHolstered)
02871                         {
02872                                 if (ent->client->saber[0].soundOff)
02873                                 {
02874                                         G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOff);
02875                                 }
02876                                 if (ent->client->saber[1].soundOff &&
02877                                         ent->client->saber[1].model[0])
02878                                 {
02879                                         G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOff);
02880                                 }
02881                                 ent->client->ps.weaponTime = 400;
02882                                 ent->client->ps.saberHolstered = 2;
02883                         }
02884                         if (!challenged->client->ps.saberHolstered)
02885                         {
02886                                 if (challenged->client->saber[0].soundOff)
02887                                 {
02888                                         G_Sound(challenged, CHAN_AUTO, challenged->client->saber[0].soundOff);
02889                                 }
02890                                 if (challenged->client->saber[1].soundOff &&
02891                                         challenged->client->saber[1].model[0])
02892                                 {
02893                                         G_Sound(challenged, CHAN_AUTO, challenged->client->saber[1].soundOff);
02894                                 }
02895                                 challenged->client->ps.weaponTime = 400;
02896                                 challenged->client->ps.saberHolstered = 2;
02897                         }
02898                 }
02899                 else
02900                 {
02901                         //Print the message that a player has been challenged in private, only announce the actual duel initiation in private
02902                         trap_SendServerCommand( challenged-g_entities, va("cp \"%s %s\n\"", ent->client->pers.netname, G_GetStringEdString("MP_SVGAME", "PLDUELCHALLENGE")) );
02903                         trap_SendServerCommand( ent-g_entities, va("cp \"%s %s\n\"", G_GetStringEdString("MP_SVGAME", "PLDUELCHALLENGED"), challenged->client->pers.netname) );
02904                 }
02905 
02906                 challenged->client->ps.fd.privateDuelTime = 0; //reset the timer in case this player just got out of a duel. He should still be able to accept the challenge.
02907 
02908                 ent->client->ps.forceHandExtend = HANDEXTEND_DUELCHALLENGE;
02909                 ent->client->ps.forceHandExtendTime = level.time + 1000;
02910 
02911                 ent->client->ps.duelIndex = challenged->s.number;
02912                 ent->client->ps.duelTime = level.time + 5000;
02913         }
02914 }

void Cmd_ToggleSaber_f gentity_t ent  ) 
 

Definition at line 2467 of file g_cmds.c.

02468 {
02469         if (ent->client->ps.fd.forceGripCripple)
02470         { //if they are being gripped, don't let them unholster their saber
02471                 if (ent->client->ps.saberHolstered)
02472                 {
02473                         return;
02474                 }
02475         }
02476 
02477         if (ent->client->ps.saberInFlight)
02478         {
02479                 if (ent->client->ps.saberEntityNum)
02480                 { //turn it off in midair
02481                         saberKnockDown(&g_entities[ent->client->ps.saberEntityNum], ent, ent);
02482                 }
02483                 return;
02484         }
02485 
02486         if (ent->client->ps.forceHandExtend != HANDEXTEND_NONE)
02487         {
02488                 return;
02489         }
02490 
02491         if (ent->client->ps.weapon != WP_SABER)
02492         {
02493                 return;
02494         }
02495 
02496 //      if (ent->client->ps.duelInProgress && !ent->client->ps.saberHolstered)
02497 //      {
02498 //              return;
02499 //      }
02500 
02501         if (ent->client->ps.duelTime >= level.time)
02502         {
02503                 return;
02504         }
02505 
02506         if (ent->client->ps.saberLockTime >= level.time)
02507         {
02508                 return;
02509         }
02510 
02511         if (ent->client && ent->client->ps.weaponTime < 1)
02512         {
02513                 if (ent->client->ps.saberHolstered == 2)
02514                 {
02515                         ent->client->ps.saberHolstered = 0;
02516 
02517                         if (ent->client->saber[0].soundOn)
02518                         {
02519                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOn);
02520                         }
02521                         if (ent->client->saber[1].soundOn)
02522                         {
02523                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOn);
02524                         }
02525                 }
02526                 else
02527                 {
02528                         ent->client->ps.saberHolstered = 2;
02529                         if (ent->client->saber[0].soundOff)
02530                         {
02531                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[0].soundOff);
02532                         }
02533                         if (ent->client->saber[1].soundOff &&
02534                                 ent->client->saber[1].model[0])
02535                         {
02536                                 G_Sound(ent, CHAN_AUTO, ent->client->saber[1].soundOff);
02537                         }
02538                         //prevent anything from being done for 400ms after holster
02539                         ent->client->ps.weaponTime = 400;
02540                 }
02541         }
02542 }

int CombatBotAI bot_state_t bs,
float  thinktime
 

Definition at line 4885 of file ai_main.c.

References bot_state_s::altChargeTime, bot_state_t, BotGetWeaponRange(), BWEAPONRANGE_MELEE, BWEAPONRANGE_SABER, gentity_s::client, bot_state_s::cur_ps, bot_state_s::currentEnemy, bot_state_s::doAltAttack, bot_state_s::doAttack, bot_state_s::eye, bot_state_s::frame_Enemy_Len, InFieldOfVision(), level, MELEE_ATTACK_RANGE, entityState_s::origin, playerState_s::origin, gclient_s::ps, Q_irand(), gentity_s::s, SABER_ATTACK_RANGE, ShouldSecondaryFire(), level_locals_t::time, vec3_t, vectoangles(), VectorCopy, VectorSubtract, bot_state_s::viewangles, playerState_s::weapon, WEAPON_CHARGING, WEAPON_CHARGING_ALT, playerState_s::weaponChargeTime, playerState_s::weaponstate, WP_ROCKET_LAUNCHER, and WP_THERMAL.

Referenced by StandardBotAI().

04886 {
04887         vec3_t eorg, a;
04888         int secFire;
04889         float fovcheck;
04890 
04891         if (!bs->currentEnemy)
04892         {
04893                 return 0;
04894         }
04895 
04896         if (bs->currentEnemy->client)
04897         {
04898                 VectorCopy(bs->currentEnemy->client->ps.origin, eorg);
04899         }
04900         else
04901         {
04902                 VectorCopy(bs->currentEnemy->s.origin, eorg);
04903         }
04904 
04905         VectorSubtract(eorg, bs->eye, a);
04906         vectoangles(a, a);
04907 
04908         if (BotGetWeaponRange(bs) == BWEAPONRANGE_SABER)
04909         {
04910                 if (bs->frame_Enemy_Len <= SABER_ATTACK_RANGE)
04911                 {
04912                         bs->doAttack = 1;
04913                 }
04914         }
04915         else if (BotGetWeaponRange(bs) == BWEAPONRANGE_MELEE)
04916         {
04917                 if (bs->frame_Enemy_Len <= MELEE_ATTACK_RANGE)
04918                 {
04919                         bs->doAttack = 1;
04920                 }
04921         }
04922         else
04923         {
04924                 if (bs->cur_ps.weapon == WP_THERMAL || bs->cur_ps.weapon == WP_ROCKET_LAUNCHER)
04925                 { //be careful with the hurty weapons
04926                         fovcheck = 40;
04927 
04928                         if (bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT &&
04929                                 bs->cur_ps.weapon == WP_ROCKET_LAUNCHER)
04930                         { //if we're charging the weapon up then we can hold fire down within a normal fov
04931                                 fovcheck = 60;
04932                         }
04933                 }
04934                 else
04935                 {
04936                         fovcheck = 60;
04937                 }
04938 
04939                 if (bs->cur_ps.weaponstate == WEAPON_CHARGING ||
04940                         bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT)
04941                 {
04942                         fovcheck = 160;
04943                 }
04944 
04945                 if (bs->frame_Enemy_Len < 128)
04946                 {
04947                         fovcheck *= 2;
04948                 }
04949 
04950                 if (InFieldOfVision(bs->viewangles, fovcheck, a))
04951                 {
04952                         if (bs->cur_ps.weapon == WP_THERMAL)
04953                         {
04954                                 if (((level.time - bs->cur_ps.weaponChargeTime) < (bs->frame_Enemy_Len*2) &&
04955                                         (level.time - bs->cur_ps.weaponChargeTime) < 4000 &&
04956                                         bs->frame_Enemy_Len > 64) ||
04957                                         (bs->cur_ps.weaponstate != WEAPON_CHARGING &&
04958                                         bs->cur_ps.weaponstate != WEAPON_CHARGING_ALT))
04959                                 {
04960                                         if (bs->cur_ps.weaponstate != WEAPON_CHARGING && bs->cur_ps.weaponstate != WEAPON_CHARGING_ALT)
04961                                         {
04962                                                 if (bs->frame_Enemy_Len > 512 && bs->frame_Enemy_Len < 800)
04963                                                 {
04964                                                         bs->doAltAttack = 1;
04965                                                         //bs->doAttack = 1;
04966                                                 }
04967                                                 else
04968                                                 {
04969                                                         bs->doAttack = 1;
04970                                                         //bs->doAltAttack = 1;
04971                                                 }
04972                                         }
04973 
04974                                         if (bs->cur_ps.weaponstate == WEAPON_CHARGING)
04975                                         {
04976                                                 bs->doAttack = 1;
04977                                         }
04978                                         else if (bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT)
04979                                         {
04980                                                 bs->doAltAttack = 1;
04981                                         }
04982                                 }
04983                         }
04984                         else
04985                         {
04986                                 secFire = ShouldSecondaryFire(bs);
04987 
04988                                 if (bs->cur_ps.weaponstate != WEAPON_CHARGING_ALT &&
04989                                         bs->cur_ps.weaponstate != WEAPON_CHARGING)
04990                                 {
04991                                         bs->altChargeTime = Q_irand(500, 1000);
04992                                 }
04993 
04994                                 if (secFire == 1)
04995                                 {
04996                                         bs->doAltAttack = 1;
04997                                 }
04998                                 else if (!secFire)
04999                                 {
05000                                         if (bs->cur_ps.weapon != WP_THERMAL)
05001                                         {
05002                                                 if (bs->cur_ps.weaponstate != WEAPON_CHARGING ||
05003                                                         bs->altChargeTime > (level.time - bs->cur_ps.weaponChargeTime))
05004                                                 {
05005                                                         bs->doAttack = 1;
05006                                                 }
05007                                         }
05008                                         else
05009                                         {
05010                                                 bs->doAttack = 1;
05011                                         }
05012                                 }
05013 
05014                                 if (secFire == 2)
05015                                 { //released a charge
05016                                         return 1;
05017                                 }
05018                         }
05019                 }
05020         }
05021 
05022         return 0;
05023 }

void CommanderBotAI bot_state_t bs  ) 
 

Definition at line 4252 of file ai_main.c.

References bot_state_t, CommanderBotCTFAI(), CommanderBotSiegeAI(), CommanderBotTeamplayAI(), g_gametype, GT_CTF, GT_CTY, GT_SIEGE, GT_TEAM, and vmCvar_t::integer.

Referenced by StandardBotAI().

04253 {
04254         if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY)
04255         {
04256                 CommanderBotCTFAI(bs);
04257         }
04258         else if (g_gametype.integer == GT_SIEGE)
04259         {
04260                 CommanderBotSiegeAI(bs);
04261         }
04262         else if (g_gametype.integer == GT_TEAM)
04263         {
04264                 CommanderBotTeamplayAI(bs);
04265         }
04266 }

void CommanderBotCTFAI bot_state_t bs  ) 
 

Definition at line 3911 of file ai_main.c.

References bot_state_t, botstates, gentity_s::client, bot_state_s::client, level_locals_t::clients, bot_state_s::ctfState, CTFSTATE_ATTACKER, CTFSTATE_DEFENDER, CTFSTATE_GETFLAGHOME, CTFSTATE_GUARDCARRIER, CTFSTATE_RETRIEVAL, g_entities, gentity_t, level, MAX_CLIENTS, entityState_s::number, OnSameTeam(), playerState_s::powerups, gclient_s::ps, PW_BLUEFLAG, PW_REDFLAG, gentity_s::s, bot_state_s::squadLeader, and TEAM_RED.

Referenced by CommanderBotAI().

03912 {
03913         int i = 0;
03914         gentity_t *ent;
03915         int squadmates = 0;
03916         gentity_t *squad[MAX_CLIENTS];
03917         int defendAttackPriority = 0; //0 == attack, 1 == defend
03918         int guardDefendPriority = 0; //0 == defend, 1 == guard
03919         int attackRetrievePriority = 0; //0 == retrieve, 1 == attack
03920         int myFlag = 0;
03921         int enemyFlag = 0;
03922         int enemyHasOurFlag = 0;
03923         int weHaveEnemyFlag = 0;
03924         int numOnMyTeam = 0;
03925         int numOnEnemyTeam = 0;
03926         int numAttackers = 0;
03927         int numDefenders = 0;
03928 
03929         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
03930         {
03931                 myFlag = PW_REDFLAG;
03932         }
03933         else
03934         {
03935                 myFlag = PW_BLUEFLAG;
03936         }
03937 
03938         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
03939         {
03940                 enemyFlag = PW_BLUEFLAG;
03941         }
03942         else
03943         {
03944                 enemyFlag = PW_REDFLAG;
03945         }
03946 
03947         while (i < MAX_CLIENTS)
03948         {
03949                 ent = &g_entities[i];
03950 
03951                 if (ent && ent->client)
03952                 {
03953                         if (ent->client->ps.powerups[enemyFlag] && OnSameTeam(&g_entities[bs->client], ent))
03954                         {
03955                                 weHaveEnemyFlag = 1;
03956                         }
03957                         else if (ent->client->ps.powerups[myFlag] && !OnSameTeam(&g_entities[bs->client], ent))
03958                         {
03959                                 enemyHasOurFlag = 1;
03960                         }
03961 
03962                         if (OnSameTeam(&g_entities[bs->client], ent))
03963                         {
03964                                 numOnMyTeam++;
03965                         }
03966                         else
03967                         {
03968                                 numOnEnemyTeam++;
03969                         }
03970 
03971                         if (botstates[ent->s.number])
03972                         {
03973                                 if (botstates[ent->s.number]->ctfState == CTFSTATE_ATTACKER ||
03974                                         botstates[ent->s.number]->ctfState == CTFSTATE_RETRIEVAL)
03975                                 {
03976                                         numAttackers++;
03977                                 }
03978                                 else
03979                                 {
03980                                         numDefenders++;
03981                                 }
03982                         }
03983                         else
03984                         { //assume real players to be attackers in our logic
03985                                 numAttackers++;
03986                         }
03987                 }
03988                 i++;
03989         }
03990 
03991         i = 0;
03992 
03993         while (i < MAX_CLIENTS)
03994         {
03995                 ent = &g_entities[i];
03996 
03997                 if (ent && ent->client && botstates[i] && botstates[i]->squadLeader && botstates[i]->squadLeader->s.number == bs->client && i != bs->client)
03998                 {
03999                         squad[squadmates] = ent;
04000                         squadmates++;
04001                 }
04002 
04003                 i++;
04004         }
04005 
04006         squad[squadmates] = &g_entities[bs->client];
04007         squadmates++;
04008 
04009         i = 0;
04010 
04011         if (enemyHasOurFlag && !weHaveEnemyFlag)
04012         { //start off with an attacker instead of a retriever if we don't have the enemy flag yet so that they can't capture it first.
04013           //after that we focus on getting our flag back.
04014                 attackRetrievePriority = 1;
04015         }
04016 
04017         while (i < squadmates)
04018         {
04019                 if (squad[i] && squad[i]->client && botstates[squad[i]->s.number])
04020                 {
04021                         if (botstates[squad[i]->s.number]->ctfState != CTFSTATE_GETFLAGHOME)
04022                         { //never tell a bot to stop trying to bring the flag to the base
04023                                 if (defendAttackPriority)
04024                                 {
04025                                         if (weHaveEnemyFlag)
04026                                         {
04027                                                 if (guardDefendPriority)
04028                                                 {
04029                                                         botstates[squad[i]->s.number]->ctfState = CTFSTATE_GUARDCARRIER;
04030                                                         guardDefendPriority = 0;
04031                                                 }
04032                                                 else
04033                                                 {
04034                                                         botstates[squad[i]->s.number]->ctfState = CTFSTATE_DEFENDER;
04035                                                         guardDefendPriority = 1;
04036                                                 }
04037                                         }
04038                                         else
04039                                         {
04040                                                 botstates[squad[i]->s.number]->ctfState = CTFSTATE_DEFENDER;
04041                                         }
04042                                         defendAttackPriority = 0;
04043                                 }
04044                                 else
04045                                 {
04046                                         if (enemyHasOurFlag)
04047                                         {
04048                                                 if (attackRetrievePriority)
04049                                                 {
04050                                                         botstates[squad[i]->s.number]->ctfState = CTFSTATE_ATTACKER;
04051                                                         attackRetrievePriority = 0;
04052                                                 }
04053                                                 else
04054                                                 {
04055                                                         botstates[squad[i]->s.number]->ctfState = CTFSTATE_RETRIEVAL;
04056                                                         attackRetrievePriority = 1;
04057                                                 }
04058                                         }
04059                                         else
04060                                         {
04061                                                 botstates[squad[i]->s.number]->ctfState = CTFSTATE_ATTACKER;
04062                                         }
04063                                         defendAttackPriority = 1;
04064                                 }
04065                         }
04066                         else if ((numOnMyTeam < 2 || !numAttackers) && enemyHasOurFlag)
04067                         { //I'm the only one on my team who will attack and the enemy has my flag, I have to go after him
04068                                 botstates[squad[i]->s.number]->ctfState = CTFSTATE_RETRIEVAL;
04069                         }
04070                 }
04071 
04072                 i++;
04073         }
04074 }

void CommanderBotSiegeAI bot_state_t bs  ) 
 

Definition at line 4077 of file ai_main.c.

References bot_state_t, botstates, bot_state_s::client, gentity_s::client, g_entities, gentity_t, bot_state_s::isSquadLeader, MAX_CLIENTS, entityState_s::number, OnSameTeam(), gentity_s::s, bot_state_s::siegeState, and bot_state_s::state_Forced.

Referenced by CommanderBotAI().

04078 {
04079         int i = 0;
04080         int squadmates = 0;
04081         int commanded = 0;
04082         int teammates = 0;
04083         gentity_t *squad[MAX_CLIENTS];
04084         gentity_t *ent;
04085         bot_state_t *bst;
04086 
04087         while (i < MAX_CLIENTS)
04088         {
04089                 ent = &g_entities[i];
04090 
04091                 if (ent && ent->client && OnSameTeam(&g_entities[bs->client], ent) && botstates[ent->s.number])
04092                 {
04093                         bst = botstates[ent->s.number];
04094 
04095                         if (bst && !bst->isSquadLeader && !bst->state_Forced)
04096                         {
04097                                 squad[squadmates] = ent;
04098                                 squadmates++;
04099                         }
04100                         else if (bst && !bst->isSquadLeader && bst->state_Forced)
04101                         { //count them as commanded
04102                                 commanded++;
04103                         }
04104                 }
04105 
04106                 if (ent && ent->client && OnSameTeam(&g_entities[bs->client], ent))
04107                 {
04108                         teammates++;
04109                 }
04110 
04111                 i++;
04112         }
04113         
04114         if (!squadmates)
04115         {
04116                 return;
04117         }
04118 
04119         //tell squad mates to do what I'm doing, up to half of team, let the other half make their own decisions
04120         i = 0;
04121 
04122         while (i < squadmates && squad[i])
04123         {
04124                 bst = botstates[squad[i]->s.number];
04125 
04126                 if (commanded > teammates/2)
04127                 {
04128                         break;
04129                 }
04130 
04131                 if (bst)
04132                 {
04133                         bst->state_Forced = bs->siegeState;
04134                         bst->siegeState = bs->siegeState;
04135                         commanded++;
04136                 }
04137 
04138                 i++;
04139         }
04140 }

void CommanderBotTeamplayAI bot_state_t bs  ) 
 

Definition at line 4158 of file ai_main.c.

References bot_state_t, botstates, bot_state_s::client, gentity_s::client, g_entities, gentity_t, gentity_s::health, bot_state_s::isSquadLeader, level, MAX_CLIENTS, entityState_s::number, OnSameTeam(), Q_irand(), gentity_s::s, bot_state_s::squadCannotLead, bot_state_s::squadLeader, bot_state_s::squadRegroupInterval, bot_state_s::state_Forced, bot_state_s::teamplayState, TEAMPLAYSTATE_ASSISTING, TEAMPLAYSTATE_FOLLOWING, TEAMPLAYSTATE_REGROUP, and level_locals_t::time.

Referenced by CommanderBotAI().

04159 {
04160         int i = 0;
04161         int squadmates = 0;
04162         int teammates = 0;
04163         int teammate_indanger = -1;
04164         int teammate_helped = 0;
04165         int foundsquadleader = 0;
04166         int worsthealth = 50;
04167         gentity_t *squad[MAX_CLIENTS];
04168         gentity_t *ent;
04169         bot_state_t *bst;
04170 
04171         while (i < MAX_CLIENTS)
04172         {
04173                 ent = &g_entities[i];
04174 
04175                 if (ent && ent->client && OnSameTeam(&g_entities[bs->client], ent) && botstates[ent->s.number])
04176                 {
04177                         bst = botstates[ent->s.number];
04178 
04179                         if (foundsquadleader && bst && bst->isSquadLeader)
04180                         { //never more than one squad leader
04181                                 bst->isSquadLeader = 0;
04182                         }
04183 
04184                         if (bst && !bst->isSquadLeader)
04185                         {
04186                                 squad[squadmates] = ent;
04187                                 squadmates++;
04188                         }
04189                         else if (bst)
04190                         {
04191                                 foundsquadleader = 1;
04192                         }
04193                 }
04194 
04195                 if (ent && ent->client && OnSameTeam(&g_entities[bs->client], ent))
04196                 {
04197                         teammates++;
04198 
04199                         if (ent->health < worsthealth)
04200                         {
04201                                 teammate_indanger = ent->s.number;
04202                                 worsthealth = ent->health;
04203                         }
04204                 }
04205 
04206                 i++;
04207         }
04208         
04209         if (!squadmates)
04210         {
04211                 return;
04212         }
04213 
04214         i = 0;
04215 
04216         while (i < squadmates && squad[i])
04217         {
04218                 bst = botstates[squad[i]->s.number];
04219 
04220                 if (bst && !bst->state_Forced)
04221                 { //only order if this guy is not being ordered directly by the real player team leader
04222                         if (teammate_indanger >= 0 && !teammate_helped)
04223                         { //send someone out to help whoever needs help most at the moment
04224                                 bst->teamplayState = TEAMPLAYSTATE_ASSISTING;
04225                                 bst->squadLeader = &g_entities[teammate_indanger];
04226                                 teammate_helped = 1;
04227                         }
04228                         else if ((teammate_indanger == -1 || teammate_helped) && bst->teamplayState == TEAMPLAYSTATE_ASSISTING)
04229                         { //no teammates need help badly, but this guy is trying to help them anyway, so stop
04230                                 bst->teamplayState = TEAMPLAYSTATE_FOLLOWING;
04231                                 bst->squadLeader = &g_entities[bs->client];
04232                         }
04233 
04234                         if (bs->squadRegroupInterval < level.time && Q_irand(1, 10) < 5)
04235                         { //every so often tell the squad to regroup for the sake of variation
04236                                 if (bst->teamplayState == TEAMPLAYSTATE_FOLLOWING)
04237                                 {
04238                                         bst->teamplayState = TEAMPLAYSTATE_REGROUP;
04239                                 }
04240 
04241                                 bs->isSquadLeader = 0;
04242                                 bs->squadCannotLead = level.time + 500;
04243                                 bs->squadRegroupInterval = level.time + Q_irand(45000, 65000);
04244                         }
04245                 }
04246 
04247                 i++;
04248         }       
04249 }

void CTFFlagMovement bot_state_t bs  ) 
 

Definition at line 5647 of file ai_main.c.

References BOT_FLAG_GET_DISTANCE, bot_state_t, gentity_s::classname, bot_state_s::client, droppedBlueFlag, droppedRedFlag, trace_t::entityNum, FL_DROPPED_ITEM, flagBlue, flagRed, gentity_s::flags, trace_t::fraction, gentity_t, bot_state_s::goalPosition, MASK_SOLID, NULL, entityState_s::number, bot_state_s::origin, entityState_s::pos, gentity_s::s, bot_state_s::staticFlagSpot, strcmp(), trap_Trace(), trajectory_t::trBase, vec3_t, VectorCopy, VectorSubtract, bot_state_s::wantFlag, and bot_state_s::wpDestination.

Referenced by StandardBotAI().

05648 {
05649         int diddrop = 0;
05650         gentity_t *desiredDrop = NULL;
05651         vec3_t a, mins, maxs;
05652         trace_t tr;
05653 
05654         mins[0] = -15;
05655         mins[1] = -15;
05656         mins[2] = -7;
05657         maxs[0] = 15;
05658         maxs[1] = 15;
05659         maxs[2] = 7;
05660 
05661         if (bs->wantFlag && (bs->wantFlag->flags & FL_DROPPED_ITEM))
05662         {
05663                 if (bs->staticFlagSpot[0] == bs->wantFlag->s.pos.trBase[0] &&
05664                         bs->staticFlagSpot[1] == bs->wantFlag->s.pos.trBase[1] &&
05665                         bs->staticFlagSpot[2] == bs->wantFlag->s.pos.trBase[2])
05666                 {
05667                         VectorSubtract(bs->origin, bs->wantFlag->s.pos.trBase, a);
05668 
05669                         if (VectorLength(a) <= BOT_FLAG_GET_DISTANCE)
05670                         {
05671                                 VectorCopy(bs->wantFlag->s.pos.trBase, bs->goalPosition);
05672                                 return;
05673                         }
05674                         else
05675                         {
05676                                 bs->wantFlag = NULL;
05677                         }
05678                 }
05679                 else
05680                 {
05681                         bs->wantFlag = NULL;
05682                 }
05683         }
05684         else if (bs->wantFlag)
05685         {
05686                 bs->wantFlag = NULL;
05687         }
05688 
05689         if (flagRed && flagBlue)
05690         {
05691                 if (bs->wpDestination == flagRed ||
05692                         bs->wpDestination == flagBlue)
05693                 {
05694                         if (bs->wpDestination == flagRed && droppedRedFlag && (droppedRedFlag->flags & FL_DROPPED_ITEM) && droppedRedFlag->classname && strcmp(droppedRedFlag->classname, "freed") != 0)
05695                         {
05696                                 desiredDrop = droppedRedFlag;
05697                                 diddrop = 1;
05698                         }
05699                         if (bs->wpDestination == flagBlue && droppedBlueFlag && (droppedBlueFlag->flags & FL_DROPPED_ITEM) && droppedBlueFlag->classname && strcmp(droppedBlueFlag->classname, "freed") != 0)
05700                         {
05701                                 desiredDrop = droppedBlueFlag;
05702                                 diddrop = 1;
05703                         }
05704 
05705                         if (diddrop && desiredDrop)
05706                         {
05707                                 VectorSubtract(bs->origin, desiredDrop->s.pos.trBase, a);
05708 
05709                                 if (VectorLength(a) <= BOT_FLAG_GET_DISTANCE)
05710                                 {
05711                                         trap_Trace(&tr, bs->origin, mins, maxs, desiredDrop->s.pos.trBase, bs->client, MASK_SOLID);
05712 
05713                                         if (tr.fraction == 1 || tr.entityNum == desiredDrop->s.number)
05714                                         {
05715                                                 VectorCopy(desiredDrop->s.pos.trBase, bs->goalPosition);
05716                                                 VectorCopy(desiredDrop->s.pos.trBase, bs->staticFlagSpot);
05717                                                 return;
05718                                         }
05719                                 }
05720                         }
05721                 }
05722         }
05723 }

int CTFTakesPriority bot_state_t bs  ) 
 

Definition at line 2804 of file ai_main.c.

References BOT_MAX_WEAPON_CHASE_CTF, BOT_MAX_WEAPON_GATHER_TIME, bot_state_t, BotDefendFlag(), BotGetEnemyFlag(), BotGetFlagBack(), BotGetFlagHome(), BotGuardFlagCarrier(), botstates, gentity_s::client, bot_state_s::client, level_locals_t::clients, bot_state_s::ctfState, CTFSTATE_ATTACKER, CTFSTATE_DEFENDER, CTFSTATE_GETFLAGHOME, CTFSTATE_GUARDCARRIER, CTFSTATE_RETRIEVAL, ctfStateNames, bot_state_s::cur_ps, droppedBlueFlag, droppedRedFlag, eFlagBlue, eFlagRed, FL_DROPPED_ITEM, flagBlue, flagRed, gentity_s::flags, g_entities, g_gametype, G_Printf(), G_TestLine(), gentity_t, GetBestIdleGoal(), GetNewFlagPoint(), GT_CTF, GT_CTY, gWPArray, vmCvar_t::integer, wpobject_s::inuse, bot_state_s::lastDeadTime, level, MAX_CLIENTS, NULL, entityState_s::number, oFlagBlue, oFlagRed, OnSameTeam(), wpobject_s::origin, playerState_s::powerups, gclient_s::ps, PW_BLUEFLAG, PW_REDFLAG, gentity_s::s, bot_state_s::state_Forced, TEAM_BLUE, TEAM_RED, level_locals_t::time, vec3_t, VectorCopy, playerState_s::weapon, wpobject_s::weight, WP_BRYAR_PISTOL, bot_state_s::wpDestination, bot_state_s::wpDestSwitchTime, and wpobject_t.

Referenced by GetIdealDestination().

02805 {
02806         gentity_t *ent = NULL;
02807         int enemyFlag = 0;
02808         int myFlag = 0;
02809         int enemyHasOurFlag = 0;
02810         int weHaveEnemyFlag = 0;
02811         int numOnMyTeam = 0;
02812         int numOnEnemyTeam = 0;
02813         int numAttackers = 0;
02814         int numDefenders = 0;
02815         int i = 0;
02816         int idleWP;
02817         int dosw = 0;
02818         wpobject_t *dest_sw = NULL;
02819 #ifdef BOT_CTF_DEBUG
02820         vec3_t t;
02821 
02822         G_Printf("CTFSTATE: %s\n", ctfStateNames[bs->ctfState]);
02823 #endif
02824 
02825         if (g_gametype.integer != GT_CTF && g_gametype.integer != GT_CTY)
02826         {
02827                 return 0;
02828         }
02829 
02830         if (bs->cur_ps.weapon == WP_BRYAR_PISTOL &&
02831                 (level.time - bs->lastDeadTime) < BOT_MAX_WEAPON_GATHER_TIME)
02832         { //get the nearest weapon laying around base before heading off for battle
02833                 idleWP = GetBestIdleGoal(bs);
02834 
02835                 if (idleWP != -1 && gWPArray[idleWP] && gWPArray[idleWP]->inuse)
02836                 {
02837                         if (bs->wpDestSwitchTime < level.time)
02838                         {
02839                                 bs->wpDestination = gWPArray[idleWP];
02840                         }
02841                         return 1;
02842                 }
02843         }
02844         else if (bs->cur_ps.weapon == WP_BRYAR_PISTOL &&
02845                 (level.time - bs->lastDeadTime) < BOT_MAX_WEAPON_CHASE_CTF &&
02846                 bs->wpDestination && bs->wpDestination->weight)
02847         {
02848                 dest_sw = bs->wpDestination;
02849                 dosw = 1;
02850         }
02851 
02852         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
02853         {
02854                 myFlag = PW_REDFLAG;
02855         }
02856         else
02857         {
02858                 myFlag = PW_BLUEFLAG;
02859         }
02860 
02861         if (level.clients[bs->client].sess.sessionTeam == TEAM_RED)
02862         {
02863                 enemyFlag = PW_BLUEFLAG;
02864         }
02865         else
02866         {
02867                 enemyFlag = PW_REDFLAG;
02868         }
02869 
02870         if (!flagRed || !flagBlue ||
02871                 !flagRed->inuse || !flagBlue->inuse ||
02872                 !eFlagRed || !eFlagBlue)
02873         {
02874                 return 0;
02875         }
02876 
02877 #ifdef BOT_CTF_DEBUG
02878         VectorCopy(flagRed->origin, t);
02879         t[2] += 128;
02880         G_TestLine(flagRed->origin, t, 0x0000ff, 500);
02881 
02882         VectorCopy(flagBlue->origin, t);
02883         t[2] += 128;
02884         G_TestLine(flagBlue->origin, t, 0x0000ff, 500);
02885 #endif
02886 
02887         if (droppedRedFlag && (droppedRedFlag->flags & FL_DROPPED_ITEM))
02888         {
02889                 GetNewFlagPoint(flagRed, droppedRedFlag, TEAM_RED);
02890         }
02891         else
02892         {
02893                 flagRed = oFlagRed;
02894         }
02895 
02896         if (droppedBlueFlag && (droppedBlueFlag->flags & FL_DROPPED_ITEM))
02897         {
02898                 GetNewFlagPoint(flagBlue, droppedBlueFlag, TEAM_BLUE);
02899         }
02900         else
02901         {
02902                 flagBlue = oFlagBlue;
02903         }
02904 
02905         if (!bs->ctfState)
02906         {
02907                 return 0;
02908         }
02909 
02910         i = 0;
02911 
02912         while (i < MAX_CLIENTS)
02913         {
02914                 ent = &g_entities[i];
02915 
02916                 if (ent && ent->client)
02917                 {
02918                         if (ent->client->ps.powerups[enemyFlag] && OnSameTeam(&g_entities[bs->client], ent))
02919                         {
02920                                 weHaveEnemyFlag = 1;
02921                         }
02922                         else if (ent->client->ps.powerups[myFlag] && !OnSameTeam(&g_entities[bs->client], ent))
02923                         {
02924                                 enemyHasOurFlag = 1;
02925                         }
02926 
02927                         if (OnSameTeam(&g_entities[bs->client], ent))
02928                         {
02929                                 numOnMyTeam++;
02930                         }
02931                         else
02932                         {
02933                                 numOnEnemyTeam++;
02934                         }
02935 
02936                         if (botstates[ent->s.number])
02937                         {
02938                                 if (botstates[ent->s.number]->ctfState == CTFSTATE_ATTACKER ||
02939                                         botstates[ent->s.number]->ctfState == CTFSTATE_RETRIEVAL)
02940                                 {
02941                                         numAttackers++;
02942                                 }
02943                                 else
02944                                 {
02945                                         numDefenders++;
02946                                 }
02947                         }
02948                         else
02949                         { //assume real players to be attackers in our logic
02950                                 numAttackers++;
02951                         }
02952                 }
02953                 i++;
02954         }
02955 
02956         if (bs->cur_ps.powerups[enemyFlag])
02957         {
02958                 if ((numOnMyTeam < 2 || !numAttackers) && enemyHasOurFlag)
02959                 {
02960                         bs->ctfState = CTFSTATE_RETRIEVAL;
02961                 }
02962                 else
02963                 {
02964                         bs->ctfState = CTFSTATE_GETFLAGHOME;
02965                 }
02966         }
02967         else if (bs->ctfState == CTFSTATE_GETFLAGHOME)
02968         {
02969                 bs->ctfState = 0;
02970         }
02971 
02972         if (bs->state_Forced)
02973         {
02974                 bs->ctfState = bs->state_Forced;
02975         }
02976 
02977         if (bs->ctfState == CTFSTATE_DEFENDER)
02978         {
02979                 if (BotDefendFlag(bs))
02980                 {
02981                         goto success;
02982                 }
02983         }
02984 
02985         if (bs->ctfState == CTFSTATE_ATTACKER)
02986         {
02987                 if (BotGetEnemyFlag(bs))
02988                 {
02989                         goto success;
02990                 }
02991         }
02992 
02993         if (bs->ctfState == CTFSTATE_RETRIEVAL)
02994         {
02995                 if (BotGetFlagBack(bs))
02996                 {
02997                         goto success;
02998                 }
02999                 else
03000                 { //can't find anyone on another team being a carrier, so ignore this priority
03001                         bs->ctfState = 0;
03002                 }
03003         }
03004 
03005         if (bs->ctfState == CTFSTATE_GUARDCARRIER)
03006         {
03007                 if (BotGuardFlagCarrier(bs))
03008                 {
03009                         goto success;
03010                 }
03011                 else
03012                 { //can't find anyone on our team being a carrier, so ignore this priority
03013                         bs->ctfState = 0;
03014                 }
03015         }
03016 
03017         if (bs->ctfState == CTFSTATE_GETFLAGHOME)
03018         {
03019                 if (BotGetFlagHome(bs))
03020                 {
03021                         goto success;
03022                 }
03023         }
03024 
03025         return 0;
03026 
03027 success:
03028         if (dosw)
03029         { //allow ctf code to run, but if after a particular item then keep going after it
03030                 bs->wpDestination = dest_sw;
03031         }
03032 
03033         return 1;
03034 }

int EntityVisibleBox vec3_t  org1,
vec3_t  mins,
vec3_t  maxs,
vec3_t  org2,
int  ignore,
int  ignore2
 

Definition at line 3036 of file ai_main.c.

References trace_t::allsolid, trace_t::entityNum, ENTITYNUM_NONE, trace_t::fraction, MASK_SOLID, trace_t::startsolid, trap_Trace(), and vec3_t.

Referenced by Siege_TargetClosestObjective(), and StandardBotAI().

03037 {
03038         trace_t tr;
03039 
03040         trap_Trace(&tr, org1, mins, maxs, org2, ignore, MASK_SOLID);
03041 
03042         if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
03043         {
03044                 return 1;
03045         }
03046         else if (tr.entityNum != ENTITYNUM_NONE && tr.entityNum == ignore2)
03047         {
03048                 return 1;
03049         }
03050 
03051         return 0;
03052 }

void ExitLevel void   ) 
 

Definition at line 2125 of file g_main.c.

References siegePers_s::beatingTime, level_locals_t::changemap, level_locals_t::clients, CON_CONNECTED, CON_CONNECTING, clientPersistant_t::connected, DuelLimitHit(), DuelResetWinsLosses(), EXEC_APPEND, g_gametype, g_maxclients, g_siegePersistant, g_siegeTeamSwitch, G_WriteSessionData(), gclient_t, GT_DUEL, GT_POWERDUEL, GT_SIEGE, vmCvar_t::integer, level_locals_t::intermissiontime, level, NULL, gclient_s::pers, PERS_SCORE, playerState_s::persistant, gclient_s::ps, qtrue, level_locals_t::restarted, SiegeDoTeamAssign(), TEAM_BLUE, TEAM_RED, level_locals_t::teamScores, and trap_SendConsoleCommand().

Referenced by CheckIntermissionExit().

02125                       {
02126         int             i;
02127         gclient_t *cl;
02128 
02129         // if we are running a tournement map, kick the loser to spectator status,
02130         // which will automatically grab the next spectator and restart
02131         if ( g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL ) {
02132                 if (!DuelLimitHit())
02133                 {
02134                         if ( !level.restarted ) {
02135                                 trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
02136                                 level.restarted = qtrue;
02137                                 level.changemap = NULL;
02138                                 level.intermissiontime = 0;
02139                         }
02140                         return; 
02141                 }
02142 
02143                 DuelResetWinsLosses();
02144         }
02145 
02146 
02147         if (g_gametype.integer == GT_SIEGE &&
02148                 g_siegeTeamSwitch.integer &&
02149                 g_siegePersistant.beatingTime)
02150         { //restart same map...
02151                 trap_SendConsoleCommand( EXEC_APPEND, "map_restart 0\n" );
02152         }
02153         else
02154         {
02155                 trap_SendConsoleCommand( EXEC_APPEND, "vstr nextmap\n" );
02156         }
02157         level.changemap = NULL;
02158         level.intermissiontime = 0;
02159 
02160         if (g_gametype.integer == GT_SIEGE &&
02161                 g_siegeTeamSwitch.integer)
02162         { //switch out now
02163                 SiegeDoTeamAssign();
02164         }
02165 
02166         // reset all the scores so we don't enter the intermission again
02167         level.teamScores[TEAM_RED] = 0;
02168         level.teamScores[TEAM_BLUE] = 0;
02169         for ( i=0 ; i< g_maxclients.integer ; i++ ) {
02170                 cl = level.clients + i;
02171                 if ( cl->pers.connected != CON_CONNECTED ) {
02172                         continue;
02173                 }
02174                 cl->ps.persistant[PERS_SCORE] = 0;
02175         }
02176 
02177         // we need to do this here before chaning to CON_CONNECTING
02178         G_WriteSessionData();
02179 
02180         // change all client states to connecting, so the early players into the
02181         // next level will know the others aren't done reconnecting
02182         for (i=0 ; i< g_maxclients.integer ; i++) {
02183                 if ( level.clients[i].pers.connected == CON_CONNECTED ) {
02184                         level.clients[i].pers.connected = CON_CONNECTING;
02185                 }
02186         }
02187 
02188 }

qboolean G_ThereIsAMaster void   ) 
 

Definition at line 4307 of file g_combat.c.

References gentity_s::client, g_entities, gentity_t, playerState_s::isJediMaster, MAX_CLIENTS, gclient_s::ps, qboolean, qfalse, and qtrue.

Referenced by G_Damage(), and ScanForEnemies().

04308 {
04309         int i = 0;
04310         gentity_t *ent;
04311 
04312         while (i < MAX_CLIENTS)
04313         {
04314                 ent = &g_entities[i];
04315 
04316                 if (ent && ent->client && ent->client->ps.isJediMaster)
04317                 {
04318                         return qtrue;
04319                 }
04320 
04321                 i++;
04322         }
04323 
04324         return qfalse;
04325 }

int GetBestIdleGoal bot_state_t bs  ) 
 

Definition at line 3559 of file ai_main.c.

References bot_state_t, BotHasAssociated(), wpobject_s::flags, gWPArray, gWPNum, wpobject_s::index, wpobject_s::inuse, bot_state_s::isCamper, level, Q_irand(), bot_state_s::randomNav, bot_state_s::randomNavTime, level_locals_t::time, TotalTrailDistance(), wpobject_s::weight, bot_state_s::wpCurrent, and WPFLAG_GOALPOINT.

Referenced by CTFTakesPriority(), GetIdealDestination(), and SiegeTakesPriority().

03560 {
03561         int i = 0;
03562         int highestweight = 0;
03563         int desiredindex = -1;
03564         int dist_to_weight = 0;
03565         int traildist;
03566 
03567         if (!bs->wpCurrent)
03568         {
03569                 return -1;
03570         }
03571 
03572         if (bs->isCamper != 2)
03573         {
03574                 if (bs->randomNavTime < level.time)
03575                 {
03576                         if (Q_irand(1, 10) < 5)
03577                         {
03578                                 bs->randomNav = 1;
03579                         }
03580                         else
03581                         {
03582                                 bs->randomNav = 0;
03583                         }
03584                         
03585                         bs->randomNavTime = level.time + Q_irand(5000, 15000);
03586                 }
03587         }
03588 
03589         if (bs->randomNav)
03590         { //stop looking for items and/or camping on them
03591                 return -1;
03592         }
03593 
03594         while (i < gWPNum)
03595         {
03596                 if (gWPArray[i] &&
03597                         gWPArray[i]->inuse &&
03598                         (gWPArray[i]->flags & WPFLAG_GOALPOINT) &&
03599                         gWPArray[i]->weight > highestweight &&
03600                         !BotHasAssociated(bs, gWPArray[i]))
03601                 {
03602                         traildist = TotalTrailDistance(bs->wpCurrent->index, i, bs);
03603 
03604                         if (traildist != -1)
03605                         {
03606                                 dist_to_weight = (int)traildist/10000;
03607                                 dist_to_weight = (gWPArray[i]->weight)-dist_to_weight;
03608 
03609                                 if (dist_to_weight > highestweight)
03610                                 {
03611                                         highestweight = dist_to_weight;
03612                                         desiredindex = i;
03613                                 }
03614                         }
03615                 }
03616 
03617                 i++;
03618         }
03619 
03620         return desiredindex;
03621 }

void GetIdealDestination bot_state_t bs  ) 
 

Definition at line 3625 of file ai_main.c.

References bot_state_t, BotGetWeaponRange(), BotIsAChickenWuss(), BWEAPONRANGE_LONG, BWEAPONRANGE_MELEE, BWEAPONRANGE_MID, BWEAPONRANGE_SABER, CA_ACTIVE, CA_AUTHORIZING, gentity_s::client, clientPersistant_t::connected, bot_state_s::ctfState, CTFTakesPriority(), bot_state_s::currentEnemy, bot_state_s::dangerousObject, bot_state_s::dontGoBack, bot_state_s::escapeDirTime, bot_state_s::frame_Enemy_Len, g_gametype, gentity_t, GetBestIdleGoal(), GetNearestBadThing(), GetNearestVisibleWP(), GT_SINGLE_PLAYER, gWPArray, gentity_s::health, wpobject_s::index, vmCvar_t::integer, wpobject_s::inuse, gentity_s::inuse, JMTakesPriority(), level, NULL, entityState_s::origin, playerState_s::origin, wpobject_s::origin, gclient_s::pers, entityState_s::pos, gclient_s::ps, Q_irand(), bot_state_s::revengeEnemy, bot_state_s::runningLikeASissy, bot_state_s::runningToEscapeThreat, gentity_s::s, bot_state_s::siegeState, SiegeTakesPriority(), bot_state_s::squadLeader, gentity_s::takedamage, level_locals_t::time, TotalTrailDistance(), trap_Cvar_Update(), trajectory_t::trBase, vec3_t, VectorCopy, VectorSubtract, bot_state_s::wpCamping, bot_state_s::wpCurrent, bot_state_s::wpDestIgnoreTime, bot_state_s::wpDestination, bot_state_s::wpDestSwitchTime, bot_state_s::wpDirection, and bot_state_s::wpStoreDest.

Referenced by StandardBotAI().

03626 {
03627         int tempInt, cWPIndex, bChicken, idleWP;
03628         float distChange, plusLen, minusLen;
03629         vec3_t usethisvec, a;
03630         gentity_t *badthing;
03631 
03632 #ifdef _DEBUG
03633         trap_Cvar_Update(&bot_nogoals);
03634 
03635         if (bot_nogoals.integer)
03636         {
03637                 return;
03638         }
03639 #endif
03640 
03641         if (!bs->wpCurrent)
03642         {
03643                 return;
03644         }
03645 
03646         if ((level.time - bs->escapeDirTime) > 4000)
03647         {
03648                 badthing = GetNearestBadThing(bs);
03649         }
03650         else
03651         {
03652                 badthing = NULL;
03653         }
03654 
03655         if (badthing && badthing->inuse &&
03656                 badthing->health > 0 && badthing->takedamage)
03657         {
03658                 bs->dangerousObject = badthing;
03659         }
03660         else
03661         {
03662                 bs->dangerousObject = NULL;
03663         }
03664 
03665         if (!badthing && bs->wpDestIgnoreTime > level.time)
03666         {
03667                 return;
03668         }
03669 
03670         if (!badthing && bs->dontGoBack > level.time)
03671         {
03672                 if (bs->wpDestination)
03673                 {
03674                         bs->wpStoreDest = bs->wpDestination;
03675                 }
03676                 bs->wpDestination = NULL;
03677                 return;
03678         }
03679         else if (!badthing && bs->wpStoreDest)
03680         { //after we finish running away, switch back to our original destination
03681                 bs->wpDestination = bs->wpStoreDest;
03682                 bs->wpStoreDest = NULL;
03683         }
03684 
03685         if (badthing && bs->wpCamping)
03686         {
03687                 bs->wpCamping = NULL;
03688         }
03689 
03690         if (bs->wpCamping)
03691         {
03692                 bs->wpDestination = bs->wpCamping;
03693                 return;
03694         }
03695 
03696         if (!badthing && CTFTakesPriority(bs))
03697         {
03698                 if (bs->ctfState)
03699                 {
03700                         bs->runningToEscapeThreat = 1;
03701                 }
03702                 return;
03703         }
03704         else if (!badthing && SiegeTakesPriority(bs))
03705         {
03706                 if (bs->siegeState)
03707                 {
03708                         bs->runningToEscapeThreat = 1;
03709                 }
03710                 return;
03711         }
03712         else if (!badthing && JMTakesPriority(bs))
03713         {
03714                 bs->runningToEscapeThreat = 1;
03715         }
03716 
03717         if (badthing)
03718         {
03719                 bs->runningLikeASissy = level.time + 100;
03720 
03721                 if (bs->wpDestination)
03722                 {
03723                         bs->wpStoreDest = bs->wpDestination;
03724                 }
03725                 bs->wpDestination = NULL;
03726 
03727                 if (bs->wpDirection)
03728                 {
03729                         tempInt = bs->wpCurrent->index+1;
03730                 }
03731                 else
03732                 {
03733                         tempInt = bs->wpCurrent->index-1;
03734                 }
03735 
03736                 if (gWPArray[tempInt] && gWPArray[tempInt]->inuse && bs->escapeDirTime < level.time)
03737                 {
03738                         VectorSubtract(badthing->s.pos.trBase, bs->wpCurrent->origin, a);
03739                         plusLen = VectorLength(a);
03740                         VectorSubtract(badthing->s.pos.trBase, gWPArray[tempInt]->origin, a);
03741                         minusLen = VectorLength(a);
03742 
03743                         if (plusLen < minusLen)
03744                         {
03745                                 if (bs->wpDirection)
03746                                 {
03747                                         bs->wpDirection = 0;
03748                                 }
03749                                 else
03750                                 {
03751                                         bs->wpDirection = 1;
03752                                 }
03753 
03754                                 bs->wpCurrent = gWPArray[tempInt];
03755 
03756                                 bs->escapeDirTime = level.time + Q_irand(500, 1000);//Q_irand(1000, 1400);
03757 
03758                                 //G_Printf("Escaping from scary bad thing [%s]\n", badthing->classname);
03759                         }
03760                 }
03761                 //G_Printf("Run away run away run away!\n");
03762                 return;
03763         }
03764 
03765         distChange = 0; //keep the compiler from complaining
03766 
03767         tempInt = BotGetWeaponRange(bs);
03768 
03769         if (tempInt == BWEAPONRANGE_MELEE)
03770         {
03771                 distChange = 1;
03772         }
03773         else if (tempInt == BWEAPONRANGE_SABER)
03774         {
03775                 distChange = 1;
03776         }
03777         else if (tempInt == BWEAPONRANGE_MID)
03778         {
03779                 distChange = 128;
03780         }
03781         else if (tempInt == BWEAPONRANGE_LONG)
03782         {
03783                 distChange = 300;
03784         }
03785 
03786         if (bs->revengeEnemy && bs->revengeEnemy->health > 0 &&
03787                 bs->revengeEnemy->client && (bs->revengeEnemy->client->pers.connected == CA_ACTIVE || bs->revengeEnemy->client->pers.connected == CA_AUTHORIZING))
03788         { //if we hate someone, always try to get to them
03789                 if (bs->wpDestSwitchTime < level.time)
03790                 {
03791                         if (bs->revengeEnemy->client)
03792                         {
03793                                 VectorCopy(bs->revengeEnemy->client->ps.origin, usethisvec);
03794                         }
03795                         else
03796                         {
03797                                 VectorCopy(bs->revengeEnemy->s.origin, usethisvec);
03798                         }
03799 
03800                         tempInt = GetNearestVisibleWP(usethisvec, 0);
03801 
03802                         if (tempInt != -1 && TotalTrailDistance(bs->wpCurrent->index, tempInt, bs) != -1)
03803                         {
03804                                 bs->wpDestination = gWPArray[tempInt];
03805                                 bs->wpDestSwitchTime = level.time + Q_irand(5000, 10000);
03806                         }
03807                 }
03808         }
03809         else if (bs->squadLeader && bs->squadLeader->health > 0 &&
03810                 bs->squadLeader->client && (bs->squadLeader->client->pers.connected == CA_ACTIVE || bs->squadLeader->client->pers.connected == CA_AUTHORIZING))
03811         {
03812                 if (bs->wpDestSwitchTime < level.time)
03813                 {
03814                         if (bs->squadLeader->client)
03815                         {
03816                                 VectorCopy(bs->squadLeader->client->ps.origin, usethisvec);
03817                         }
03818                         else
03819                         {
03820                                 VectorCopy(bs->squadLeader->s.origin, usethisvec);
03821                         }
03822 
03823                         tempInt = GetNearestVisibleWP(usethisvec, 0);
03824 
03825                         if (tempInt != -1 && TotalTrailDistance(bs->wpCurrent->index, tempInt, bs) != -1)
03826                         {
03827                                 bs->wpDestination = gWPArray[tempInt];
03828                                 bs->wpDestSwitchTime = level.time + Q_irand(5000, 10000);
03829                         }
03830                 }
03831         }
03832         else if (bs->currentEnemy)
03833         {
03834                 if (bs->currentEnemy->client)
03835                 {
03836                         VectorCopy(bs->currentEnemy->client->ps.origin, usethisvec);
03837                 }
03838                 else
03839                 {
03840                         VectorCopy(bs->currentEnemy->s.origin, usethisvec);
03841                 }
03842 
03843                 bChicken = BotIsAChickenWuss(bs);
03844                 bs->runningToEscapeThreat = bChicken;
03845 
03846                 if (bs->frame_Enemy_Len < distChange || (bChicken && bChicken != 2))
03847                 {
03848                         cWPIndex = bs->wpCurrent->index;
03849 
03850                         if (bs->frame_Enemy_Len > 400)
03851                         { //good distance away, start running toward a good place for an item or powerup or whatever
03852                                 idleWP = GetBestIdleGoal(bs);
03853 
03854                                 if (idleWP != -1 && gWPArray[idleWP] && gWPArray[idleWP]->inuse)
03855                                 {
03856                                         bs->wpDestination = gWPArray[idleWP];
03857                                 }
03858                         }
03859                         else if (gWPArray[cWPIndex-1] && gWPArray[cWPIndex-1]->inuse &&
03860                                 gWPArray[cWPIndex+1] && gWPArray[cWPIndex+1]->inuse)
03861                         {
03862                                 VectorSubtract(gWPArray[cWPIndex+1]->origin, usethisvec, a);
03863                                 plusLen = VectorLength(a);
03864                                 VectorSubtract(gWPArray[cWPIndex-1]->origin, usethisvec, a);
03865                                 minusLen = VectorLength(a);
03866 
03867                                 if (minusLen > plusLen)
03868                                 {
03869                                         bs->wpDestination = gWPArray[cWPIndex-1];
03870                                 }
03871                                 else
03872                                 {
03873                                         bs->wpDestination = gWPArray[cWPIndex+1];
03874                                 }
03875                         }
03876                 }
03877                 else if (bChicken != 2 && bs->wpDestSwitchTime < level.time)
03878                 {
03879                         tempInt = GetNearestVisibleWP(usethisvec, 0);
03880 
03881                         if (tempInt != -1 && TotalTrailDistance(bs->wpCurrent->index, tempInt, bs) != -1)
03882                         {
03883                                 bs->wpDestination = gWPArray[tempInt];
03884 
03885                                 if (g_gametype.integer == GT_SINGLE_PLAYER)
03886                                 { //be more aggressive
03887                                         bs->wpDestSwitchTime = level.time + Q_irand(300, 1000);
03888                                 }
03889                                 else
03890                                 {
03891                                         bs->wpDestSwitchTime = level.time + Q_irand(1000, 5000);
03892                                 }
03893                         }
03894                 }
03895         }
03896 
03897         if (!bs->wpDestination && bs->wpDestSwitchTime < level.time)
03898         {
03899                 //G_Printf("I need something to do\n");
03900                 idleWP = GetBestIdleGoal(bs);
03901 
03902                 if (idleWP != -1 && gWPArray[idleWP] && gWPArray[idleWP]->inuse)
03903                 {
03904                         bs->wpDestination = gWPArray[idleWP];
03905                 }
03906         }
03907 }

int GetLoveLevel bot_state_t bs,
bot_state_t love
 

Definition at line 5269 of file ai_main.c.

References bot_attachments, bot_state_t, bot_state_s::client, g_entities, g_gametype, GT_DUEL, GT_POWERDUEL, vmCvar_t::integer, botattachment_s::level, bot_state_s::loved, bot_state_s::lovednum, botattachment_s::name, NULL, and strcmp().

Referenced by BotScanForLeader(), and CheckForFriendInLOF().

05270 {
05271         int i = 0;
05272         const char *lname = NULL;
05273 
05274         if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
05275         { //There is no love in 1-on-1
05276                 return 0;
05277         }
05278 
05279         if (!bs || !love || !g_entities[love->client].client)
05280         {
05281                 return 0;
05282         }
05283 
05284         if (!bs->lovednum)
05285         {
05286                 return 0;
05287         }
05288 
05289         if (!bot_attachments.integer)
05290         {
05291                 return 1;
05292         }
05293 
05294         lname = g_entities[love->client].client->pers.netname;
05295 
05296         if (!lname)
05297         {
05298                 return 0;
05299         }
05300 
05301         while (i < bs->lovednum)
05302         {
05303                 if (strcmp(bs->loved[i].name, lname) == 0)
05304                 {
05305                         return bs->loved[i].level;
05306                 }
05307 
05308                 i++;
05309         }
05310 
05311         return 0;
05312 }

gentity_t* GetNearestBadThing bot_state_t bs  ) 
 

Definition at line 2395 of file ai_main.c.

References bot_state_t, BotPVSCheck(), bot_state_s::client, gentity_s::client, bot_state_s::currentEnemy, entityShared_t::currentOrigin, gentity_s::damage, bot_state_s::doForcePush, bot_state_s::dontGoBack, ENEMY_FORGET_MS, bot_state_s::enemySeenTime, trace_t::entityNum, trace_t::fraction, g_entities, gentity_s::genericValue3, gentity_s::genericValue5, gentity_t, gentity_s::health, gentity_s::inuse, level, MASK_SOLID, MAX_CLIENTS, NULL, level_locals_t::num_entities, entityState_s::number, OnSameTeam(), bot_state_s::origin, entityShared_t::ownerNum, PassLovedOneCheck(), PassStandardEnemyChecks(), entityState_s::pos, gentity_s::r, gentity_s::s, bot_state_s::settings, bot_settings_s::skill, gentity_s::splashDamage, level_locals_t::time, trap_Trace(), trajectory_t::trBase, vec3_t, VectorSubtract, entityState_s::weapon, WP_DET_PACK, WP_FLECHETTE, WP_ROCKET_LAUNCHER, WP_THERMAL, and WP_TRIP_MINE.

Referenced by GetIdealDestination().

02396 {
02397         int i = 0;
02398         float glen;
02399         vec3_t hold;
02400         int bestindex = 0;
02401         float bestdist = 800; //if not within a radius of 800, it's no threat anyway
02402         int foundindex = 0;
02403         float factor = 0;
02404         gentity_t *ent;
02405         trace_t tr;
02406 
02407         while (i < level.num_entities)
02408         {
02409                 ent = &g_entities[i];
02410 
02411                 if ( (ent &&
02412                         !ent->client &&
02413                         ent->inuse &&
02414                         ent->damage &&
02415                         /*(ent->s.weapon == WP_THERMAL || ent->s.weapon == WP_FLECHETTE)*/
02416                         ent->s.weapon &&
02417                         ent->splashDamage) ||
02418                         (ent &&
02419                         ent->genericValue5 == 1000 &&
02420                         ent->inuse &&
02421                         ent->health > 0 &&
02422                         ent->genericValue3 != bs->client &&
02423                         g_entities[ent->genericValue3].client && !OnSameTeam(&g_entities[bs->client], &g_entities[ent->genericValue3])) )
02424                 { //try to escape from anything with a non-0 s.weapon and non-0 damage. This hopefully only means dangerous projectiles.
02425                   //Or a sentry gun if bolt_Head == 1000. This is a terrible hack, yes.
02426                         VectorSubtract(bs->origin, ent->r.currentOrigin, hold);
02427                         glen = VectorLength(hold);
02428 
02429                         if (ent->s.weapon != WP_THERMAL && ent->s.weapon != WP_FLECHETTE &&
02430                                 ent->s.weapon != WP_DET_PACK && ent->s.weapon != WP_TRIP_MINE)
02431                         {
02432                                 factor = 0.5;
02433 
02434                                 if (ent->s.weapon && glen <= 256 && bs->settings.skill > 2)
02435                                 { //it's a projectile so push it away
02436                                         bs->doForcePush = level.time + 700;
02437                                         //G_Printf("PUSH PROJECTILE\n");
02438                                 }
02439                         }
02440                         else
02441                         {
02442                                 factor = 1;
02443                         }
02444 
02445                         if (ent->s.weapon == WP_ROCKET_LAUNCHER &&
02446                                 (ent->r.ownerNum == bs->client ||
02447                                 (ent->r.ownerNum > 0 && ent->r.ownerNum < MAX_CLIENTS &&
02448                                 g_entities[ent->r.ownerNum].client && OnSameTeam(&g_entities[bs->client], &g_entities[ent->r.ownerNum]))) )
02449                         { //don't be afraid of your own rockets or your teammates' rockets
02450                                 factor = 0;
02451                         }
02452 
02453                         if (glen < bestdist*factor && BotPVSCheck(bs->origin, ent->s.pos.trBase))
02454                         {
02455                                 trap_Trace(&tr, bs->origin, NULL, NULL, ent->s.pos.trBase, bs->client, MASK_SOLID);
02456 
02457                                 if (tr.fraction == 1 || tr.entityNum == ent->s.number)
02458                                 {
02459                                         bestindex = i;
02460                                         bestdist = glen;
02461                                         foundindex = 1;
02462                                 }
02463                         }
02464                 }
02465 
02466                 if (ent && !ent->client && ent->inuse && ent->damage && ent->s.weapon && ent->r.ownerNum < MAX_CLIENTS && ent->r.ownerNum >= 0)
02467                 { //if we're in danger of a projectile belonging to someone and don't have an enemy, set the enemy to them
02468                         gentity_t *projOwner = &g_entities[ent->r.ownerNum];
02469 
02470                         if (projOwner && projOwner->inuse && projOwner->client)
02471                         {
02472                                 if (!bs->currentEnemy)
02473                                 {
02474                                         if (PassStandardEnemyChecks(bs, projOwner))
02475                                         {
02476                                                 if (PassLovedOneCheck(bs, projOwner))
02477                                                 {
02478                                                         VectorSubtract(bs->origin, ent->r.currentOrigin, hold);
02479                                                         glen = VectorLength(hold);
02480 
02481                                                         if (glen < 512)
02482                                                         {
02483                                                                 bs->currentEnemy = projOwner;
02484                                                                 bs->enemySeenTime = level.time + ENEMY_FORGET_MS;
02485                                                         }
02486                                                 }
02487                                         }
02488                                 }
02489                         }
02490                 }
02491 
02492                 i++;
02493         }
02494 
02495         if (foundindex)
02496         {
02497                 bs->dontGoBack = level.time + 1500;
02498                 return &g_entities[bestindex];
02499         }
02500         else
02501         {
02502                 return NULL;
02503         }
02504 }

int GetNearestVisibleWP vec3_t  org,
int  ignore
 

Definition at line 1104 of file ai_main.c.

References BotPVSCheck(), g_RMG, gWPArray, gWPNum, vmCvar_t::integer, wpobject_s::inuse, OrgVisibleBox(), vec3_t, and VectorSubtract.

Referenced by BotGetFlagBack(), BotGuardFlagCarrier(), CalculateSiegeGoals(), GetIdealDestination(), JMTakesPriority(), Siege_DefendFromAttackers(), and StandardBotAI().

01105 {
01106         int i;
01107         float bestdist;
01108         float flLen;
01109         int bestindex;
01110         vec3_t a, mins, maxs;
01111 
01112         i = 0;
01113         if (g_RMG.integer)
01114         {
01115                 bestdist = 300;
01116         }
01117         else
01118         {
01119                 bestdist = 800;//99999;
01120                                    //don't trace over 800 units away to avoid GIANT HORRIBLE SPEED HITS ^_^
01121         }
01122         bestindex = -1;
01123 
01124         mins[0] = -15;
01125         mins[1] = -15;
01126         mins[2] = -1;
01127         maxs[0] = 15;
01128         maxs[1] = 15;
01129         maxs[2] = 1;
01130 
01131         while (i < gWPNum)
01132         {
01133                 if (gWPArray[i] && gWPArray[i]->inuse)
01134                 {
01135                         VectorSubtract(org, gWPArray[i]->origin, a);
01136                         flLen = VectorLength(a);
01137 
01138                         if (flLen < bestdist && (g_RMG.integer || BotPVSCheck(org, gWPArray[i]->origin)) && OrgVisibleBox(org, mins, maxs, gWPArray[i]->origin, ignore))
01139                         {
01140                                 bestdist = flLen;
01141                                 bestindex = i;
01142                         }
01143                 }
01144 
01145                 i++;
01146         }
01147 
01148         return bestindex;
01149 }

void GetNewFlagPoint wpobject_t wp,
gentity_t flagEnt,
int  team
 

Definition at line 2739 of file ai_main.c.

References flagBlue, flagRed, trace_t::fraction, gentity_t, gWPArray, gWPNum, MASK_SOLID, entityState_s::number, wpobject_s::origin, entityState_s::pos, gentity_s::s, TEAM_RED, trap_Trace(), trajectory_t::trBase, vec3_t, VectorSubtract, WP_KEEP_FLAG_DIST, and wpobject_t.

Referenced by CTFTakesPriority().

02740 { //get the nearest possible waypoint to the flag since it's not in its original position
02741         int i = 0;
02742         vec3_t a, mins, maxs;
02743         float bestdist;
02744         float testdist;
02745         int bestindex = 0;
02746         int foundindex = 0;
02747         trace_t tr;
02748 
02749         mins[0] = -15;
02750         mins[1] = -15;
02751         mins[2] = -5;
02752         maxs[0] = 15;
02753         maxs[1] = 15;
02754         maxs[2] = 5;
02755 
02756         VectorSubtract(wp->origin, flagEnt->s.pos.trBase, a);
02757 
02758         bestdist = VectorLength(a);
02759 
02760         if (bestdist <= WP_KEEP_FLAG_DIST)
02761         {
02762                 trap_Trace(&tr, wp->origin, mins, maxs, flagEnt->s.pos.trBase, flagEnt->s.number, MASK_SOLID);
02763 
02764                 if (tr.fraction == 1)
02765                 { //this point is good
02766                         return;
02767                 }
02768         }
02769 
02770         while (i < gWPNum)
02771         {
02772                 VectorSubtract(gWPArray[i]->origin, flagEnt->s.pos.trBase, a);
02773                 testdist = VectorLength(a);
02774 
02775                 if (testdist < bestdist)
02776                 {
02777                         trap_Trace(&tr, gWPArray[i]->origin, mins, maxs, flagEnt->s.pos.trBase, flagEnt->s.number, MASK_SOLID);
02778 
02779                         if (tr.fraction == 1)
02780                         {
02781                                 foundindex = 1;
02782                                 bestindex = i;
02783                                 bestdist = testdist;
02784                         }
02785                 }
02786 
02787                 i++;
02788         }
02789 
02790         if (foundindex)
02791         {
02792                 if (team == TEAM_RED)
02793                 {
02794                         flagRed = gWPArray[bestindex];
02795                 }
02796                 else
02797                 {
02798                         flagBlue = gWPArray[bestindex];
02799                 }
02800         }
02801 }

int InFieldOfVision vec3_t  viewangles,
float  fov,
vec3_t  angles
 

Definition at line 2034 of file ai_main.c.

References AngleMod(), and vec3_t.

Referenced by CombatBotAI(), ForceTelepathy(), ForceThrow(), ScanForEnemies(), and StandardBotAI().

02035 {
02036         int i;
02037         float diff, angle;
02038 
02039         for (i = 0; i < 2; i++)
02040         {
02041                 angle = AngleMod(viewangles[i]);
02042                 angles[i] = AngleMod(angles[i]);
02043                 diff = angles[i] - angle;
02044                 if (angles[i] > angle)
02045                 {
02046                         if (diff > 180.0)
02047                         {
02048                                 diff -= 360.0;
02049                         }
02050                 }
02051                 else
02052                 {
02053                         if (diff < -180.0)
02054                         {
02055                                 diff += 360.0;
02056                         }
02057                 }
02058                 if (diff > 0)
02059                 {
02060                         if (diff > fov * 0.5)
02061                         {
02062                                 return 0;
02063                         }
02064                 }
02065                 else
02066                 {
02067                         if (diff < -fov * 0.5)
02068                         {
02069                                 return 0;
02070                         }
02071                 }
02072         }
02073         return 1;
02074 }

int IsTeamplay void   ) 
 

Definition at line 336 of file ai_main.c.

References g_gametype, GT_TEAM, and vmCvar_t::integer.

Referenced by BotLovedOneDied(), BotScanForLeader(), CheckForFriendInLOF(), and PassLovedOneCheck().

00337 {
00338         if ( g_gametype.integer < GT_TEAM )
00339         {
00340                 return 0;
00341         }
00342 
00343         return 1;
00344 }

int JMTakesPriority bot_state_t bs  ) 
 

Definition at line 3424 of file ai_main.c.

References bot_state_t, gentity_s::client, bot_state_s::cur_ps, entityShared_t::currentOrigin, bot_state_s::destinationGrabTime, g_entities, g_gametype, gentity_t, GetNearestVisibleWP(), gJMSaberEnt, GT_JEDIMASTER, gWPArray, vmCvar_t::integer, wpobject_s::inuse, gentity_s::inuse, playerState_s::isJediMaster, bot_state_s::jmState, level, MAX_CLIENTS, NULL, entityState_s::number, playerState_s::origin, gclient_s::ps, gentity_s::r, gentity_s::s, level_locals_t::time, and bot_state_s::wpDestination.

Referenced by GetIdealDestination().

03425 {
03426         int i = 0;
03427         int wpClose = -1;
03428         gentity_t *theImportantEntity = NULL;
03429 
03430         if (g_gametype.integer != GT_JEDIMASTER)
03431         {
03432                 return 0;
03433         }
03434 
03435         if (bs->cur_ps.isJediMaster)
03436         {
03437                 return 0;
03438         }
03439 
03440         //jmState becomes the index for the one who carries the saber. If jmState is -1 then the saber is currently
03441         //without an owner
03442         bs->jmState = -1;
03443 
03444         while (i < MAX_CLIENTS)
03445         {
03446                 if (g_entities[i].client && g_entities[i].inuse &&
03447                         g_entities[i].client->ps.isJediMaster)
03448                 {
03449                         bs->jmState = i;
03450                         break;
03451                 }
03452 
03453                 i++;
03454         }
03455 
03456         if (bs->jmState != -1)
03457         {
03458                 theImportantEntity = &g_entities[bs->jmState];
03459         }
03460         else
03461         {
03462                 theImportantEntity = gJMSaberEnt;
03463         }
03464 
03465         if (theImportantEntity && theImportantEntity->inuse && bs->destinationGrabTime < level.time)
03466         {
03467                 if (theImportantEntity->client)
03468                 {
03469                         wpClose = GetNearestVisibleWP(theImportantEntity->client->ps.origin, theImportantEntity->s.number);     
03470                 }
03471                 else
03472                 {
03473                         wpClose = GetNearestVisibleWP(theImportantEntity->r.currentOrigin, theImportantEntity->s.number);       
03474                 }
03475 
03476                 if (wpClose != -1 && gWPArray[wpClose] && gWPArray[wpClose]->inuse)
03477                 {
03478                         /*
03479                         Com_Printf("BOT GRABBED IDEAL JM LOCATION\n");
03480                         if (bs->wpDestination != gWPArray[wpClose])
03481                         {
03482                                 Com_Printf("IDEAL WAS NOT ALREADY IDEAL\n");
03483 
03484                                 if (!bs->wpDestination)
03485                                 {
03486                                         Com_Printf("IDEAL WAS NULL\n");
03487                                 }
03488                         }
03489                         */
03490                         bs->wpDestination = gWPArray[wpClose];
03491                         bs->destinationGrabTime = level.time + 4000;
03492                 }
03493         }
03494 
03495         return 1;
03496 }

int KeepAltFromFiring bot_state_t bs  ) 
 

Definition at line 5520 of file ai_main.c.

References bot_state_t, bot_state_s::cur_ps, bot_state_s::doAltAttack, WEAPON_CHARGING_ALT, and playerState_s::weaponstate.

Referenced by StandardBotAI().

05521 {
05522         if (bs->cur_ps.weaponstate != WEAPON_CHARGING_ALT &&
05523                 bs->doAltAttack)
05524         {
05525                 bs->doAltAttack = 0;
05526         }
05527 
05528         if (bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT &&
05529                 !bs->doAltAttack)
05530         {
05531                 bs->doAltAttack = 1;
05532         }
05533 
05534         return 0;
05535 }

int KeepPrimFromFiring bot_state_t bs  ) 
 

Definition at line 5484 of file ai_main.c.

References bot_state_t, bot_state_s::cur_ps, bot_state_s::doAttack, WEAPON_CHARGING, and playerState_s::weaponstate.

Referenced by StandardBotAI().

05485 {
05486         if (bs->cur_ps.weaponstate != WEAPON_CHARGING &&
05487                 bs->doAttack)
05488         {
05489                 bs->doAttack = 0;
05490         }
05491 
05492         if (bs->cur_ps.weaponstate == WEAPON_CHARGING &&
05493                 !bs->doAttack)
05494         {
05495                 bs->doAttack = 1;
05496         }
05497 
05498         return 0;
05499 }

void MeleeCombatHandling bot_state_t bs  ) 
 

Definition at line 4269 of file ai_main.c.

References AngleVectors(), bot_state_t, gentity_s::client, bot_state_s::currentEnemy, trace_t::endpos, bot_state_s::frame_Enemy_Len, bot_state_s::goalPosition, level, MASK_SOLID, bot_state_s::meleeStrafeDir, bot_state_s::meleeStrafeTime, NULL, bot_state_s::origin, entityState_s::origin, playerState_s::origin, gclient_s::ps, Q_irand(), gentity_s::s, level_locals_t::time, trap_Trace(), vec3_t, vectoangles(), VectorCopy, and VectorSubtract.

Referenced by StandardBotAI().

04270 {
04271         vec3_t usethisvec;
04272         vec3_t downvec;
04273         vec3_t midorg;
04274         vec3_t a;
04275         vec3_t fwd;
04276         vec3_t mins, maxs;
04277         trace_t tr;
04278         int en_down;
04279         int me_down;
04280         int mid_down;
04281 
04282         if (!bs->currentEnemy)
04283         {
04284                 return;
04285         }
04286 
04287         if (bs->currentEnemy->client)
04288         {
04289                 VectorCopy(bs->currentEnemy->client->ps.origin, usethisvec);
04290         }
04291         else
04292         {
04293                 VectorCopy(bs->currentEnemy->s.origin, usethisvec);
04294         }
04295 
04296         if (bs->meleeStrafeTime < level.time)
04297         {
04298                 if (bs->meleeStrafeDir)
04299                 {
04300                         bs->meleeStrafeDir = 0;
04301                 }
04302                 else
04303                 {
04304                         bs->meleeStrafeDir = 1;
04305                 }
04306 
04307                 bs->meleeStrafeTime = level.time + Q_irand(500, 1800);
04308         }
04309 
04310         mins[0] = -15;
04311         mins[1] = -15;
04312         mins[2] = -24;
04313         maxs[0] = 15;
04314         maxs[1] = 15;
04315         maxs[2] = 32;
04316 
04317         VectorCopy(usethisvec, downvec);
04318         downvec[2] -= 4096;
04319 
04320         trap_Trace(&tr, usethisvec, mins, maxs, downvec, -1, MASK_SOLID);
04321 
04322         en_down = (int)tr.endpos[2];
04323 
04324         VectorCopy(bs->origin, downvec);
04325         downvec[2] -= 4096;
04326 
04327         trap_Trace(&tr, bs->origin, mins, maxs, downvec, -1, MASK_SOLID);
04328 
04329         me_down = (int)tr.endpos[2];
04330 
04331         VectorSubtract(usethisvec, bs->origin, a);
04332         vectoangles(a, a);
04333         AngleVectors(a, fwd, NULL, NULL);
04334 
04335         midorg[0] = bs->origin[0] + fwd[0]*bs->frame_Enemy_Len/2;
04336         midorg[1] = bs->origin[1] + fwd[1]*bs->frame_Enemy_Len/2;
04337         midorg[2] = bs->origin[2] + fwd[2]*bs->frame_Enemy_Len/2;
04338 
04339         VectorCopy(midorg, downvec);
04340         downvec[2] -= 4096;
04341 
04342         trap_Trace(&tr, midorg, mins, maxs, downvec, -1, MASK_SOLID);
04343 
04344         mid_down = (int)tr.endpos[2];
04345 
04346         if (me_down == en_down &&
04347                 en_down == mid_down)
04348         {
04349                 VectorCopy(usethisvec, bs->goalPosition);
04350         }
04351 }

void MoveTowardIdealAngles bot_state_t bs  ) 
 

Definition at line 1543 of file ai_main.c.

References bot_state_t, bot_state_s::goalAngles, bot_state_s::ideal_viewangles, and VectorCopy.

Referenced by StandardBotAI().

01544 {
01545         VectorCopy(bs->goalAngles, bs->ideal_viewangles);
01546 }

int NumBots void   ) 
 

Definition at line 416 of file ai_main.c.

References numbots.

00416                   {
00417         return numbots;
00418 }

int OrgVisible vec3_t  org1,
vec3_t  org2,
int  ignore
 

Definition at line 982 of file ai_main.c.

References trace_t::fraction, MASK_SOLID, NULL, trap_Trace(), and vec3_t.

Referenced by BotCheckDetPacks(), FindGenericEnemyIndex(), RepairPaths(), ScanForEnemies(), SeekerDroneUpdate(), and StandardBotAI().

00983 {
00984         trace_t tr;
00985 
00986         trap_Trace(&tr, org1, NULL, NULL, org2, ignore, MASK_SOLID);
00987 
00988         if (tr.fraction == 1)
00989         {
00990                 return 1;
00991         }
00992 
00993         return 0;
00994 }

int OrgVisibleBox vec3_t  org1,
vec3_t  mins,
vec3_t  maxs,
vec3_t  org2,
int  ignore
 

Definition at line 1029 of file ai_main.c.

References trace_t::allsolid, trace_t::fraction, g_RMG, vmCvar_t::integer, MASK_SOLID, NULL, trace_t::startsolid, trap_Trace(), and vec3_t.

Referenced by CalculatePaths(), GetNearestVisibleWP(), and GetNearestVisibleWPToItem().

01030 {
01031         trace_t tr;
01032 
01033         if (g_RMG.integer)
01034         {
01035                 trap_Trace(&tr, org1, NULL, NULL, org2, ignore, MASK_SOLID);
01036         }
01037         else
01038         {
01039                 trap_Trace(&tr, org1, mins, maxs, org2, ignore, MASK_SOLID);
01040         }
01041 
01042         if (tr.fraction == 1 && !tr.startsolid && !tr.allsolid)
01043         {
01044                 return 1;
01045         }
01046 
01047         return 0;
01048 }

int PassLovedOneCheck bot_state_t bs,
gentity_t ent
 

Definition at line 2078 of file ai_main.c.

References bot_attachments, bot_state_t, botstates, bot_state_s::client, level_locals_t::clients, g_entities, g_gametype, gentity_t, GT_DUEL, GT_POWERDUEL, vmCvar_t::integer, IsTeamplay(), botattachment_s::level, level, bot_state_s::loved, bot_state_s::lovednum, botattachment_s::name, entityState_s::number, OnSameTeam(), gentity_s::s, and strcmp().

Referenced by BotDamageNotification(), BotLovedOneDied(), GetNearestBadThing(), ScanForEnemies(), and StandardBotAI().

02079 {
02080         int i;
02081         bot_state_t *loved;
02082 
02083         if (!bs->lovednum)
02084         {
02085                 return 1;
02086         }
02087 
02088         if (g_gametype.integer == GT_DUEL || g_gametype.integer == GT_POWERDUEL)
02089         { //There is no love in 1-on-1
02090                 return 1;
02091         }
02092 
02093         i = 0;
02094 
02095         if (!botstates[ent->s.number])
02096         { //not a bot
02097                 return 1;
02098         }
02099 
02100         if (!bot_attachments.integer)
02101         {
02102                 return 1;
02103         }
02104 
02105         loved = botstates[ent->s.number];
02106 
02107         while (i < bs->lovednum)
02108         {
02109                 if (strcmp(level.clients[loved->client].pers.netname, bs->loved[i].name) == 0)
02110                 {
02111                         if (!IsTeamplay() && bs->loved[i].level < 2)
02112                         { //if FFA and level of love is not greater than 1, just don't care
02113                                 return 1;
02114                         }
02115                         else if (IsTeamplay() && !OnSameTeam(&g_entities[bs->client], &g_entities[loved->client]) && bs->loved[i].level < 2)
02116                         { //is teamplay, but not on same team and level < 2
02117                                 return 1;
02118                         }
02119                         else
02120                         {
02121                                 return 0;
02122                         }
02123                 }
02124 
02125                 i++;
02126         }
02127 
02128         return 1;
02129 }

int PassStandardEnemyChecks bot_state_t bs,
gentity_t en
 

Definition at line 1753 of file ai_main.c.

References bot_state_t, BotMindTricked(), bot_state_s::client, gentity_s::client, clientPersistant_t::connected, bot_state_s::cur_ps, bot_state_s::currentEnemy, bot_state_s::doingFallback, playerState_s::duelIndex, playerState_s::duelInProgress, g_entities, g_friendlyFire, g_gametype, gentity_t, gLevelFlags, GT_JEDIMASTER, gentity_s::health, vmCvar_t::integer, playerState_s::isJediMaster, LEVELFLAG_IGNOREINFALLBACK, entityState_s::number, OnSameTeam(), playerState_s::origin, bot_state_s::origin, gclient_s::pers, PM_INTERMISSION, PM_SPECTATOR, playerState_s::pm_type, gclient_s::ps, gentity_s::s, gclient_s::sess, clientSession_t::sessionTeam, entityState_s::solid, gentity_s::takedamage, TEAM_SPECTATOR, vec3_t, and VectorSubtract.

Referenced by BotDamageNotification(), GetNearestBadThing(), ScanForEnemies(), and StandardBotAI().

01754 {
01755         if (!bs || !en)
01756         { //shouldn't happen
01757                 return 0;
01758         }
01759 
01760         if (!en->client)
01761         { //not a client, don't care about him
01762                 return 0;
01763         }
01764 
01765         if (en->health < 1)
01766         { //he's already dead
01767                 return 0;
01768         }
01769 
01770         if (!en->takedamage)
01771         { //a client that can't take damage?
01772                 return 0;
01773         }
01774 
01775         if (bs->doingFallback &&
01776                 (gLevelFlags & LEVELFLAG_IGNOREINFALLBACK))
01777         { //we screwed up in our nav routines somewhere and we've reverted to a fallback state to
01778                 //try to get back on the trail. If the level specifies to ignore enemies in this state,
01779                 //then ignore them.
01780                 return 0;
01781         }
01782 
01783         if (en->client->ps.pm_type == PM_INTERMISSION ||
01784                 en->client->ps.pm_type == PM_SPECTATOR ||
01785                 en->client->sess.sessionTeam == TEAM_SPECTATOR)
01786         { //don't attack spectators
01787                 return 0;
01788         }
01789 
01790         if (!en->client->pers.connected)
01791         { //a "zombie" client?
01792                 return 0;
01793         }
01794 
01795         if (!en->s.solid)
01796         { //shouldn't happen
01797                 return 0;
01798         }
01799 
01800         if (bs->client == en->s.number)
01801         { //don't attack yourself
01802                 return 0;
01803         }
01804 
01805         if (OnSameTeam(&g_entities[bs->client], en))
01806         { //don't attack teammates
01807                 return 0;
01808         }
01809 
01810         if (BotMindTricked(bs->client, en->s.number))
01811         {
01812                 if (bs->currentEnemy && bs->currentEnemy->s.number == en->s.number)
01813                 { //if mindtricked by this enemy, then be less "aware" of them, even though
01814                         //we know they're there.
01815                         vec3_t vs;
01816                         float vLen = 0;
01817 
01818                         VectorSubtract(bs->origin, en->client->ps.origin, vs);
01819                         vLen = VectorLength(vs);
01820 
01821                         if (vLen > 64 /*&& (level.time - en->client->dangerTime) > 150*/)
01822                         {
01823                                 return 0;
01824                         }
01825                 }
01826         }
01827 
01828         if (en->client->ps.duelInProgress && en->client->ps.duelIndex != bs->client)
01829         { //don't attack duelists unless you're dueling them
01830                 return 0;
01831         }
01832 
01833         if (bs->cur_ps.duelInProgress && en->s.number != bs->cur_ps.duelIndex)
01834         { //ditto, the other way around
01835                 return 0;
01836         }
01837 
01838         if (g_gametype.integer == GT_JEDIMASTER && !en->client->ps.isJediMaster && !bs->cur_ps.isJediMaster)
01839         { //rules for attacking non-JM in JM mode
01840                 vec3_t vs;
01841                 float vLen = 0;
01842 
01843                 if (!g_friendlyFire.integer)
01844                 { //can't harm non-JM in JM mode if FF is off
01845                         return 0;
01846                 }
01847 
01848                 VectorSubtract(bs->origin, en->client->ps.origin, vs);
01849                 vLen = VectorLength(vs);
01850 
01851                 if (vLen > 350)
01852                 {
01853                         return 0;
01854                 }
01855         }
01856 
01857         return 1;
01858 }

int PassWayCheck bot_state_t bs,
int  windex
 

Definition at line 1157 of file ai_main.c.

References bot_state_t, bot_state_s::cur_ps, playerState_s::fd, wpobject_s::flags, wpobject_s::forceJumpTo, forcedata_s::forcePowerLevel, FP_LEVITATION, g_RMG, gWPArray, vmCvar_t::integer, wpobject_s::inuse, wpobject_s::origin, bot_state_s::wpCurrent, bot_state_s::wpDirection, WPFLAG_BLUE_FLAG, WPFLAG_ONEWAY_BACK, WPFLAG_ONEWAY_FWD, and WPFLAG_RED_FLAG.

Referenced by StandardBotAI().

01158 {
01159         if (!gWPArray[windex] || !gWPArray[windex]->inuse)
01160         { //bad point index
01161                 return 0;
01162         }
01163 
01164         if (g_RMG.integer)
01165         {
01166                 if ((gWPArray[windex]->flags & WPFLAG_RED_FLAG) ||
01167                         (gWPArray[windex]->flags & WPFLAG_BLUE_FLAG))
01168                 { //red or blue flag, we'd like to get here
01169                         return 1;
01170                 }
01171         }
01172 
01173         if (bs->wpDirection && (gWPArray[windex]->flags & WPFLAG_ONEWAY_FWD))
01174         { //we're not travelling in a direction on the trail that will allow us to pass this point
01175                 return 0;
01176         }
01177         else if (!bs->wpDirection && (gWPArray[windex]->flags & WPFLAG_ONEWAY_BACK))
01178         { //we're not travelling in a direction on the trail that will allow us to pass this point
01179                 return 0;
01180         }
01181 
01182         if (bs->wpCurrent && gWPArray[windex]->forceJumpTo &&
01183                 gWPArray[windex]->origin[2] > (bs->wpCurrent->origin[2]+64) &&
01184                 bs->cur_ps.fd.forcePowerLevel[FP_LEVITATION] < gWPArray[windex]->forceJumpTo)
01185         { //waypoint requires force jump level greater than our current one to pass
01186                 return 0;
01187         }
01188 
01189         return 1;
01190 }

int PlayersInGame void   ) 
 

Definition at line 793 of file ai_main.c.

References gentity_s::client, CON_CONNECTED, clientPersistant_t::connected, g_entities, gentity_t, MAX_CLIENTS, and gclient_s::pers.

Referenced by BotAISetupClient().

00794 {
00795         int i = 0;
00796         gentity_t *ent;
00797         int pl = 0;
00798 
00799         while (i < MAX_CLIENTS)
00800         {
00801                 ent = &g_entities[i];
00802 
00803                 if (ent && ent->client && ent->client->pers.connected == CON_CONNECTED)
00804                 {
00805                         pl++;
00806                 }
00807 
00808                 i++;
00809         }
00810 
00811         return pl;
00812 }

int PrimFiring bot_state_t bs  ) 
 

Definition at line 5466 of file ai_main.c.

References bot_state_t, bot_state_s::cur_ps, bot_state_s::doAttack, WEAPON_CHARGING, and playerState_s::weaponstate.

Referenced by StandardBotAI().

05467 {
05468         if (bs->cur_ps.weaponstate != WEAPON_CHARGING &&
05469                 bs->doAttack)
05470         {
05471                 return 1;
05472         }
05473 
05474         if (bs->cur_ps.weaponstate == WEAPON_CHARGING &&
05475                 !bs->doAttack)
05476         {
05477                 return 1;
05478         }
05479 
05480         return 0;
05481 }

void RemoveColorEscapeSequences char *  text  ) 
 

Definition at line 674 of file ai_main.c.

References Q_IsColorString.

Referenced by BotAI().

00674                                               {
00675         int i, l;
00676 
00677         l = 0;
00678         for ( i = 0; text[i]; i++ ) {
00679                 if (Q_IsColorString(&text[i])) {
00680                         i++;
00681                         continue;
00682                 }
00683                 if (text[i] > 0x7E)
00684                         continue;
00685                 text[l++] = text[i];
00686         }
00687         text[l] = '\0';
00688 }

void SaberCombatHandling bot_state_t bs  ) 
 

Definition at line 4354 of file ai_main.c.

References trace_t::allsolid, AngleVectors(), bot_state_s::beStill, BG_SaberInKata(), BG_SaberInSpecial(), bot_state_t, bot_state_s::client, gentity_s::client, bot_state_s::currentEnemy, bot_state_s::doAttack, trace_t::endpos, ENTITYNUM_NONE, trace_t::fraction, bot_state_s::frame_Enemy_Len, bot_state_s::goalPosition, playerState_s::groundEntityNum, bot_state_s::jumpTime, level, LS_SPINATTACK, LS_SPINATTACK_DUAL, MASK_SOLID, bot_state_s::meleeStrafeDir, bot_state_s::meleeStrafeTime, NULL, bot_state_s::origin, entityState_s::origin, playerState_s::origin, gclient_s::ps, Q_irand(), gentity_s::s, bot_state_s::saberBFTime, bot_state_s::saberBTime, bot_state_s::saberDefendDecideTime, bot_state_s::saberDefending, playerState_s::saberMove, bot_state_s::saberSTime, trace_t::startsolid, level_locals_t::time, trap_Trace(), vec3_t, vectoangles(), VectorCopy, VectorNormalize(), VectorSubtract, playerState_s::weapon, and WP_SABER.

Referenced by StandardBotAI().

04355 {
04356         vec3_t usethisvec;
04357         vec3_t downvec;
04358         vec3_t midorg;
04359         vec3_t a;
04360         vec3_t fwd;
04361         vec3_t mins, maxs;
04362         trace_t tr;
04363         int en_down;
04364         int me_down;
04365         int mid_down;
04366 
04367         if (!bs->currentEnemy)
04368         {
04369                 return;
04370         }
04371 
04372         if (bs->currentEnemy->client)
04373         {
04374                 VectorCopy(bs->currentEnemy->client->ps.origin, usethisvec);
04375         }
04376         else
04377         {
04378                 VectorCopy(bs->currentEnemy->s.origin, usethisvec);
04379         }
04380 
04381         if (bs->meleeStrafeTime < level.time)
04382         {
04383                 if (bs->meleeStrafeDir)
04384                 {
04385                         bs->meleeStrafeDir = 0;
04386                 }
04387                 else
04388                 {
04389                         bs->meleeStrafeDir = 1;
04390                 }
04391 
04392                 bs->meleeStrafeTime = level.time + Q_irand(500, 1800);
04393         }
04394 
04395         mins[0] = -15;
04396         mins[1] = -15;
04397         mins[2] = -24;
04398         maxs[0] = 15;
04399         maxs[1] = 15;
04400         maxs[2] = 32;
04401 
04402         VectorCopy(usethisvec, downvec);
04403         downvec[2] -= 4096;
04404 
04405         trap_Trace(&tr, usethisvec, mins, maxs, downvec, -1, MASK_SOLID);
04406 
04407         en_down = (int)tr.endpos[2];
04408 
04409         if (tr.startsolid || tr.allsolid)
04410         {
04411                 en_down = 1;
04412                 me_down = 2;
04413         }
04414         else
04415         {
04416                 VectorCopy(bs->origin, downvec);
04417                 downvec[2] -= 4096;
04418 
04419                 trap_Trace(&tr, bs->origin, mins, maxs, downvec, -1, MASK_SOLID);
04420 
04421                 me_down = (int)tr.endpos[2];
04422 
04423                 if (tr.startsolid || tr.allsolid)
04424                 {
04425                         en_down = 1;
04426                         me_down = 2;
04427                 }
04428         }
04429 
04430         VectorSubtract(usethisvec, bs->origin, a);
04431         vectoangles(a, a);
04432         AngleVectors(a, fwd, NULL, NULL);
04433 
04434         midorg[0] = bs->origin[0] + fwd[0]*bs->frame_Enemy_Len/2;
04435         midorg[1] = bs->origin[1] + fwd[1]*bs->frame_Enemy_Len/2;
04436         midorg[2] = bs->origin[2] + fwd[2]*bs->frame_Enemy_Len/2;
04437 
04438         VectorCopy(midorg, downvec);
04439         downvec[2] -= 4096;
04440 
04441         trap_Trace(&tr, midorg, mins, maxs, downvec, -1, MASK_SOLID);
04442 
04443         mid_down = (int)tr.endpos[2];
04444 
04445         if (me_down == en_down &&
04446                 en_down == mid_down)
04447         {
04448                 if (usethisvec[2] > (bs->origin[2]+32) &&
04449                         bs->currentEnemy->client &&
04450                         bs->currentEnemy->client->ps.groundEntityNum == ENTITYNUM_NONE)
04451                 {
04452                         bs->jumpTime = level.time + 100;
04453                 }
04454 
04455                 if (bs->frame_Enemy_Len > 128)
04456                 { //be ready to attack
04457                         bs->saberDefending = 0;
04458                         bs->saberDefendDecideTime = level.time + Q_irand(1000, 2000);
04459                 }
04460                 else
04461                 {
04462                         if (bs->saberDefendDecideTime < level.time)
04463                         {
04464                                 if (bs->saberDefending)
04465                                 {
04466                                         bs->saberDefending = 0;
04467                                 }
04468                                 else
04469                                 {
04470                                         bs->saberDefending = 1;
04471                                 }
04472 
04473                                 bs->saberDefendDecideTime = level.time + Q_irand(500, 2000);
04474                         }
04475                 }
04476 
04477                 if (bs->frame_Enemy_Len < 54)
04478                 {
04479                         VectorCopy(bs->origin, bs->goalPosition);
04480                         bs->saberBFTime = 0;
04481                 }
04482                 else
04483                 {
04484                         VectorCopy(usethisvec, bs->goalPosition);
04485                 }
04486 
04487                 if (bs->currentEnemy && bs->currentEnemy->client)
04488                 {
04489                         if (!BG_SaberInSpecial(bs->currentEnemy->client->ps.saberMove) && bs->frame_Enemy_Len > 90 && bs->saberBFTime > level.time && bs->saberBTime > level.time && bs->beStill < level.time && bs->saberSTime < level.time)
04490                         {
04491                                 bs->beStill = level.time + Q_irand(500, 1000);
04492                                 bs->saberSTime = level.time + Q_irand(1200, 1800);
04493                         }
04494                         else if (bs->currentEnemy->client->ps.weapon == WP_SABER && bs->frame_Enemy_Len < 80 && (Q_irand(1, 10) < 8 && bs->saberBFTime < level.time) || bs->saberBTime > level.time || BG_SaberInKata(bs->currentEnemy->client->ps.saberMove) || bs->currentEnemy->client->ps.saberMove == LS_SPINATTACK || bs->currentEnemy->client->ps.saberMove == LS_SPINATTACK_DUAL)
04495                         {
04496                                 vec3_t vs;
04497                                 vec3_t groundcheck;
04498                                 int idealDist;
04499                                 int checkIncr = 0;
04500 
04501                                 VectorSubtract(bs->origin, usethisvec, vs);
04502                                 VectorNormalize(vs);
04503 
04504                                 if (BG_SaberInKata(bs->currentEnemy->client->ps.saberMove) || bs->currentEnemy->client->ps.saberMove == LS_SPINATTACK || bs->currentEnemy->client->ps.saberMove == LS_SPINATTACK_DUAL)
04505                                 {
04506                                         idealDist = 256;
04507                                 }
04508                                 else
04509                                 {
04510                                         idealDist = 64;
04511                                 }
04512 
04513                                 while (checkIncr < idealDist)
04514                                 {
04515                                         bs->goalPosition[0] = bs->origin[0] + vs[0]*checkIncr;
04516                                         bs->goalPosition[1] = bs->origin[1] + vs[1]*checkIncr;
04517                                         bs->goalPosition[2] = bs->origin[2] + vs[2]*checkIncr;
04518 
04519                                         if (bs->saberBTime < level.time)
04520                                         {
04521                                                 bs->saberBFTime = level.time + Q_irand(900, 1300);
04522                                                 bs->saberBTime = level.time + Q_irand(300, 700);
04523                                         }
04524 
04525                                         VectorCopy(bs->goalPosition, groundcheck);
04526 
04527                                         groundcheck[2] -= 64;
04528 
04529                                         trap_Trace(&tr, bs->goalPosition, NULL, NULL, groundcheck, bs->client, MASK_SOLID);
04530                                         
04531                                         if (tr.fraction == 1.0f)
04532                                         { //don't back off of a ledge
04533                                                 VectorCopy(usethisvec, bs->goalPosition);
04534                                                 break;
04535                                         }
04536                                         checkIncr += 64;
04537                                 }
04538                         }
04539                         else if (bs->currentEnemy->client->ps.weapon == WP_SABER && bs->frame_Enemy_Len >= 75)
04540                         {
04541                                 bs->saberBFTime = level.time + Q_irand(700, 1300);
04542                                 bs->saberBTime = 0;
04543                         }
04544                 }
04545 
04546                 /*AngleVectors(bs->viewangles, NULL, fwd, NULL);
04547 
04548                 if (bs->meleeStrafeDir)
04549                 {
04550                         bs->goalPosition[0] += fwd[0]*16;
04551                         bs->goalPosition[1] += fwd[1]*16;
04552                         bs->goalPosition[2] += fwd[2]*16;
04553                 }
04554                 else
04555                 {
04556                         bs->goalPosition[0] -= fwd[0]*16;
04557                         bs->goalPosition[1] -= fwd[1]*16;
04558                         bs->goalPosition[2] -= fwd[2]*16;
04559                 }*/
04560         }
04561         else if (bs->frame_Enemy_Len <= 56)
04562         {
04563                 bs->doAttack = 1;
04564                 bs->saberDefending = 0;
04565         }
04566 }

int ScanForEnemies bot_state_t bs  ) 
 

Definition at line 2134 of file ai_main.c.

References bot_state_t, BotCanHear(), BotMindTricked(), BotPVSCheck(), client, bot_state_s::client, gentity_s::client, bot_state_s::cur_ps, bot_state_s::currentEnemy, gclient_s::dangerTime, bot_state_s::eye, bot_state_s::frame_Enemy_Len, g_entities, g_friendlyFire, g_gametype, G_ThereIsAMaster(), GT_JEDIMASTER, InFieldOfVision(), vmCvar_t::integer, playerState_s::isJediMaster, level, MAX_CLIENTS, OnSameTeam(), OrgVisible(), playerState_s::origin, PassLovedOneCheck(), PassStandardEnemyChecks(), gclient_s::ps, qboolean, qfalse, qtrue, level_locals_t::time, vec3_t, vectoangles(), VectorSubtract, and bot_state_s::viewangles.

Referenced by StandardBotAI().

02135 {
02136         vec3_t a;
02137         float distcheck;
02138         float closest;
02139         int bestindex;
02140         int i;
02141         float hasEnemyDist = 0;
02142         qboolean noAttackNonJM = qfalse;
02143 
02144         closest = 999999;
02145         i = 0;
02146         bestindex = -1;
02147 
02148         if (bs->currentEnemy)
02149         { //only switch to a new enemy if he's significantly closer
02150                 hasEnemyDist = bs->frame_Enemy_Len;
02151         }
02152 
02153         if (bs->currentEnemy && bs->currentEnemy->client &&
02154                 bs->currentEnemy->client->ps.isJediMaster)
02155         { //The Jedi Master must die.
02156                 return -1;
02157         }
02158 
02159         if (g_gametype.integer == GT_JEDIMASTER)
02160         {
02161                 if (G_ThereIsAMaster() && !bs->cur_ps.isJediMaster)
02162                 { //if friendly fire is on in jedi master we can attack people that bug us
02163                         if (!g_friendlyFire.integer)
02164                         {
02165                                 noAttackNonJM = qtrue;
02166                         }
02167                         else
02168                         {
02169                                 closest = 128; //only get mad at people if they get close enough to you to anger you, or hurt you
02170                         }
02171                 }
02172         }
02173 
02174         while (i <= MAX_CLIENTS)
02175         {
02176                 if (i != bs->client && g_entities[i].client && !OnSameTeam(&g_entities[bs->client], &g_entities[i]) && PassStandardEnemyChecks(bs, &g_entities[i]) && BotPVSCheck(g_entities[i].client->ps.origin, bs->eye) && PassLovedOneCheck(bs, &g_entities[i]))
02177                 {
02178                         VectorSubtract(g_entities[i].client->ps.origin, bs->eye, a);
02179                         distcheck = VectorLength(a);
02180                         vectoangles(a, a);
02181 
02182                         if (g_entities[i].client->ps.isJediMaster)
02183                         { //make us think the Jedi Master is close so we'll attack him above all
02184                                 distcheck = 1;
02185                         }
02186 
02187                         if (distcheck < closest && ((InFieldOfVision(bs->viewangles, 90, a) && !BotMindTricked(bs->client, i)) || BotCanHear(bs, &g_entities[i], distcheck)) && OrgVisible(bs->eye, g_entities[i].client->ps.origin, -1))
02188                         {
02189                                 if (BotMindTricked(bs->client, i))
02190                                 {
02191                                         if (distcheck < 256 || (level.time - g_entities[i].client->dangerTime) < 100)
02192                                         {
02193                                                 if (!hasEnemyDist || distcheck < (hasEnemyDist - 128))
02194                                                 { //if we have an enemy, only switch to closer if he is 128+ closer to avoid flipping out
02195                                                         if (!noAttackNonJM || g_entities[i].client->ps.isJediMaster)
02196                                                         {
02197                                                                 closest = distcheck;
02198                                                                 bestindex = i;
02199                                                         }
02200                                                 }
02201                                         }
02202                                 }
02203                                 else
02204                                 {
02205                                         if (!hasEnemyDist || distcheck < (hasEnemyDist - 128))
02206                                         { //if we have an enemy, only switch to closer if he is 128+ closer to avoid flipping out
02207                                                 if (!noAttackNonJM || g_entities[i].client->ps.isJediMaster)
02208                                                 {
02209                                                         closest = distcheck;
02210                                                         bestindex = i;
02211                                                 }
02212                                         }
02213                                 }
02214                         }
02215                 }
02216                 i++;
02217         }
02218         
02219         return bestindex;
02220 }

int ShouldSecondaryFire bot_state_t bs  ) 
 

Definition at line 4804 of file ai_main.c.

References bot_state_s::altChargeTime, weaponData_s::altEnergyPerShot, playerState_s::ammo, weaponData_s::ammoIndex, bot_state_t, bot_state_s::cur_ps, bot_state_s::frame_Enemy_Len, level, playerState_s::rocketLastValidTime, playerState_s::rocketLockTime, level_locals_t::time, playerState_s::weapon, WEAPON_CHARGING_ALT, playerState_s::weaponChargeTime, weaponData, playerState_s::weaponstate, WP_BLASTER, WP_BOWCASTER, WP_BRYAR_PISTOL, WP_REPEATER, and WP_ROCKET_LAUNCHER.

Referenced by CombatBotAI().

04805 {
04806         int weap;
04807         int dif;
04808         float rTime;
04809 
04810         weap = bs->cur_ps.weapon;
04811 
04812         if (bs->cur_ps.ammo[weaponData[weap].ammoIndex] < weaponData[weap].altEnergyPerShot)
04813         {
04814                 return 0;
04815         }
04816 
04817         if (bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT && bs->cur_ps.weapon == WP_ROCKET_LAUNCHER)
04818         {
04819                 float heldTime = (level.time - bs->cur_ps.weaponChargeTime);
04820 
04821                 rTime = bs->cur_ps.rocketLockTime;
04822 
04823                 if (rTime < 1)
04824                 {
04825                         rTime = bs->cur_ps.rocketLastValidTime;
04826                 }
04827 
04828                 if (heldTime > 5000)
04829                 { //just give up and release it if we can't manage a lock in 5 seconds
04830                         return 2;
04831                 }
04832 
04833                 if (rTime > 0)
04834                 {
04835                         dif = ( level.time - rTime ) / ( 1200.0f / 16.0f );
04836                         
04837                         if (dif >= 10)
04838                         {
04839                                 return 2;
04840                         }
04841                         else if (bs->frame_Enemy_Len > 250)
04842                         {
04843                                 return 1;
04844                         }
04845                 }
04846                 else if (bs->frame_Enemy_Len > 250)
04847                 {
04848                         return 1;
04849                 }
04850         }
04851         else if ((bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT) && (level.time - bs->cur_ps.weaponChargeTime) > bs->altChargeTime)
04852         {
04853                 return 2;
04854         }
04855         else if (bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT)
04856         {
04857                 return 1;
04858         }
04859 
04860         if (weap == WP_BRYAR_PISTOL && bs->frame_Enemy_Len < 300)
04861         {
04862                 return 1;
04863         }
04864         else if (weap == WP_BOWCASTER && bs->frame_Enemy_Len > 300)
04865         {
04866                 return 1;
04867         }
04868         else if (weap == WP_REPEATER && bs->frame_Enemy_Len < 600 && bs->frame_Enemy_Len > 250)
04869         {
04870                 return 1;
04871         }
04872         else if (weap == WP_BLASTER && bs->frame_Enemy_Len < 300)
04873         {
04874                 return 1;
04875         }
04876         else if (weap == WP_ROCKET_LAUNCHER && bs->frame_Enemy_Len > 250)
04877         {
04878                 return 1;
04879         }
04880 
04881         return 0;
04882 }

int Siege_CountDefenders bot_state_t bs  ) 
 

Definition at line 3202 of file ai_main.c.

References bot_state_t, botstates, bot_state_s::client, gentity_s::client, g_entities, gentity_t, MAX_CLIENTS, gclient_s::sess, clientSession_t::sessionTeam, bot_state_s::siegeState, and SIEGESTATE_DEFENDER.

Referenced by SiegeTakesPriority().

03203 {
03204         int i = 0;
03205         int num = 0;
03206         gentity_t *ent;
03207         bot_state_t *bot;
03208 
03209         while (i < MAX_CLIENTS)
03210         {
03211                 ent = &g_entities[i];
03212                 bot = botstates[i];
03213 
03214                 if (ent && ent->client && bot)
03215                 {
03216                         if (bot->siegeState == SIEGESTATE_DEFENDER &&
03217                                 ent->client->sess.sessionTeam == g_entities[bs->client].client->sess.sessionTeam)
03218                         {
03219                                 num++;
03220                         }
03221                 }
03222 
03223                 i++;
03224         }
03225 
03226         return num;
03227 }

int Siege_CountTeammates bot_state_t bs  ) 
 

Definition at line 3230 of file ai_main.c.

References bot_state_t, bot_state_s::client, gentity_s::client, g_entities, gentity_t, MAX_CLIENTS, gclient_s::sess, and clientSession_t::sessionTeam.

Referenced by SiegeTakesPriority().

03231 {
03232         int i = 0;
03233         int num = 0;
03234         gentity_t *ent;
03235 
03236         while (i < MAX_CLIENTS)
03237         {
03238                 ent = &g_entities[i];
03239 
03240                 if (ent && ent->client)
03241                 {
03242                         if (ent->client->sess.sessionTeam == g_entities[bs->client].client->sess.sessionTeam)
03243                         {
03244                                 num++;
03245                         }
03246                 }
03247 
03248                 i++;
03249         }
03250 
03251         return num;
03252 }

void Siege_DefendFromAttackers bot_state_t bs  ) 
 

Definition at line 3154 of file ai_main.c.

References bot_state_t, client, bot_state_s::client, gentity_s::client, bot_state_s::destinationGrabTime, g_entities, gentity_t, GetNearestVisibleWP(), gWPArray, gentity_s::health, wpobject_s::inuse, level, MAX_CLIENTS, bot_state_s::origin, playerState_s::origin, gclient_s::ps, gclient_s::sess, clientSession_t::sessionTeam, TEAM_SPECTATOR, level_locals_t::time, vec3_t, VectorSubtract, and bot_state_s::wpDestination.

Referenced by SiegeTakesPriority().

03155 { //this may be a little cheap, but the best way to find our defending point is probably
03156   //to just find the nearest person on the opposing team since they'll most likely
03157   //be on offense in this situation
03158         int wpClose = -1;
03159         int i = 0;
03160         float testdist = 999999;
03161         int bestindex = -1;
03162         float bestdist = 999999;
03163         gentity_t *ent;
03164         vec3_t a;
03165 
03166         while (i < MAX_CLIENTS)
03167         {
03168                 ent = &g_entities[i];
03169 
03170                 if (ent && ent->client && ent->client->sess.sessionTeam != g_entities[bs->client].client->sess.sessionTeam &&
03171                         ent->health > 0 && ent->client->sess.sessionTeam != TEAM_SPECTATOR)
03172                 {
03173                         VectorSubtract(ent->client->ps.origin, bs->origin, a);
03174 
03175                         testdist = VectorLength(a);
03176 
03177                         if (testdist < bestdist)
03178                         {
03179                                 bestindex = i;
03180                                 bestdist = testdist;
03181                         }
03182                 }
03183 
03184                 i++;
03185         }
03186 
03187         if (bestindex == -1)
03188         {
03189                 return;
03190         }
03191 
03192         wpClose = GetNearestVisibleWP(g_entities[bestindex].client->ps.origin, -1);     
03193 
03194         if (wpClose != -1 && gWPArray[wpClose] && gWPArray[wpClose]->inuse)
03195         {
03196                 bs->wpDestination = gWPArray[wpClose];
03197                 bs->destinationGrabTime = level.time + 10000;
03198         }
03199 }

int Siege_TargetClosestObjective bot_state_t bs,
int  flag
 

Definition at line 3055 of file ai_main.c.

References entityShared_t::absmax, entityShared_t::absmin, wpobject_s::associated_entity, BOT_MIN_SIEGE_GOAL_SHOOT, BOT_MIN_SIEGE_GOAL_TRAVEL, bot_state_t, BotGetWeaponRange(), BWEAPONRANGE_MELEE, BWEAPONRANGE_SABER, bot_state_s::client, ENTITYNUM_NONE, EntityVisibleBox(), wpobject_s::flags, g_entities, gentity_t, bot_state_s::goalPosition, gWPArray, gWPNum, wpobject_s::inuse, NULL, entityState_s::number, wpobject_s::origin, bot_state_s::origin, gentity_s::r, gentity_s::s, bot_state_s::shootGoal, gentity_s::takedamage, bot_state_s::touchGoal, gentity_s::use, vec3_t, VectorCopy, VectorSubtract, and bot_state_s::wpDestination.

Referenced by SiegeTakesPriority().

03056 {
03057         int i = 0;
03058         int bestindex = -1;
03059         float testdistance = 0;
03060         float bestdistance = 999999999;
03061         gentity_t *goalent;
03062         vec3_t a, dif;
03063         vec3_t mins, maxs;
03064 
03065         mins[0] = -1;
03066         mins[1] = -1;
03067         mins[2] = -1;
03068 
03069         maxs[0] = 1;
03070         maxs[1] = 1;
03071         maxs[2] = 1;
03072 
03073         if ( bs->wpDestination && (bs->wpDestination->flags & flag) && bs->wpDestination->associated_entity != ENTITYNUM_NONE &&
03074                  &g_entities[bs->wpDestination->associated_entity] && g_entities[bs->wpDestination->associated_entity].use )
03075         {
03076                 goto hasPoint;
03077         }
03078 
03079         while (i < gWPNum)
03080         {
03081                 if ( gWPArray[i] && gWPArray[i]->inuse && (gWPArray[i]->flags & flag) && gWPArray[i]->associated_entity != ENTITYNUM_NONE &&
03082                          &g_entities[gWPArray[i]->associated_entity] && g_entities[gWPArray[i]->associated_entity].use )
03083                 {
03084                         VectorSubtract(gWPArray[i]->origin, bs->origin, a);
03085                         testdistance = VectorLength(a);
03086 
03087                         if (testdistance < bestdistance)
03088                         {
03089                                 bestdistance = testdistance;
03090                                 bestindex = i;
03091                         }
03092                 }
03093 
03094                 i++;
03095         }
03096 
03097         if (bestindex != -1)
03098         {
03099                 bs->wpDestination = gWPArray[bestindex];
03100         }
03101         else
03102         {
03103                 return 0;
03104         }
03105 hasPoint:
03106         goalent = &g_entities[bs->wpDestination->associated_entity];
03107 
03108         if (!goalent)
03109         {
03110                 return 0;
03111         }
03112 
03113         VectorSubtract(bs->origin, bs->wpDestination->origin, a);
03114 
03115         testdistance = VectorLength(a);
03116 
03117         dif[0] = (goalent->r.absmax[0]+goalent->r.absmin[0])/2;
03118         dif[1] = (goalent->r.absmax[1]+goalent->r.absmin[1])/2;
03119         dif[2] = (goalent->r.absmax[2]+goalent->r.absmin[2])/2;
03120         //brush models can have tricky origins, so this is our hacky method of getting the center point
03121 
03122         if (goalent->takedamage && testdistance < BOT_MIN_SIEGE_GOAL_SHOOT &&
03123                 EntityVisibleBox(bs->origin, mins, maxs, dif, bs->client, goalent->s.number))
03124         {
03125                 bs->shootGoal = goalent;
03126                 bs->touchGoal = NULL;
03127         }
03128         else if (goalent->use && testdistance < BOT_MIN_SIEGE_GOAL_TRAVEL)
03129         {
03130                 bs->shootGoal = NULL;
03131                 bs->touchGoal = goalent;
03132         }
03133         else
03134         { //don't know how to handle this goal object!
03135                 bs->shootGoal = NULL;
03136                 bs->touchGoal = NULL;
03137         }
03138 
03139         if (BotGetWeaponRange(bs) == BWEAPONRANGE_MELEE ||
03140                 BotGetWeaponRange(bs) == BWEAPONRANGE_SABER)
03141         {
03142                 bs->shootGoal = NULL; //too risky
03143         }
03144 
03145         if (bs->touchGoal)
03146         {
03147                 //G_Printf("Please, master, let me touch it!\n");
03148                 VectorCopy(dif, bs->goalPosition);
03149         }
03150 
03151         return 1;
03152 }

int SiegeTakesPriority bot_state_t bs  ) 
 

Definition at line 3256 of file ai_main.c.

References entityShared_t::absmax, entityShared_t::absmin, BOT_MAX_WEAPON_CHASE_TIME, BOT_MAX_WEAPON_GATHER_TIME, bot_state_t, BotPVSCheck(), bot_state_s::client, bot_state_s::cur_ps, trace_t::entityNum, trace_t::fraction, g_entities, g_gametype, gclient_t, GetBestIdleGoal(), GT_SIEGE, gWPArray, imperial_attackers, vmCvar_t::integer, wpobject_s::inuse, bot_state_s::lastDeadTime, level, MASK_SOLID, NULL, entityState_s::number, bot_state_s::origin, gentity_s::r, rebel_attackers, gentity_s::s, gclient_s::sess, clientSession_t::sessionTeam, bot_state_s::shootGoal, Siege_CountDefenders(), Siege_CountTeammates(), Siege_DefendFromAttackers(), Siege_TargetClosestObjective(), bot_state_s::siegeState, SIEGESTATE_ATTACKER, SIEGESTATE_DEFENDER, SIEGETEAM_TEAM1, bot_state_s::state_Forced, level_locals_t::time, trap_Trace(), vec3_t, playerState_s::weapon, wpobject_s::weight, WP_BRYAR_PISTOL, bot_state_s::wpDestination, bot_state_s::wpDestSwitchTime, WPFLAG_SIEGE_IMPERIALOBJ, WPFLAG_SIEGE_REBELOBJ, and wpobject_t.

Referenced by GetIdealDestination().

03257 {
03258         int attacker;
03259         int flagForDefendableObjective;
03260         int flagForAttackableObjective;
03261         int defenders, teammates;
03262         int idleWP;
03263         wpobject_t *dest_sw = NULL;
03264         int dosw = 0;
03265         gclient_t *bcl;
03266         vec3_t dif;
03267         trace_t tr;
03268 
03269         if (g_gametype.integer != GT_SIEGE)
03270         {
03271                 return 0;
03272         }
03273 
03274         bcl = g_entities[bs->client].client;
03275 
03276         if (!bcl)
03277         {
03278                 return 0;
03279         }
03280 
03281         if (bs->cur_ps.weapon == WP_BRYAR_PISTOL &&
03282                 (level.time - bs->lastDeadTime) < BOT_MAX_WEAPON_GATHER_TIME)
03283         { //get the nearest weapon laying around base before heading off for battle
03284                 idleWP = GetBestIdleGoal(bs);
03285 
03286                 if (idleWP != -1 && gWPArray[idleWP] && gWPArray[idleWP]->inuse)
03287                 {
03288                         if (bs->wpDestSwitchTime < level.time)
03289                         {
03290                                 bs->wpDestination = gWPArray[idleWP];
03291                         }
03292                         return 1;
03293                 }
03294         }
03295         else if (bs->cur_ps.weapon == WP_BRYAR_PISTOL &&
03296                 (level.time - bs->lastDeadTime) < BOT_MAX_WEAPON_CHASE_TIME &&
03297                 bs->wpDestination && bs->wpDestination->weight)
03298         {
03299                 dest_sw = bs->wpDestination;
03300                 dosw = 1;
03301         }
03302 
03303         if (bcl->sess.sessionTeam == SIEGETEAM_TEAM1)
03304         {
03305                 attacker = imperial_attackers;
03306                 flagForDefendableObjective = WPFLAG_SIEGE_REBELOBJ;
03307                 flagForAttackableObjective = WPFLAG_SIEGE_IMPERIALOBJ;
03308         }
03309         else
03310         {
03311                 attacker = rebel_attackers;
03312                 flagForDefendableObjective = WPFLAG_SIEGE_IMPERIALOBJ;
03313                 flagForAttackableObjective = WPFLAG_SIEGE_REBELOBJ;
03314         }
03315 
03316         if (attacker)
03317         {
03318                 bs->siegeState = SIEGESTATE_ATTACKER;
03319         }
03320         else
03321         {
03322                 bs->siegeState = SIEGESTATE_DEFENDER;
03323                 defenders = Siege_CountDefenders(bs);
03324                 teammates = Siege_CountTeammates(bs);
03325 
03326                 if (defenders > teammates/3 && teammates > 1)
03327                 { //devote around 1/4 of our team to completing our own side goals even if we're a defender.
03328                   //If we have no side goals we will realize that later on and join the defenders
03329                         bs->siegeState = SIEGESTATE_ATTACKER;
03330                 }
03331         }
03332 
03333         if (bs->state_Forced)
03334         {
03335                 bs->siegeState = bs->state_Forced;
03336         }
03337 
03338         if (bs->siegeState == SIEGESTATE_ATTACKER)
03339         {
03340                 if (!Siege_TargetClosestObjective(bs, flagForAttackableObjective))
03341                 { //looks like we have no goals other than to keep the other team from completing objectives
03342                         Siege_DefendFromAttackers(bs);
03343                         if (bs->shootGoal)
03344                         {
03345                                 dif[0] = (bs->shootGoal->r.absmax[0]+bs->shootGoal->r.absmin[0])/2;
03346                                 dif[1] = (bs->shootGoal->r.absmax[1]+bs->shootGoal->r.absmin[1])/2;
03347                                 dif[2] = (bs->shootGoal->r.absmax[2]+bs->shootGoal->r.absmin[2])/2;
03348                                 
03349                                 if (!BotPVSCheck(bs->origin, dif))
03350                                 {
03351                                         bs->shootGoal = NULL;
03352                                 }
03353                                 else
03354                                 {
03355                                         trap_Trace(&tr, bs->origin, NULL, NULL, dif, bs->client, MASK_SOLID);
03356 
03357                                         if (tr.fraction != 1 && tr.entityNum != bs->shootGoal->s.number)
03358                                         {
03359                                                 bs->shootGoal = NULL;
03360                                         }
03361                                 }
03362                         }
03363                 }
03364         }
03365         else if (bs->siegeState == SIEGESTATE_DEFENDER)
03366         {
03367                 Siege_DefendFromAttackers(bs);
03368                 if (bs->shootGoal)
03369                 {
03370                         dif[0] = (bs->shootGoal->r.absmax[0]+bs->shootGoal->r.absmin[0])/2;
03371                         dif[1] = (bs->shootGoal->r.absmax[1]+bs->shootGoal->r.absmin[1])/2;
03372                         dif[2] = (bs->shootGoal->r.absmax[2]+bs->shootGoal->r.absmin[2])/2;
03373                                 
03374                         if (!BotPVSCheck(bs->origin, dif))
03375                         {
03376                                 bs->shootGoal = NULL;
03377                         }
03378                         else
03379                         {
03380                                 trap_Trace(&tr, bs->origin, NULL, NULL, dif, bs->client, MASK_SOLID);
03381 
03382                                 if (tr.fraction != 1 && tr.entityNum != bs->shootGoal->s.number)
03383                                 {
03384                                         bs->shootGoal = NULL;
03385                                 }
03386                         }
03387                 }
03388         }
03389         else
03390         { //get busy!
03391                 Siege_TargetClosestObjective(bs, flagForAttackableObjective);
03392                 if (bs->shootGoal)
03393                 {
03394                         dif[0] = (bs->shootGoal->r.absmax[0]+bs->shootGoal->r.absmin[0])/2;
03395                         dif[1] = (bs->shootGoal->r.absmax[1]+bs->shootGoal->r.absmin[1])/2;
03396                         dif[2] = (bs->shootGoal->r.absmax[2]+bs->shootGoal->r.absmin[2])/2;
03397                                 
03398                         if (!BotPVSCheck(bs->origin, dif))
03399                         {
03400                                 bs->shootGoal = NULL;
03401                         }
03402                         else
03403                         {
03404                                 trap_Trace(&tr, bs->origin, NULL, NULL, dif, bs->client, MASK_SOLID);
03405 
03406                                 if (tr.fraction != 1 && tr.entityNum != bs->shootGoal->s.number)
03407                                 {
03408                                         bs->shootGoal = NULL;
03409                                 }
03410                         }
03411                 }
03412         }
03413 
03414         if (dosw)
03415         { //allow siege objective code to run, but if after a particular item then keep going after it
03416                 bs->wpDestination = dest_sw;
03417         }
03418 
03419         return 1;
03420 }

void StandardBotAI bot_state_t bs,
float  thinktime
 

Definition at line 5931 of file ai_main.c.

References entityShared_t::absmax, entityShared_t::absmin, botskills_s::accuracy, AltFiring(), playerState_s::ammo, bot_state_s::beStill, bot_forcepowers, bot_forgimmick, bot_getinthecarrr, bot_honorableduelacceptance, BOT_PLANT_DISTANCE, BOT_PLANT_INTERVAL, BOT_SABER_THROW_RANGE, bot_state_t, BOT_WPTOUCH_DISTANCE, BotAimLeading(), BotAimOffsetGoalAngles(), bot_state_s::botChallengingTime, BotCheckDetPacks(), BotDeathNotify(), BotDoChat(), BotDoTeamplayAI(), BotFallbackNavigation(), BotGetWeaponRange(), BotMindTricked(), BotReplyGreetings(), BotScanForLeader(), BotSelectChoiceWeapon(), BotSelectIdealWeapon(), botstates, BotSurfaceNear(), BotTrace_Duck(), BotTrace_Jump(), BotTrace_Strafe(), BotTryAnotherWeapon(), BotUseInventoryItem(), BotWeaponBlockable(), BotWeaponCanLead(), BWEAPONRANGE_LONG, BWEAPONRANGE_MELEE, BWEAPONRANGE_MID, BWEAPONRANGE_SABER, CA_ACTIVE, CA_AUTHORIZING, bot_state_s::campStanding, bot_state_s::chatAltObject, bot_state_s::chatObject, bot_state_s::chatTeam, bot_state_s::chatTime, CheckForFriendInLOF(), CheckForFunc(), CLASS_VEHICLE, bot_state_s::client, gentity_s::client, client, level_locals_t::clients, Cmd_EngageDuel_f(), Cmd_SaberAttackCycle_f(), Cmd_ToggleSaber_f(), CombatBotAI(), CommanderBotAI(), clientPersistant_t::connected, CTFFlagMovement(), bot_state_s::cur_ps, bot_state_s::currentChat, bot_state_s::currentEnemy, entityShared_t::currentOrigin, bot_state_s::dangerousObject, bot_state_s::deathActivitiesDone, bot_state_s::destinationGrabTime, bot_state_s::doAltAttack, bot_state_s::doAttack, bot_state_s::doChat, bot_state_s::doForcePush, bot_state_s::doingFallback, bot_state_s::duckTime, playerState_s::duelIndex, playerState_s::duelInProgress, playerState_s::duelTime, playerState_s::electrifyTime, ENEMY_FORGET_MS, bot_state_s::enemySeenTime, ENTITYNUM_NONE, EntityVisibleBox(), ET_NPC, entityState_s::eType, bot_state_s::eye, playerState_s::fd, wpobject_s::flags, FORCE_DARKSIDE, FORCE_LEVEL_1, FORCE_LIGHTNING_RADIUS, FORCE_LIGHTSIDE, forcedata_s::forceGripBeingGripped, forcedata_s::forceGripCripple, bot_state_s::forceJumpChargeTime, bot_state_s::forceJumping, bot_state_s::forceMove_Forward, bot_state_s::forceMove_Right, bot_state_s::forceMove_Up, forcedata_s::forcePower, forcedata_s::forcePowerLevel, forcePowerNeeded, forcedata_s::forcePowersActive, forcedata_s::forcePowersKnown, ForcePowerUsableOn(), forcedata_s::forceSide, bot_state_s::forceWeaponSelect, FP_ABSORB, FP_DRAIN, FP_GRIP, FP_HEAL, FP_LEVITATION, FP_LIGHTNING, FP_PROTECT, FP_PULL, FP_PUSH, FP_RAGE, FP_SABER_OFFENSE, FP_SABERTHROW, FP_SEE, FP_SPEED, FP_TEAM_FORCE, FP_TEAM_HEAL, FP_TELEPATHY, bot_state_s::frame_Enemy_Len, bot_state_s::frame_Enemy_Vis, bot_state_s::frame_Waypoint_Len, bot_state_s::frame_Waypoint_Vis, g_entities, g_forcePowerDisable, g_gametype, g_privateDuel, g_RMG, gDeactivated, gentity_t, GetIdealDestination(), GetNearestVisibleWP(), gJMSaberEnt, gLevelFlags, bot_state_s::goalAngles, bot_state_s::goalMovedir, bot_state_s::goalPosition, GT_JEDIMASTER, GT_SINGLE_PLAYER, GT_TEAM, gWPArray, gWPNum, playerState_s::hasDetPackPlanted, gentity_s::health, bot_state_s::hereWhenSpotted, bot_state_s::hitSpotted, bot_state_s::iHaveNoIdeaWhereIAmGoing, wpobject_s::index, InFieldOfVision(), vmCvar_t::integer, gentity_s::inuse, wpobject_s::inuse, bot_state_s::isCamping, playerState_s::isJediMaster, bot_state_s::isSquadLeader, bot_state_s::jDelay, bot_state_s::jmState, bot_state_s::jumpHoldTime, bot_state_s::jumpPrep, bot_state_s::jumpTime, KeepAltFromFiring(), KeepPrimFromFiring(), bot_state_s::lastAttacked, bot_state_s::lastDeadTime, bot_state_s::lastEnemySpotted, bot_state_s::lastHurt, bot_state_s::lastSignificantAreaChange, bot_state_s::lastSignificantChangeTime, bot_state_s::lastVisibleEnemyIndex, level, LEVELFLAG_NOPOINTPREDICTION, playerState_s::m_iVehicleNum, gentity_s::m_pVehicle, MAX_DRAIN_DISTANCE, MAX_GENTITIES, MAX_GRIP_DISTANCE, MAX_TRICK_DISTANCE, MELEE_ATTACK_RANGE, MeleeCombatHandling(), bot_state_s::meleeStrafeDir, bot_state_s::meleeStrafeDisable, MoveTowardIdealAngles(), bot_state_s::noUseTime, entityState_s::NPC_class, NULL, entityState_s::number, OrgVisible(), playerState_s::origin, bot_state_s::origin, wpobject_s::origin, entityState_s::origin, PassLovedOneCheck(), PassStandardEnemyChecks(), PassWayCheck(), gclient_s::pers, PITCH, bot_state_s::plantContinue, bot_state_s::plantDecided, bot_state_s::plantKillEmAll, bot_state_s::plantTime, playerState_s::pm_flags, PMF_JUMP_HELD, PrimFiring(), gclient_s::ps, Q_irand(), qboolean, qfalse, qtrue, gentity_s::r, rand(), botskills_s::reflex, bot_state_s::revengeEnemy, bot_state_s::revengeHateLevel, gentity_s::s, SABER_ATTACK_RANGE, forcedata_s::saberAnimLevel, SaberCombatHandling(), bot_state_s::saberDefending, playerState_s::saberEntityNum, playerState_s::saberHolstered, playerState_s::saberInFlight, playerState_s::saberLockTime, bot_state_s::saberPower, bot_state_s::saberPowerTime, bot_state_s::saberThrowTime, ScanForEnemies(), bot_state_s::settings, bot_state_s::shootGoal, bot_settings_s::skill, bot_state_s::skills, bot_state_s::squadCannotLead, bot_state_s::squadLeader, SS_DUAL, SS_FAST, SS_MEDIUM, SS_STAFF, SS_STRONG, STRAFEAROUND_LEFT, STRAFEAROUND_RIGHT, StrafeTracing(), gentity_s::takedamage, TEAM_SPECTATOR, level_locals_t::time, bot_state_s::timeToReact, TotalTrailDistance(), trap_EA_Alt_Attack(), trap_EA_Attack(), trap_EA_Crouch(), trap_EA_ForcePower(), trap_EA_Jump(), trap_EA_Move(), trap_EA_MoveBack(), trap_EA_MoveForward(), trap_EA_MoveLeft(), trap_EA_MoveRight(), trap_EA_Say(), trap_EA_SayTeam(), trap_EA_Use(), vec3_origin, vec3_t, vectoangles(), VectorCopy, VectorNormalize(), VectorSubtract, bot_state_s::viewangles, playerState_s::viewheight, WaitingForNow(), playerState_s::weapon, WEAPON_CHARGING, WEAPON_CHARGING_ALT, WEAPON_READY, weaponData, playerState_s::weaponstate, WP_DET_PACK, WP_FLECHETTE, WP_SABER, WP_TRIP_MINE, bot_state_s::wpCamping, bot_state_s::wpCampingTo, WPConstantRoutine(), bot_state_s::wpCurrent, bot_state_s::wpDestIgnoreTime, bot_state_s::wpDestination, bot_state_s::wpDestSwitchTime, bot_state_s::wpDirection, WPFLAG_BLUE_FLAG, WPFLAG_NOMOVEFUNC, WPFLAG_NOVIS, WPFLAG_RED_FLAG, WPFLAG_WAITFORFUNC, WPOrgVisible(), bot_state_s::wpSeenTime, bot_state_s::wpStoreDest, bot_state_s::wpSwitchTime, WPTouchRoutine(), bot_state_s::wpTravelTime, and YAW.

Referenced by BotAI().

05932 {
05933         int wp, enemy;
05934         int desiredIndex;
05935         int goalWPIndex;
05936         int doingFallback = 0;
05937         int fjHalt;
05938         vec3_t a, ang, headlevel, eorg, noz_x, noz_y, dif, a_fo;
05939         float reaction;
05940         float bLeadAmount;
05941         int meleestrafe = 0;
05942         int useTheForce = 0;
05943         int forceHostile = 0;
05944         int cBAI = 0;
05945         gentity_t *friendInLOF = 0;
05946         float mLen;
05947         int visResult = 0;
05948         int selResult = 0;
05949         int mineSelect = 0;
05950         int detSelect = 0;
05951         vec3_t preFrameGAngles;
05952 
05953         if (gDeactivated)
05954         {
05955                 bs->wpCurrent = NULL;
05956                 bs->currentEnemy = NULL;
05957                 bs->wpDestination = NULL;
05958                 bs->wpDirection = 0;
05959                 return;
05960         }
05961 
05962         if (g_entities[bs->client].inuse &&
05963                 g_entities[bs->client].client &&
05964                 g_entities[bs->client].client->sess.sessionTeam == TEAM_SPECTATOR)
05965         {
05966                 bs->wpCurrent = NULL;
05967                 bs->currentEnemy = NULL;
05968                 bs->wpDestination = NULL;
05969                 bs->wpDirection = 0;
05970                 return;
05971         }
05972 
05973 
05974 #ifndef FINAL_BUILD
05975         if (bot_getinthecarrr.integer)
05976         { //stupid vehicle debug, I tire of having to connect another client to test passengers.
05977                 gentity_t *botEnt = &g_entities[bs->client];
05978 
05979                 if (botEnt->inuse && botEnt->client && botEnt->client->ps.m_iVehicleNum)
05980                 { //in a vehicle, so...
05981                         bs->noUseTime = level.time + 5000;
05982 
05983                         if (bot_getinthecarrr.integer != 2)
05984                         {
05985                                 trap_EA_MoveForward(bs->client);
05986 
05987                                 if (bot_getinthecarrr.integer == 3)
05988                                 { //use alt fire
05989                                         trap_EA_Alt_Attack(bs->client);
05990                                 }
05991                         }
05992                 }
05993                 else
05994                 { //find one, get in
05995                         int i = 0;
05996                         gentity_t *vehicle = NULL;
05997                         //find the nearest, manned vehicle
05998                         while (i < MAX_GENTITIES)
05999                         {
06000                                 vehicle = &g_entities[i];
06001 
06002                                 if (vehicle->inuse && vehicle->client && vehicle->s.eType == ET_NPC &&
06003                                         vehicle->s.NPC_class == CLASS_VEHICLE && vehicle->m_pVehicle &&
06004                                         (vehicle->client->ps.m_iVehicleNum || bot_getinthecarrr.integer == 2))
06005                                 { //ok, this is a vehicle, and it has a pilot/passengers
06006                                         break;
06007                                 }
06008                                 i++;
06009                         }
06010                         if (i != MAX_GENTITIES && vehicle)
06011                         { //broke before end so we must've found something
06012                                 vec3_t v;
06013 
06014                                 VectorSubtract(vehicle->client->ps.origin, bs->origin, v);
06015                                 VectorNormalize(v);
06016                                 vectoangles(v, bs->goalAngles);
06017                                 MoveTowardIdealAngles(bs);
06018                                 trap_EA_Move(bs->client, v, 5000.0f);
06019 
06020                                 if (bs->noUseTime < (level.time-400))
06021                                 {
06022                                         bs->noUseTime = level.time + 500;
06023                                 }
06024                         }
06025                 }
06026 
06027                 return;
06028         }
06029 #endif
06030 
06031         if (bot_forgimmick.integer)
06032         {
06033                 bs->wpCurrent = NULL;
06034                 bs->currentEnemy = NULL;
06035                 bs->wpDestination = NULL;
06036                 bs->wpDirection = 0;
06037 
06038                 if (bot_forgimmick.integer == 2)
06039                 { //for debugging saber stuff, this is handy
06040                         trap_EA_Attack(bs->client);
06041                 }
06042 
06043                 if (bot_forgimmick.integer == 3)
06044                 { //for testing cpu usage moving around rmg terrain without AI
06045                         vec3_t mdir;
06046 
06047                         VectorSubtract(bs->origin, vec3_origin, mdir);
06048                         VectorNormalize(mdir);
06049                         trap_EA_Attack(bs->client);
06050                         trap_EA_Move(bs->client, mdir, 5000);
06051                 }
06052 
06053                 if (bot_forgimmick.integer == 4)
06054                 { //constantly move toward client 0
06055                         if (g_entities[0].client && g_entities[0].inuse)
06056                         {
06057                                 vec3_t mdir;
06058 
06059                                 VectorSubtract(g_entities[0].client->ps.origin, bs->origin, mdir);
06060                                 VectorNormalize(mdir);
06061                                 trap_EA_Move(bs->client, mdir, 5000);
06062                         }
06063                 }
06064 
06065                 if (bs->forceMove_Forward)
06066                 {
06067                         if (bs->forceMove_Forward > 0)
06068                         {
06069                                 trap_EA_MoveForward(bs->client);
06070                         }
06071                         else
06072                         {
06073                                 trap_EA_MoveBack(bs->client);
06074                         }
06075                 }
06076                 if (bs->forceMove_Right)
06077                 {
06078                         if (bs->forceMove_Right > 0)
06079                         {
06080                                 trap_EA_MoveRight(bs->client);
06081                         }
06082                         else
06083                         {
06084                                 trap_EA_MoveLeft(bs->client);
06085                         }
06086                 }
06087                 if (bs->forceMove_Up)
06088                 {
06089                         trap_EA_Jump(bs->client);
06090                 }
06091                 return;
06092         }
06093 
06094         if (!bs->lastDeadTime)
06095         { //just spawned in?
06096                 bs->lastDeadTime = level.time;
06097         }
06098 
06099         if (g_entities[bs->client].health < 1)
06100         {
06101                 bs->lastDeadTime = level.time;
06102 
06103                 if (!bs->deathActivitiesDone && bs->lastHurt && bs->lastHurt->client && bs->lastHurt->s.number != bs->client)
06104                 {
06105                         BotDeathNotify(bs);
06106                         if (PassLovedOneCheck(bs, bs->lastHurt))
06107                         {
06108                                 //CHAT: Died
06109                                 bs->chatObject = bs->lastHurt;
06110                                 bs->chatAltObject = NULL;
06111                                 BotDoChat(bs, "Died", 0);
06112                         }
06113                         else if (!PassLovedOneCheck(bs, bs->lastHurt) &&
06114                                 botstates[bs->lastHurt->s.number] &&
06115                                 PassLovedOneCheck(botstates[bs->lastHurt->s.number], &g_entities[bs->client]))
06116                         { //killed by a bot that I love, but that does not love me
06117                                 bs->chatObject = bs->lastHurt;
06118                                 bs->chatAltObject = NULL;
06119                                 BotDoChat(bs, "KilledOnPurposeByLove", 0);
06120                         }
06121 
06122                         bs->deathActivitiesDone = 1;
06123                 }
06124                 
06125                 bs->wpCurrent = NULL;
06126                 bs->currentEnemy = NULL;
06127                 bs->wpDestination = NULL;
06128                 bs->wpCamping = NULL;
06129                 bs->wpCampingTo = NULL;
06130                 bs->wpStoreDest = NULL;
06131                 bs->wpDestIgnoreTime = 0;
06132                 bs->wpDestSwitchTime = 0;
06133                 bs->wpSeenTime = 0;
06134                 bs->wpDirection = 0;
06135 
06136                 if (rand()%10 < 5 &&
06137                         (!bs->doChat || bs->chatTime < level.time))
06138                 {
06139                         trap_EA_Attack(bs->client);
06140                 }
06141 
06142                 return;
06143         }
06144 
06145         VectorCopy(bs->goalAngles, preFrameGAngles);
06146 
06147         bs->doAttack = 0;
06148         bs->doAltAttack = 0;
06149         //reset the attack states
06150 
06151         if (bs->isSquadLeader)
06152         {
06153                 CommanderBotAI(bs);
06154         }
06155         else
06156         {
06157                 BotDoTeamplayAI(bs);
06158         }
06159 
06160         if (!bs->currentEnemy)
06161         {
06162                 bs->frame_Enemy_Vis = 0;
06163         }
06164 
06165         if (bs->revengeEnemy && bs->revengeEnemy->client &&
06166                 bs->revengeEnemy->client->pers.connected != CA_ACTIVE && bs->revengeEnemy->client->pers.connected != CA_AUTHORIZING)
06167         {
06168                 bs->revengeEnemy = NULL;
06169                 bs->revengeHateLevel = 0;
06170         }
06171 
06172         if (bs->currentEnemy && bs->currentEnemy->client &&
06173                 bs->currentEnemy->client->pers.connected != CA_ACTIVE && bs->currentEnemy->client->pers.connected != CA_AUTHORIZING)
06174         {
06175                 bs->currentEnemy = NULL;
06176         }
06177 
06178         fjHalt = 0;
06179 
06180 #ifndef FORCEJUMP_INSTANTMETHOD
06181         if (bs->forceJumpChargeTime > level.time)
06182         {
06183                 useTheForce = 1;
06184                 forceHostile = 0;
06185         }
06186 
06187         if (bs->currentEnemy && bs->currentEnemy->client && bs->frame_Enemy_Vis && bs->forceJumpChargeTime < level.time)
06188 #else
06189         if (bs->currentEnemy && bs->currentEnemy->client && bs->frame_Enemy_Vis)
06190 #endif
06191         {
06192                 VectorSubtract(bs->currentEnemy->client->ps.origin, bs->eye, a_fo);
06193                 vectoangles(a_fo, a_fo);
06194 
06195                 //do this above all things
06196                 if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_PUSH)) && (bs->doForcePush > level.time || bs->cur_ps.fd.forceGripBeingGripped > level.time) && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_PUSH]][FP_PUSH] /*&& InFieldOfVision(bs->viewangles, 50, a_fo)*/)
06197                 {
06198                         level.clients[bs->client].ps.fd.forcePowerSelected = FP_PUSH;
06199                         useTheForce = 1;
06200                         forceHostile = 1;
06201                 }
06202                 else if (bs->cur_ps.fd.forceSide == FORCE_DARKSIDE)
06203                 { //try dark side powers
06204                   //in order of priority top to bottom
06205                         if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_GRIP)) && (bs->cur_ps.fd.forcePowersActive & (1 << FP_GRIP)) && InFieldOfVision(bs->viewangles, 50, a_fo))
06206                         { //already gripping someone, so hold it
06207                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_GRIP;
06208                                 useTheForce = 1;
06209                                 forceHostile = 1;
06210                         }
06211                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_LIGHTNING)) && bs->frame_Enemy_Len < FORCE_LIGHTNING_RADIUS && level.clients[bs->client].ps.fd.forcePower > 50 && InFieldOfVision(bs->viewangles, 50, a_fo))
06212                         {
06213                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_LIGHTNING;
06214                                 useTheForce = 1;
06215                                 forceHostile = 1;
06216                         }
06217                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_GRIP)) && bs->frame_Enemy_Len < MAX_GRIP_DISTANCE && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_GRIP]][FP_GRIP] && InFieldOfVision(bs->viewangles, 50, a_fo))
06218                         {
06219                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_GRIP;
06220                                 useTheForce = 1;
06221                                 forceHostile = 1;
06222                         }
06223                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_RAGE)) && g_entities[bs->client].health < 25 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_RAGE]][FP_RAGE])
06224                         {
06225                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_RAGE;
06226                                 useTheForce = 1;
06227                                 forceHostile = 0;
06228                         }
06229                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_DRAIN)) && bs->frame_Enemy_Len < MAX_DRAIN_DISTANCE && level.clients[bs->client].ps.fd.forcePower > 50 && InFieldOfVision(bs->viewangles, 50, a_fo) && bs->currentEnemy->client->ps.fd.forcePower > 10 && bs->currentEnemy->client->ps.fd.forceSide == FORCE_LIGHTSIDE)
06230                         {
06231                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_DRAIN;
06232                                 useTheForce = 1;
06233                                 forceHostile = 1;
06234                         }
06235                 }
06236                 else if (bs->cur_ps.fd.forceSide == FORCE_LIGHTSIDE)
06237                 { //try light side powers
06238                         if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_ABSORB)) && bs->cur_ps.fd.forceGripCripple &&
06239                                  level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_ABSORB]][FP_ABSORB])
06240                         { //absorb to get out
06241                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_ABSORB;
06242                                 useTheForce = 1;
06243                                 forceHostile = 0;
06244                         }
06245                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_ABSORB)) && bs->cur_ps.electrifyTime >= level.time &&
06246                                  level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_ABSORB]][FP_ABSORB])
06247                         { //absorb lightning
06248                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_ABSORB;
06249                                 useTheForce = 1;
06250                                 forceHostile = 0;
06251                         }
06252                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_TELEPATHY)) && bs->frame_Enemy_Len < MAX_TRICK_DISTANCE && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_TELEPATHY]][FP_TELEPATHY] && InFieldOfVision(bs->viewangles, 50, a_fo) && !(bs->currentEnemy->client->ps.fd.forcePowersActive & (1 << FP_SEE)))
06253                         {
06254                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_TELEPATHY;
06255                                 useTheForce = 1;
06256                                 forceHostile = 1;
06257                         }
06258                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_ABSORB)) && g_entities[bs->client].health < 75 && bs->currentEnemy->client->ps.fd.forceSide == FORCE_DARKSIDE && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_ABSORB]][FP_ABSORB])
06259                         {
06260                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_ABSORB;
06261                                 useTheForce = 1;
06262                                 forceHostile = 0;
06263                         }
06264                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_PROTECT)) && g_entities[bs->client].health < 35 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_PROTECT]][FP_PROTECT])
06265                         {
06266                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_PROTECT;
06267                                 useTheForce = 1;
06268                                 forceHostile = 0;
06269                         }
06270                 }
06271 
06272                 if (!useTheForce)
06273                 { //try neutral powers
06274                         if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_PUSH)) && bs->cur_ps.fd.forceGripBeingGripped > level.time && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_PUSH]][FP_PUSH] && InFieldOfVision(bs->viewangles, 50, a_fo))
06275                         {
06276                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_PUSH;
06277                                 useTheForce = 1;
06278                                 forceHostile = 1;
06279                         }
06280                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_SPEED)) && g_entities[bs->client].health < 25 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_SPEED]][FP_SPEED])
06281                         {
06282                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_SPEED;
06283                                 useTheForce = 1;
06284                                 forceHostile = 0;
06285                         }
06286                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_SEE)) && BotMindTricked(bs->client, bs->currentEnemy->s.number) && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_SEE]][FP_SEE])
06287                         {
06288                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_SEE;
06289                                 useTheForce = 1;
06290                                 forceHostile = 0;
06291                         }
06292                         else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_PULL)) && bs->frame_Enemy_Len < 256 && level.clients[bs->client].ps.fd.forcePower > 75 && InFieldOfVision(bs->viewangles, 50, a_fo))
06293                         {
06294                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_PULL;
06295                                 useTheForce = 1;
06296                                 forceHostile = 1;
06297                         }
06298                 }
06299         }
06300 
06301         if (!useTheForce)
06302         { //try powers that we don't care if we have an enemy for
06303                 if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_HEAL)) && g_entities[bs->client].health < 50 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_HEAL]][FP_HEAL] && bs->cur_ps.fd.forcePowerLevel[FP_HEAL] > FORCE_LEVEL_1)
06304                 {
06305                         level.clients[bs->client].ps.fd.forcePowerSelected = FP_HEAL;
06306                         useTheForce = 1;
06307                         forceHostile = 0;
06308                 }
06309                 else if ((bs->cur_ps.fd.forcePowersKnown & (1 << FP_HEAL)) && g_entities[bs->client].health < 50 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_HEAL]][FP_HEAL] && !bs->currentEnemy && bs->isCamping > level.time)
06310                 { //only meditate and heal if we're camping
06311                         level.clients[bs->client].ps.fd.forcePowerSelected = FP_HEAL;
06312                         useTheForce = 1;
06313                         forceHostile = 0;
06314                 }
06315         }
06316 
06317         if (useTheForce && forceHostile)
06318         {
06319                 if (bs->currentEnemy && bs->currentEnemy->client &&
06320                         !ForcePowerUsableOn(&g_entities[bs->client], bs->currentEnemy, level.clients[bs->client].ps.fd.forcePowerSelected))
06321                 {
06322                         useTheForce = 0;
06323                         forceHostile = 0;
06324                 }
06325         }
06326 
06327         doingFallback = 0;
06328 
06329         bs->deathActivitiesDone = 0;
06330 
06331         if (BotUseInventoryItem(bs))
06332         {
06333                 if (rand()%10 < 5)
06334                 {
06335                         trap_EA_Use(bs->client);
06336                 }
06337         }
06338 
06339         if (bs->cur_ps.ammo[weaponData[bs->cur_ps.weapon].ammoIndex] < weaponData[bs->cur_ps.weapon].energyPerShot)
06340         {
06341                 if (BotTryAnotherWeapon(bs))
06342                 {
06343                         return;
06344                 }
06345         }
06346         else
06347         {
06348                 if (bs->currentEnemy && bs->lastVisibleEnemyIndex == bs->currentEnemy->s.number &&
06349                         bs->frame_Enemy_Vis && bs->forceWeaponSelect /*&& bs->plantContinue < level.time*/)
06350                 {
06351                         bs->forceWeaponSelect = 0;
06352                 }
06353 
06354                 if (bs->plantContinue > level.time)
06355                 {
06356                         bs->doAttack = 1;
06357                         bs->destinationGrabTime = 0;
06358                 }
06359 
06360                 if (!bs->forceWeaponSelect && bs->cur_ps.hasDetPackPlanted && bs->plantKillEmAll > level.time)
06361                 {
06362                         bs->forceWeaponSelect = WP_DET_PACK;
06363                 }
06364 
06365                 if (bs->forceWeaponSelect)
06366                 {
06367                         selResult = BotSelectChoiceWeapon(bs, bs->forceWeaponSelect, 1);
06368                 }
06369 
06370                 if (selResult)
06371                 {
06372                         if (selResult == 2)
06373                         { //newly selected
06374                                 return;
06375                         }
06376                 }
06377                 else if (BotSelectIdealWeapon(bs))
06378                 {
06379                         return;
06380                 }
06381         }
06382         /*if (BotSelectMelee(bs))
06383         {
06384                 return;
06385         }*/
06386 
06387         reaction = bs->skills.reflex/bs->settings.skill;
06388 
06389         if (reaction < 0)
06390         {
06391                 reaction = 0;
06392         }
06393         if (reaction > 2000)
06394         {
06395                 reaction = 2000;
06396         }
06397 
06398         if (!bs->currentEnemy)
06399         {
06400                 bs->timeToReact = level.time + reaction;
06401         }
06402 
06403         if (bs->cur_ps.weapon == WP_DET_PACK && bs->cur_ps.hasDetPackPlanted && bs->plantKillEmAll > level.time)
06404         {
06405                 bs->doAltAttack = 1;
06406         }
06407 
06408         if (bs->wpCamping)
06409         {
06410                 if (bs->isCamping < level.time)
06411                 {
06412                         bs->wpCamping = NULL;
06413                         bs->isCamping = 0;
06414                 }
06415 
06416                 if (bs->currentEnemy && bs->frame_Enemy_Vis)
06417                 {
06418                         bs->wpCamping = NULL;
06419                         bs->isCamping = 0;
06420                 }
06421         }
06422 
06423         if (bs->wpCurrent &&
06424                 (bs->wpSeenTime < level.time || bs->wpTravelTime < level.time))
06425         {
06426                 bs->wpCurrent = NULL;
06427         }
06428 
06429         if (bs->currentEnemy)
06430         {
06431                 if (bs->enemySeenTime < level.time ||
06432                         !PassStandardEnemyChecks(bs, bs->currentEnemy))
06433                 {
06434                         if (bs->revengeEnemy == bs->currentEnemy &&
06435                                 bs->currentEnemy->health < 1 &&
06436                                 bs->lastAttacked && bs->lastAttacked == bs->currentEnemy)
06437                         {
06438                                 //CHAT: Destroyed hated one [KilledHatedOne section]
06439                                 bs->chatObject = bs->revengeEnemy;
06440                                 bs->chatAltObject = NULL;
06441                                 BotDoChat(bs, "KilledHatedOne", 1);
06442                                 bs->revengeEnemy = NULL;
06443                                 bs->revengeHateLevel = 0;
06444                         }
06445                         else if (bs->currentEnemy->health < 1 && PassLovedOneCheck(bs, bs->currentEnemy) &&
06446                                 bs->lastAttacked && bs->lastAttacked == bs->currentEnemy)
06447                         {
06448                                 //CHAT: Killed
06449                                 bs->chatObject = bs->currentEnemy;
06450                                 bs->chatAltObject = NULL;
06451                                 BotDoChat(bs, "Killed", 0);
06452                         }
06453 
06454                         bs->currentEnemy = NULL;
06455                 }
06456         }
06457 
06458         if (bot_honorableduelacceptance.integer)
06459         {
06460                 if (bs->currentEnemy && bs->currentEnemy->client &&
06461                         bs->cur_ps.weapon == WP_SABER &&
06462                         g_privateDuel.integer &&
06463                         bs->frame_Enemy_Vis &&
06464                         bs->frame_Enemy_Len < 400 &&
06465                         bs->currentEnemy->client->ps.weapon == WP_SABER &&
06466                         bs->currentEnemy->client->ps.saberHolstered)
06467                 {
06468                         vec3_t e_ang_vec;
06469 
06470                         VectorSubtract(bs->currentEnemy->client->ps.origin, bs->eye, e_ang_vec);
06471 
06472                         if (InFieldOfVision(bs->viewangles, 100, e_ang_vec))
06473                         { //Our enemy has his saber holstered and has challenged us to a duel, so challenge him back
06474                                 if (!bs->cur_ps.saberHolstered)
06475                                 {
06476                                         Cmd_ToggleSaber_f(&g_entities[bs->client]);
06477                                 }
06478                                 else
06479                                 {
06480                                         if (bs->currentEnemy->client->ps.duelIndex == bs->client &&
06481                                                 bs->currentEnemy->client->ps.duelTime > level.time &&
06482                                                 !bs->cur_ps.duelInProgress)
06483                                         {
06484                                                 Cmd_EngageDuel_f(&g_entities[bs->client]);
06485                                         }
06486                                 }
06487 
06488                                 bs->doAttack = 0;
06489                                 bs->doAltAttack = 0;
06490                                 bs->botChallengingTime = level.time + 100;
06491                                 bs->beStill = level.time + 100;
06492                         }
06493                 }
06494         }
06495         //Apparently this "allows you to cheese" when fighting against bots. I'm not sure why you'd want to con bots
06496         //into an easy kill, since they're bots and all. But whatever.
06497 
06498         if (!bs->wpCurrent)
06499         {
06500                 wp = GetNearestVisibleWP(bs->origin, bs->client);
06501 
06502                 if (wp != -1)
06503                 {
06504                         bs->wpCurrent = gWPArray[wp];
06505                         bs->wpSeenTime = level.time + 1500;
06506                         bs->wpTravelTime = level.time + 10000; //never take more than 10 seconds to travel to a waypoint
06507                 }
06508         }
06509 
06510         if (bs->enemySeenTime < level.time || !bs->frame_Enemy_Vis || !bs->currentEnemy ||
06511                 (bs->currentEnemy /*&& bs->cur_ps.weapon == WP_SABER && bs->frame_Enemy_Len > 300*/))
06512         {
06513                 enemy = ScanForEnemies(bs);
06514 
06515                 if (enemy != -1)
06516                 {
06517                         bs->currentEnemy = &g_entities[enemy];
06518                         bs->enemySeenTime = level.time + ENEMY_FORGET_MS;
06519                 }
06520         }
06521 
06522         if (!bs->squadLeader && !bs->isSquadLeader)
06523         {
06524                 BotScanForLeader(bs);
06525         }
06526 
06527         if (!bs->squadLeader && bs->squadCannotLead < level.time)
06528         { //if still no leader after scanning, then become a squad leader
06529                 bs->isSquadLeader = 1;
06530         }
06531 
06532         if (bs->isSquadLeader && bs->squadLeader)
06533         { //we don't follow anyone if we are a leader
06534                 bs->squadLeader = NULL;
06535         }
06536 
06537         //ESTABLISH VISIBILITIES AND DISTANCES FOR THE WHOLE FRAME HERE
06538         if (bs->wpCurrent)
06539         {
06540                 if (g_RMG.integer)
06541                 { //this is somewhat hacky, but in RMG we don't really care about vertical placement because points are scattered across only the terrain.
06542                         vec3_t vecB, vecC;
06543 
06544                         vecB[0] = bs->origin[0];
06545                         vecB[1] = bs->origin[1];
06546                         vecB[2] = bs->origin[2];
06547 
06548                         vecC[0] = bs->wpCurrent->origin[0];
06549                         vecC[1] = bs->wpCurrent->origin[1];
06550                         vecC[2] = vecB[2];
06551 
06552 
06553                         VectorSubtract(vecC, vecB, a);
06554                 }
06555                 else
06556                 {
06557                         VectorSubtract(bs->wpCurrent->origin, bs->origin, a);
06558                 }
06559                 bs->frame_Waypoint_Len = VectorLength(a);
06560 
06561                 visResult = WPOrgVisible(&g_entities[bs->client], bs->origin, bs->wpCurrent->origin, bs->client);
06562 
06563                 if (visResult == 2)
06564                 {
06565                         bs->frame_Waypoint_Vis = 0;
06566                         bs->wpSeenTime = 0;
06567                         bs->wpDestination = NULL;
06568                         bs->wpDestIgnoreTime = level.time + 5000;
06569 
06570                         if (bs->wpDirection)
06571                         {
06572                                 bs->wpDirection = 0;
06573                         }
06574                         else
06575                         {
06576                                 bs->wpDirection = 1;
06577                         }
06578                 }
06579                 else if (visResult)
06580                 {
06581                         bs->frame_Waypoint_Vis = 1;
06582                 }
06583                 else
06584                 {
06585                         bs->frame_Waypoint_Vis = 0;
06586                 }
06587         }
06588 
06589         if (bs->currentEnemy)
06590         {
06591                 if (bs->currentEnemy->client)
06592                 {
06593                         VectorCopy(bs->currentEnemy->client->ps.origin, eorg);
06594                         eorg[2] += bs->currentEnemy->client->ps.viewheight;
06595                 }
06596                 else
06597                 {
06598                         VectorCopy(bs->currentEnemy->s.origin, eorg);
06599                 }
06600 
06601                 VectorSubtract(eorg, bs->eye, a);
06602                 bs->frame_Enemy_Len = VectorLength(a);
06603 
06604                 if (OrgVisible(bs->eye, eorg, bs->client))
06605                 {
06606                         bs->frame_Enemy_Vis = 1;
06607                         VectorCopy(eorg, bs->lastEnemySpotted);
06608                         VectorCopy(bs->origin, bs->hereWhenSpotted);
06609                         bs->lastVisibleEnemyIndex = bs->currentEnemy->s.number;
06610                         //VectorCopy(bs->eye, bs->lastEnemySpotted);
06611                         bs->hitSpotted = 0;
06612                 }
06613                 else
06614                 {
06615                         bs->frame_Enemy_Vis = 0;
06616                 }
06617         }
06618         else
06619         {
06620                 bs->lastVisibleEnemyIndex = ENTITYNUM_NONE;
06621         }
06622         //END
06623 
06624         if (bs->frame_Enemy_Vis)
06625         {
06626                 bs->enemySeenTime = level.time + ENEMY_FORGET_MS;
06627         }
06628 
06629         if (bs->wpCurrent)
06630         {
06631                 int wpTouchDist = BOT_WPTOUCH_DISTANCE;
06632                 WPConstantRoutine(bs);
06633 
06634                 if (!bs->wpCurrent)
06635                 { //WPConstantRoutine has the ability to nullify the waypoint if it fails certain checks, so..
06636                         return;
06637                 }
06638 
06639                 if (bs->wpCurrent->flags & WPFLAG_WAITFORFUNC)
06640                 {
06641                         if (!CheckForFunc(bs->wpCurrent->origin, -1))
06642                         {
06643                                 bs->beStill = level.time + 500; //no func brush under.. wait
06644                         }
06645                 }
06646                 if (bs->wpCurrent->flags & WPFLAG_NOMOVEFUNC)
06647                 {
06648                         if (CheckForFunc(bs->wpCurrent->origin, -1))
06649                         {
06650                                 bs->beStill = level.time + 500; //func brush under.. wait
06651                         }
06652                 }
06653 
06654                 if (bs->frame_Waypoint_Vis || (bs->wpCurrent->flags & WPFLAG_NOVIS))
06655                 {
06656                         if (g_RMG.integer)
06657                         {
06658                                 bs->wpSeenTime = level.time + 5000; //if we lose sight of the point, we have 1.5 seconds to regain it before we drop it
06659                         }
06660                         else
06661                         {
06662                                 bs->wpSeenTime = level.time + 1500; //if we lose sight of the point, we have 1.5 seconds to regain it before we drop it
06663                         }
06664                 }
06665                 VectorCopy(bs->wpCurrent->origin, bs->goalPosition);
06666                 if (bs->wpDirection)
06667                 {
06668                         goalWPIndex = bs->wpCurrent->index-1;
06669                 }
06670                 else
06671                 {
06672                         goalWPIndex = bs->wpCurrent->index+1;
06673                 }
06674 
06675                 if (bs->wpCamping)
06676                 {
06677                         VectorSubtract(bs->wpCampingTo->origin, bs->origin, a);
06678                         vectoangles(a, ang);
06679                         VectorCopy(ang, bs->goalAngles);
06680 
06681                         VectorSubtract(bs->origin, bs->wpCamping->origin, a);
06682                         if (VectorLength(a) < 64)
06683                         {
06684                                 VectorCopy(bs->wpCamping->origin, bs->goalPosition);
06685                                 bs->beStill = level.time + 1000;
06686 
06687                                 if (!bs->campStanding)
06688                                 {
06689                                         bs->duckTime = level.time + 1000;
06690                                 }
06691                         }
06692                 }
06693                 else if (gWPArray[goalWPIndex] && gWPArray[goalWPIndex]->inuse &&
06694                         !(gLevelFlags & LEVELFLAG_NOPOINTPREDICTION))
06695                 {
06696                         VectorSubtract(gWPArray[goalWPIndex]->origin, bs->origin, a);
06697                         vectoangles(a, ang);
06698                         VectorCopy(ang, bs->goalAngles);
06699                 }
06700                 else
06701                 {
06702                         VectorSubtract(bs->wpCurrent->origin, bs->origin, a);
06703                         vectoangles(a, ang);
06704                         VectorCopy(ang, bs->goalAngles);
06705                 }
06706 
06707                 if (bs->destinationGrabTime < level.time /*&& (!bs->wpDestination || (bs->currentEnemy && bs->frame_Enemy_Vis))*/)
06708                 {
06709                         GetIdealDestination(bs);
06710                 }
06711                 
06712                 if (bs->wpCurrent && bs->wpDestination)
06713                 {
06714                         if (TotalTrailDistance(bs->wpCurrent->index, bs->wpDestination->index, bs) == -1)
06715                         {
06716                                 bs->wpDestination = NULL;
06717                                 bs->destinationGrabTime = level.time + 10000;
06718                         }
06719                 }
06720 
06721                 if (g_RMG.integer)
06722                 {
06723                         if (bs->frame_Waypoint_Vis)
06724                         {
06725                                 if (bs->wpCurrent && !bs->wpCurrent->flags)
06726                                 {
06727                                         wpTouchDist *= 3;
06728                                 }
06729                         }
06730                 }
06731 
06732                 if (bs->frame_Waypoint_Len < wpTouchDist || (g_RMG.integer && bs->frame_Waypoint_Len < wpTouchDist*2))
06733                 {
06734                         WPTouchRoutine(bs);
06735 
06736                         if (!bs->wpDirection)
06737                         {
06738                                 desiredIndex = bs->wpCurrent->index+1;
06739                         }
06740                         else
06741                         {
06742                                 desiredIndex = bs->wpCurrent->index-1;
06743                         }
06744 
06745                         if (gWPArray[desiredIndex] &&
06746                                 gWPArray[desiredIndex]->inuse &&
06747                                 desiredIndex < gWPNum &&
06748                                 desiredIndex >= 0 &&
06749                                 PassWayCheck(bs, desiredIndex))
06750                         {
06751                                 bs->wpCurrent = gWPArray[desiredIndex];
06752                         }
06753                         else
06754                         {
06755                                 if (bs->wpDestination)
06756                                 {
06757                                         bs->wpDestination = NULL;
06758                                         bs->destinationGrabTime = level.time + 10000;
06759                                 }
06760 
06761                                 if (bs->wpDirection)
06762                                 {
06763                                         bs->wpDirection = 0;
06764                                 }
06765                                 else
06766                                 {
06767                                         bs->wpDirection = 1;
06768                                 }
06769                         }
06770                 }
06771         }
06772         else //We can't find a waypoint, going to need a fallback routine.
06773         {
06774                 /*if (g_gametype.integer == GT_DUEL)*/
06775                 { //helps them get out of messy situations
06776                         /*if ((level.time - bs->forceJumpChargeTime) > 3500)
06777                         {
06778                                 bs->forceJumpChargeTime = level.time + 2000;
06779                                 trap_EA_MoveForward(bs->client);
06780                         }
06781                         */
06782                         bs->jumpTime = level.time + 1500;
06783                         bs->jumpHoldTime = level.time + 1500;
06784                         bs->jDelay = 0;
06785                 }
06786                 doingFallback = BotFallbackNavigation(bs);
06787         }
06788 
06789         if (g_RMG.integer)
06790         { //for RMG if the bot sticks around an area too long, jump around randomly some to spread to a new area (horrible hacky method)
06791                 vec3_t vSubDif;
06792 
06793                 VectorSubtract(bs->origin, bs->lastSignificantAreaChange, vSubDif);
06794                 if (VectorLength(vSubDif) > 1500)
06795                 {
06796                         VectorCopy(bs->origin, bs->lastSignificantAreaChange);
06797                         bs->lastSignificantChangeTime = level.time + 20000;
06798                 }
06799 
06800                 if (bs->lastSignificantChangeTime < level.time)
06801                 {
06802                         bs->iHaveNoIdeaWhereIAmGoing = level.time + 17000;
06803                 }
06804         }
06805 
06806         if (bs->iHaveNoIdeaWhereIAmGoing > level.time && !bs->currentEnemy)
06807         {
06808                 VectorCopy(preFrameGAngles, bs->goalAngles);
06809                 bs->wpCurrent = NULL;
06810                 bs->wpSwitchTime = level.time + 150;
06811                 doingFallback = BotFallbackNavigation(bs);
06812                 bs->jumpTime = level.time + 150;
06813                 bs->jumpHoldTime = level.time + 150;
06814                 bs->jDelay = 0;
06815                 bs->lastSignificantChangeTime = level.time + 25000;
06816         }
06817 
06818         if (bs->wpCurrent && g_RMG.integer)
06819         {
06820                 qboolean doJ = qfalse;
06821 
06822                 if (bs->wpCurrent->origin[2]-192 > bs->origin[2])
06823                 {
06824                         doJ = qtrue;
06825                 }
06826                 else if ((bs->wpTravelTime - level.time) < 5000 && bs->wpCurrent->origin[2]-64 > bs->origin[2])
06827                 {
06828                         doJ = qtrue;
06829                 }
06830                 else if ((bs->wpTravelTime - level.time) < 7000 && (bs->wpCurrent->flags & WPFLAG_RED_FLAG))
06831                 {
06832                         if ((level.time - bs->jumpTime) > 200)
06833                         {
06834                                 bs->jumpTime = level.time + 100;
06835                                 bs->jumpHoldTime = level.time + 100;
06836                                 bs->jDelay = 0;
06837                         }
06838                 }
06839                 else if ((bs->wpTravelTime - level.time) < 7000 && (bs->wpCurrent->flags & WPFLAG_BLUE_FLAG))
06840                 {
06841                         if ((level.time - bs->jumpTime) > 200)
06842                         {
06843                                 bs->jumpTime = level.time + 100;
06844                                 bs->jumpHoldTime = level.time + 100;
06845                                 bs->jDelay = 0;
06846                         }
06847                 }
06848                 else if (bs->wpCurrent->index > 0)
06849                 {
06850                         if ((bs->wpTravelTime - level.time) < 7000)
06851                         {
06852                                 if ((gWPArray[bs->wpCurrent->index-1]->flags & WPFLAG_RED_FLAG) ||
06853                                         (gWPArray[bs->wpCurrent->index-1]->flags & WPFLAG_BLUE_FLAG))
06854                                 {
06855                                         if ((level.time - bs->jumpTime) > 200)
06856                                         {
06857                                                 bs->jumpTime = level.time + 100;
06858                                                 bs->jumpHoldTime = level.time + 100;
06859                                                 bs->jDelay = 0;
06860                                         }
06861                                 }
06862                         }
06863                 }
06864 
06865                 if (doJ)
06866                 {
06867                         bs->jumpTime = level.time + 1500;
06868                         bs->jumpHoldTime = level.time + 1500;
06869                         bs->jDelay = 0;
06870                 }
06871         }
06872 
06873         if (doingFallback)
06874         {
06875                 bs->doingFallback = qtrue;
06876         }
06877         else
06878         {
06879                 bs->doingFallback = qfalse;
06880         }
06881 
06882         if (bs->timeToReact < level.time && bs->currentEnemy && bs->enemySeenTime > level.time + (ENEMY_FORGET_MS - (ENEMY_FORGET_MS*0.2)))
06883         {
06884                 if (bs->frame_Enemy_Vis)
06885                 {
06886                         cBAI = CombatBotAI(bs, thinktime);
06887                 }
06888                 else if (bs->cur_ps.weaponstate == WEAPON_CHARGING_ALT)
06889                 { //keep charging in case we see him again before we lose track of him
06890                         bs->doAltAttack = 1;
06891                 }
06892                 else if (bs->cur_ps.weaponstate == WEAPON_CHARGING)
06893                 { //keep charging in case we see him again before we lose track of him
06894                         bs->doAttack = 1;
06895                 }
06896 
06897                 if (bs->destinationGrabTime > level.time + 100)
06898                 {
06899                         bs->destinationGrabTime = level.time + 100; //assures that we will continue staying within a general area of where we want to be in a combat situation
06900                 }
06901 
06902                 if (bs->currentEnemy->client)
06903                 {
06904                         VectorCopy(bs->currentEnemy->client->ps.origin, headlevel);
06905                         headlevel[2] += bs->currentEnemy->client->ps.viewheight;
06906                 }
06907                 else
06908                 {
06909                         VectorCopy(bs->currentEnemy->client->ps.origin, headlevel);
06910                 }
06911 
06912                 if (!bs->frame_Enemy_Vis)
06913                 {
06914                         //if (!bs->hitSpotted && VectorLength(a) > 256)
06915                         if (OrgVisible(bs->eye, bs->lastEnemySpotted, -1))
06916                         {
06917                                 VectorCopy(bs->lastEnemySpotted, headlevel);
06918                                 VectorSubtract(headlevel, bs->eye, a);
06919                                 vectoangles(a, ang);
06920                                 VectorCopy(ang, bs->goalAngles);
06921 
06922                                 if (bs->cur_ps.weapon == WP_FLECHETTE &&
06923                                         bs->cur_ps.weaponstate == WEAPON_READY &&
06924                                         bs->currentEnemy && bs->currentEnemy->client)
06925                                 {
06926                                         mLen = VectorLength(a) > 128;
06927                                         if (mLen > 128 && mLen < 1024)
06928                                         {
06929                                                 VectorSubtract(bs->currentEnemy->client->ps.origin, bs->lastEnemySpotted, a);
06930 
06931                                                 if (VectorLength(a) < 300)
06932                                                 {
06933                                                         bs->doAltAttack = 1;
06934                                                 }
06935                                         }
06936                                 }
06937                         }
06938                 }
06939                 else
06940                 {
06941                         bLeadAmount = BotWeaponCanLead(bs);
06942                         if ((bs->skills.accuracy/bs->settings.skill) <= 8 &&
06943                                 bLeadAmount)
06944                         {
06945                                 BotAimLeading(bs, headlevel, bLeadAmount);
06946                         }
06947                         else
06948                         {
06949                                 VectorSubtract(headlevel, bs->eye, a);
06950                                 vectoangles(a, ang);
06951                                 VectorCopy(ang, bs->goalAngles);
06952                         }
06953 
06954                         BotAimOffsetGoalAngles(bs);
06955                 }
06956         }
06957 
06958         if (bs->cur_ps.saberInFlight)
06959         {
06960                 bs->saberThrowTime = level.time + Q_irand(4000, 10000);
06961         }
06962 
06963         if (bs->currentEnemy)
06964         {
06965                 if (BotGetWeaponRange(bs) == BWEAPONRANGE_SABER)
06966                 {
06967                         int saberRange = SABER_ATTACK_RANGE;
06968 
06969                         VectorSubtract(bs->currentEnemy->client->ps.origin, bs->eye, a_fo);
06970                         vectoangles(a_fo, a_fo);
06971 
06972                         if (bs->saberPowerTime < level.time)
06973                         { //Don't just use strong attacks constantly, switch around a bit
06974                                 if (Q_irand(1, 10) <= 5)
06975                                 {
06976                                         bs->saberPower = qtrue;
06977                                 }
06978                                 else
06979                                 {
06980                                         bs->saberPower = qfalse;
06981                                 }
06982 
06983                                 bs->saberPowerTime = level.time + Q_irand(3000, 15000);
06984                         }
06985 
06986                         if ( g_entities[bs->client].client->ps.fd.saberAnimLevel != SS_STAFF
06987                                 && g_entities[bs->client].client->ps.fd.saberAnimLevel != SS_DUAL )
06988                         {
06989                                 if (bs->currentEnemy->health > 75 
06990                                         && g_entities[bs->client].client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] > 2)
06991                                 {
06992                                         if (g_entities[bs->client].client->ps.fd.saberAnimLevel != SS_STRONG 
06993                                                 && bs->saberPower)
06994                                         { //if we are up against someone with a lot of health and we have a strong attack available, then h4q them
06995                                                 Cmd_SaberAttackCycle_f(&g_entities[bs->client]);
06996                                         }
06997                                 }
06998                                 else if (bs->currentEnemy->health > 40 
06999                                         && g_entities[bs->client].client->ps.fd.forcePowerLevel[FP_SABER_OFFENSE] > 1)
07000                                 {
07001                                         if (g_entities[bs->client].client->ps.fd.saberAnimLevel != SS_MEDIUM)
07002                                         { //they're down on health a little, use level 2 if we can
07003                                                 Cmd_SaberAttackCycle_f(&g_entities[bs->client]);
07004                                         }
07005                                 }
07006                                 else
07007                                 {
07008                                         if (g_entities[bs->client].client->ps.fd.saberAnimLevel != SS_FAST)
07009                                         { //they've gone below 40 health, go at them with quick attacks
07010                                                 Cmd_SaberAttackCycle_f(&g_entities[bs->client]);
07011                                         }
07012                                 }
07013                         }
07014 
07015                         if (g_gametype.integer == GT_SINGLE_PLAYER)
07016                         {
07017                                 saberRange *= 3;
07018                         }
07019 
07020                         if (bs->frame_Enemy_Len <= saberRange)
07021                         {
07022                                 SaberCombatHandling(bs);
07023 
07024                                 if (bs->frame_Enemy_Len < 80)
07025                                 {
07026                                         meleestrafe = 1;
07027                                 }
07028                         }
07029                         else if (bs->saberThrowTime < level.time && !bs->cur_ps.saberInFlight &&
07030                                 (bs->cur_ps.fd.forcePowersKnown & (1 << FP_SABERTHROW)) &&
07031                                 InFieldOfVision(bs->viewangles, 30, a_fo) &&
07032                                 bs->frame_Enemy_Len < BOT_SABER_THROW_RANGE &&
07033                                 bs->cur_ps.fd.saberAnimLevel != SS_STAFF)
07034                         {
07035                                 bs->doAltAttack = 1;
07036                                 bs->doAttack = 0;
07037                         }
07038                         else if (bs->cur_ps.saberInFlight && bs->frame_Enemy_Len > 300 && bs->frame_Enemy_Len < BOT_SABER_THROW_RANGE)
07039                         {
07040                                 bs->doAltAttack = 1;
07041                                 bs->doAttack = 0;
07042                         }
07043                 }
07044                 else if (BotGetWeaponRange(bs) == BWEAPONRANGE_MELEE)
07045                 {
07046                         if (bs->frame_Enemy_Len <= MELEE_ATTACK_RANGE)
07047                         {
07048                                 MeleeCombatHandling(bs);
07049                                 meleestrafe = 1;
07050                         }
07051                 }
07052         }
07053 
07054         if (doingFallback && bs->currentEnemy) //just stand and fire if we have no idea where we are
07055         {
07056                 VectorCopy(bs->origin, bs->goalPosition);
07057         }
07058 
07059         if (bs->forceJumping > level.time)
07060         {
07061                 VectorCopy(bs->origin, noz_x);
07062                 VectorCopy(bs->goalPosition, noz_y);
07063 
07064                 noz_x[2] = noz_y[2];
07065 
07066                 VectorSubtract(noz_x, noz_y, noz_x);
07067 
07068                 if (VectorLength(noz_x) < 32)
07069                 {
07070                         fjHalt = 1;
07071                 }
07072         }
07073 
07074         if (bs->doChat && bs->chatTime > level.time && (!bs->currentEnemy || !bs->frame_Enemy_Vis))
07075         {
07076                 return;
07077         }
07078         else if (bs->doChat && bs->currentEnemy && bs->frame_Enemy_Vis)
07079         {
07080                 //bs->chatTime = level.time + bs->chatTime_stored;
07081                 bs->doChat = 0; //do we want to keep the bot waiting to chat until after the enemy is gone?
07082                 bs->chatTeam = 0;
07083         }
07084         else if (bs->doChat && bs->chatTime <= level.time)
07085         {
07086                 if (bs->chatTeam)
07087                 {
07088                         trap_EA_SayTeam(bs->client, bs->currentChat);
07089                         bs->chatTeam = 0;
07090                 }
07091                 else
07092                 {
07093                         trap_EA_Say(bs->client, bs->currentChat);
07094                 }
07095                 if (bs->doChat == 2)
07096                 {
07097                         BotReplyGreetings(bs);
07098                 }
07099                 bs->doChat = 0;
07100         }
07101 
07102         CTFFlagMovement(bs);
07103 
07104         if (/*bs->wpDestination &&*/ bs->shootGoal &&
07105                 /*bs->wpDestination->associated_entity == bs->shootGoal->s.number &&*/
07106                 bs->shootGoal->health > 0 && bs->shootGoal->takedamage)
07107         {
07108                 dif[0] = (bs->shootGoal->r.absmax[0]+bs->shootGoal->r.absmin[0])/2;
07109                 dif[1] = (bs->shootGoal->r.absmax[1]+bs->shootGoal->r.absmin[1])/2;
07110                 dif[2] = (bs->shootGoal->r.absmax[2]+bs->shootGoal->r.absmin[2])/2;
07111 
07112                 if (!bs->currentEnemy || bs->frame_Enemy_Len > 256)
07113                 { //if someone is close then don't stop shooting them for this
07114                         VectorSubtract(dif, bs->eye, a);
07115                         vectoangles(a, a);
07116                         VectorCopy(a, bs->goalAngles);
07117 
07118                         if (InFieldOfVision(bs->viewangles, 30, a) &&
07119                                 EntityVisibleBox(bs->origin, NULL, NULL, dif, bs->client, bs->shootGoal->s.number))
07120                         {
07121                                 bs->doAttack = 1;
07122                         }
07123                 }
07124         }
07125 
07126         if (bs->cur_ps.hasDetPackPlanted)
07127         { //check if our enemy gets near it and detonate if he does
07128                 BotCheckDetPacks(bs);
07129         }
07130         else if (bs->currentEnemy && bs->lastVisibleEnemyIndex == bs->currentEnemy->s.number && !bs->frame_Enemy_Vis && bs->plantTime < level.time &&
07131                 !bs->doAttack && !bs->doAltAttack)
07132         {
07133                 VectorSubtract(bs->origin, bs->hereWhenSpotted, a);
07134 
07135                 if (bs->plantDecided > level.time || (bs->frame_Enemy_Len < BOT_PLANT_DISTANCE*2 && VectorLength(a) < BOT_PLANT_DISTANCE))
07136                 {
07137                         mineSelect = BotSelectChoiceWeapon(bs, WP_TRIP_MINE, 0);
07138                         detSelect = BotSelectChoiceWeapon(bs, WP_DET_PACK, 0);
07139                         if (bs->cur_ps.hasDetPackPlanted)
07140                         {
07141                                 detSelect = 0;
07142                         }
07143 
07144                         if (bs->plantDecided > level.time && bs->forceWeaponSelect &&
07145                                 bs->cur_ps.weapon == bs->forceWeaponSelect)
07146                         {
07147                                 bs->doAttack = 1;
07148                                 bs->plantDecided = 0;
07149                                 bs->plantTime = level.time + BOT_PLANT_INTERVAL;
07150                                 bs->plantContinue = level.time + 500;
07151                                 bs->beStill = level.time + 500;
07152                         }
07153                         else if (mineSelect || detSelect)
07154                         {
07155                                 if (BotSurfaceNear(bs))
07156                                 {
07157                                         if (!mineSelect)
07158                                         { //if no mines use detpacks, otherwise use mines
07159                                                 mineSelect = WP_DET_PACK;
07160                                         }
07161                                         else
07162                                         {
07163                                                 mineSelect = WP_TRIP_MINE;
07164                                         }
07165 
07166                                         detSelect = BotSelectChoiceWeapon(bs, mineSelect, 1);
07167 
07168                                         if (detSelect && detSelect != 2)
07169                                         { //We have it and it is now our weapon
07170                                                 bs->plantDecided = level.time + 1000;
07171                                                 bs->forceWeaponSelect = mineSelect;
07172                                                 return;
07173                                         }
07174                                         else if (detSelect == 2)
07175                                         {
07176                                                 bs->forceWeaponSelect = mineSelect;
07177                                                 return;
07178                                         }
07179                                 }
07180                         }
07181                 }
07182         }
07183         else if (bs->plantContinue < level.time)
07184         {
07185                 bs->forceWeaponSelect = 0;
07186         }
07187 
07188         if (g_gametype.integer == GT_JEDIMASTER && !bs->cur_ps.isJediMaster && bs->jmState == -1 && gJMSaberEnt && gJMSaberEnt->inuse)
07189         {
07190                 vec3_t saberLen;
07191                 float fSaberLen = 0;
07192 
07193                 VectorSubtract(bs->origin, gJMSaberEnt->r.currentOrigin, saberLen);
07194                 fSaberLen = VectorLength(saberLen);
07195 
07196                 if (fSaberLen < 256)
07197                 {
07198                         if (OrgVisible(bs->origin, gJMSaberEnt->r.currentOrigin, bs->client))
07199                         {
07200                                 VectorCopy(gJMSaberEnt->r.currentOrigin, bs->goalPosition);
07201                         }
07202                 }
07203         }
07204 
07205         if (bs->beStill < level.time && !WaitingForNow(bs, bs->goalPosition) && !fjHalt)
07206         {
07207                 VectorSubtract(bs->goalPosition, bs->origin, bs->goalMovedir);
07208                 VectorNormalize(bs->goalMovedir);
07209 
07210                 if (bs->jumpTime > level.time && bs->jDelay < level.time &&
07211                         level.clients[bs->client].pers.cmd.upmove > 0)
07212                 {
07213                 //      trap_EA_Move(bs->client, bs->origin, 5000);
07214                         bs->beStill = level.time + 200;
07215                 }
07216                 else
07217                 {
07218                         trap_EA_Move(bs->client, bs->goalMovedir, 5000);
07219                 }
07220 
07221                 if (meleestrafe)
07222                 {
07223                         StrafeTracing(bs);
07224                 }
07225 
07226                 if (bs->meleeStrafeDir && meleestrafe && bs->meleeStrafeDisable < level.time)
07227                 {
07228                         trap_EA_MoveRight(bs->client);
07229                 }
07230                 else if (meleestrafe && bs->meleeStrafeDisable < level.time)
07231                 {
07232                         trap_EA_MoveLeft(bs->client);
07233                 }
07234 
07235                 if (BotTrace_Jump(bs, bs->goalPosition))
07236                 {
07237                         bs->jumpTime = level.time + 100;
07238                 }
07239                 else if (BotTrace_Duck(bs, bs->goalPosition))
07240                 {
07241                         bs->duckTime = level.time + 100;
07242                 }
07243 #ifdef BOT_STRAFE_AVOIDANCE
07244                 else
07245                 {
07246                         int strafeAround = BotTrace_Strafe(bs, bs->goalPosition);
07247 
07248                         if (strafeAround == STRAFEAROUND_RIGHT)
07249                         {
07250                                 trap_EA_MoveRight(bs->client);
07251                         }
07252                         else if (strafeAround == STRAFEAROUND_LEFT)
07253                         {
07254                                 trap_EA_MoveLeft(bs->client);
07255                         }
07256                 }
07257 #endif
07258         }
07259 
07260 #ifndef FORCEJUMP_INSTANTMETHOD
07261         if (bs->forceJumpChargeTime > level.time)
07262         {
07263                 bs->jumpTime = 0;
07264         }
07265 #endif
07266 
07267         if (bs->jumpPrep > level.time)
07268         {
07269                 bs->forceJumpChargeTime = 0;
07270         }
07271 
07272         if (bs->forceJumpChargeTime > level.time)
07273         {
07274                 bs->jumpHoldTime = ((bs->forceJumpChargeTime - level.time)/2) + level.time;
07275                 bs->forceJumpChargeTime = 0;
07276         }
07277 
07278         if (bs->jumpHoldTime > level.time)
07279         {
07280                 bs->jumpTime = bs->jumpHoldTime;
07281         }
07282 
07283         if (bs->jumpTime > level.time && bs->jDelay < level.time)
07284         {
07285                 if (bs->jumpHoldTime > level.time)
07286                 {
07287                         trap_EA_Jump(bs->client);
07288                         if (bs->wpCurrent)
07289                         {
07290                                 if ((bs->wpCurrent->origin[2] - bs->origin[2]) < 64)
07291                                 {
07292                                         trap_EA_MoveForward(bs->client);
07293                                 }
07294                         }
07295                         else
07296                         {
07297                                 trap_EA_MoveForward(bs->client);
07298                         }
07299                         if (g_entities[bs->client].client->ps.groundEntityNum == ENTITYNUM_NONE)
07300                         {
07301                                 g_entities[bs->client].client->ps.pm_flags |= PMF_JUMP_HELD;
07302                         }
07303                 }
07304                 else if (!(bs->cur_ps.pm_flags & PMF_JUMP_HELD))
07305                 {
07306                         trap_EA_Jump(bs->client);
07307                 }
07308         }
07309 
07310         if (bs->duckTime > level.time)
07311         {
07312                 trap_EA_Crouch(bs->client);
07313         }
07314 
07315         if ( bs->dangerousObject && bs->dangerousObject->inuse && bs->dangerousObject->health > 0 &&
07316                 bs->dangerousObject->takedamage && (!bs->frame_Enemy_Vis || !bs->currentEnemy) &&
07317                 (BotGetWeaponRange(bs) == BWEAPONRANGE_MID || BotGetWeaponRange(bs) == BWEAPONRANGE_LONG) &&
07318                 bs->cur_ps.weapon != WP_DET_PACK && bs->cur_ps.weapon != WP_TRIP_MINE &&
07319                 !bs->shootGoal )
07320         {
07321                 float danLen;
07322 
07323                 VectorSubtract(bs->dangerousObject->r.currentOrigin, bs->eye, a);
07324 
07325                 danLen = VectorLength(a);
07326 
07327                 if (danLen > 256)
07328                 {
07329                         vectoangles(a, a);
07330                         VectorCopy(a, bs->goalAngles);
07331 
07332                         if (Q_irand(1, 10) < 5)
07333                         {
07334                                 bs->goalAngles[YAW] += Q_irand(0, 3);
07335                                 bs->goalAngles[PITCH] += Q_irand(0, 3);
07336                         }
07337                         else
07338                         {
07339                                 bs->goalAngles[YAW] -= Q_irand(0, 3);
07340                                 bs->goalAngles[PITCH] -= Q_irand(0, 3);
07341                         }
07342 
07343                         if (InFieldOfVision(bs->viewangles, 30, a) &&
07344                                 EntityVisibleBox(bs->origin, NULL, NULL, bs->dangerousObject->r.currentOrigin, bs->client, bs->dangerousObject->s.number))
07345                         {
07346                                 bs->doAttack = 1;
07347                         }                       
07348                 }
07349         }
07350 
07351         if (PrimFiring(bs) ||
07352                 AltFiring(bs))
07353         {
07354                 friendInLOF = CheckForFriendInLOF(bs);
07355 
07356                 if (friendInLOF)
07357                 {
07358                         if (PrimFiring(bs))
07359                         {
07360                                 KeepPrimFromFiring(bs);
07361                         }
07362                         if (AltFiring(bs))
07363                         {
07364                                 KeepAltFromFiring(bs);
07365                         }
07366                         if (useTheForce && forceHostile)
07367                         {
07368                                 useTheForce = 0;
07369                         }
07370 
07371                         if (!useTheForce && friendInLOF->client)
07372                         { //we have a friend here and are not currently using force powers, see if we can help them out
07373                                 if (friendInLOF->health <= 50 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_TEAM_HEAL]][FP_TEAM_HEAL])
07374                                 {
07375                                         level.clients[bs->client].ps.fd.forcePowerSelected = FP_TEAM_HEAL;
07376                                         useTheForce = 1;
07377                                         forceHostile = 0;
07378                                 }
07379                                 else if (friendInLOF->client->ps.fd.forcePower <= 50 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_TEAM_FORCE]][FP_TEAM_FORCE])
07380                                 {
07381                                         level.clients[bs->client].ps.fd.forcePowerSelected = FP_TEAM_FORCE;
07382                                         useTheForce = 1;
07383                                         forceHostile = 0;
07384                                 }
07385                         }
07386                 }
07387         }
07388         else if (g_gametype.integer >= GT_TEAM)
07389         { //still check for anyone to help..
07390                 friendInLOF = CheckForFriendInLOF(bs);
07391 
07392                 if (!useTheForce && friendInLOF)
07393                 {
07394                         if (friendInLOF->health <= 50 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_TEAM_HEAL]][FP_TEAM_HEAL])
07395                         {
07396                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_TEAM_HEAL;
07397                                 useTheForce = 1;
07398                                 forceHostile = 0;
07399                         }
07400                         else if (friendInLOF->client->ps.fd.forcePower <= 50 && level.clients[bs->client].ps.fd.forcePower > forcePowerNeeded[level.clients[bs->client].ps.fd.forcePowerLevel[FP_TEAM_FORCE]][FP_TEAM_FORCE])
07401                         {
07402                                 level.clients[bs->client].ps.fd.forcePowerSelected = FP_TEAM_FORCE;
07403                                 useTheForce = 1;
07404                                 forceHostile = 0;
07405                         }
07406                 }
07407         }
07408 
07409         if (bs->doAttack && bs->cur_ps.weapon == WP_DET_PACK &&
07410                 bs->cur_ps.hasDetPackPlanted)
07411         { //maybe a bit hackish, but bots only want to plant one of these at any given time to avoid complications
07412                 bs->doAttack = 0;
07413         }
07414 
07415         if (bs->doAttack && bs->cur_ps.weapon == WP_SABER &&
07416                 bs->saberDefending && bs->currentEnemy && bs->currentEnemy->client &&
07417                 BotWeaponBlockable(bs->currentEnemy->client->ps.weapon) )
07418         {
07419                 bs->doAttack = 0;
07420         }
07421 
07422         if (bs->cur_ps.saberLockTime > level.time)
07423         {
07424                 if (rand()%10 < 5)
07425                 {
07426                         bs->doAttack = 1;
07427                 }
07428                 else
07429                 {
07430                         bs->doAttack = 0;
07431                 }
07432         }
07433 
07434         if (bs->botChallengingTime > level.time)
07435         {
07436                 bs->doAttack = 0;
07437                 bs->doAltAttack = 0;
07438         }
07439 
07440         if (bs->cur_ps.weapon == WP_SABER &&
07441                 bs->cur_ps.saberInFlight &&
07442                 !bs->cur_ps.saberEntityNum)
07443         { //saber knocked away, keep trying to get it back
07444                 bs->doAttack = 1;
07445                 bs->doAltAttack = 0;
07446         }
07447 
07448         if (bs->doAttack)
07449         {
07450                 trap_EA_Attack(bs->client);
07451         }
07452         else if (bs->doAltAttack)
07453         {
07454                 trap_EA_Alt_Attack(bs->client);
07455         }
07456 
07457         if (useTheForce && forceHostile && bs->botChallengingTime > level.time)
07458         {
07459                 useTheForce = qfalse;
07460         }
07461 
07462         if (useTheForce)
07463         {
07464 #ifndef FORCEJUMP_INSTANTMETHOD
07465                 if (bs->forceJumpChargeTime > level.time)
07466                 {
07467                         level.clients[bs->client].ps.fd.forcePowerSelected = FP_LEVITATION;
07468                         trap_EA_ForcePower(bs->client);
07469                 }
07470                 else
07471                 {
07472 #endif
07473                         if (bot_forcepowers.integer && !g_forcePowerDisable.integer)
07474                         {
07475                                 trap_EA_ForcePower(bs->client);
07476                         }
07477 #ifndef FORCEJUMP_INSTANTMETHOD
07478                 }
07479 #endif
07480         }
07481 
07482         MoveTowardIdealAngles(bs);
07483 }

void StrafeTracing bot_state_t bs  ) 
 

Definition at line 5417 of file ai_main.c.

References AngleVectors(), bot_state_t, bot_state_s::client, trace_t::fraction, level, MASK_SOLID, bot_state_s::meleeStrafeDir, bot_state_s::meleeStrafeDisable, NULL, bot_state_s::origin, Q_irand(), level_locals_t::time, trap_Trace(), vec3_t, VectorCopy, and bot_state_s::viewangles.

Referenced by StandardBotAI().

05418 {
05419         vec3_t mins, maxs;
05420         vec3_t right, rorg, drorg;
05421         trace_t tr;
05422 
05423         mins[0] = -15;
05424         mins[1] = -15;
05425         //mins[2] = -24;
05426         mins[2] = -22;
05427         maxs[0] = 15;
05428         maxs[1] = 15;
05429         maxs[2] = 32;
05430 
05431         AngleVectors(bs->viewangles, NULL, right, NULL);
05432 
05433         if (bs->meleeStrafeDir)
05434         {
05435                 rorg[0] = bs->origin[0] - right[0]*32;
05436                 rorg[1] = bs->origin[1] - right[1]*32;
05437                 rorg[2] = bs->origin[2] - right[2]*32;
05438         }
05439         else
05440         {
05441                 rorg[0] = bs->origin[0] + right[0]*32;
05442                 rorg[1] = bs->origin[1] + right[1]*32;
05443                 rorg[2] = bs->origin[2] + right[2]*32;
05444         }
05445 
05446         trap_Trace(&tr, bs->origin, mins, maxs, rorg, bs->client, MASK_SOLID);
05447 
05448         if (tr.fraction != 1)
05449         {
05450                 bs->meleeStrafeDisable = level.time + Q_irand(500, 1500);
05451         }
05452 
05453         VectorCopy(rorg, drorg);
05454 
05455         drorg[2] -= 32;
05456 
05457         trap_Trace(&tr, rorg, NULL, NULL, drorg, bs->client, MASK_SOLID);
05458 
05459         if (tr.fraction == 1)
05460         { //this may be a dangerous ledge, so don't strafe over it just in case
05461                 bs->meleeStrafeDisable = level.time + Q_irand(500, 1500);
05462         }
05463 }

float TotalTrailDistance int  start,
int  end,
bot_state_t bs
 

Definition at line 1193 of file ai_main.c.

References bot_state_t, bot_state_s::cur_ps, wpobject_s::disttonext, playerState_s::fd, wpobject_s::flags, wpobject_s::forceJumpTo, forcedata_s::forcePowerLevel, FP_LEVITATION, g_RMG, gWPArray, gWPNum, vmCvar_t::integer, wpobject_s::inuse, wpobject_s::origin, bot_state_s::wpCurrent, WPFLAG_ONEWAY_BACK, and WPFLAG_ONEWAY_FWD.

Referenced by BotGetFlagBack(), BotGuardFlagCarrier(), CheckForShorterRoutes(), GetBestIdleGoal(), GetIdealDestination(), and StandardBotAI().

01194 {
01195         int beginat;
01196         int endat;
01197         float distancetotal;
01198 
01199         distancetotal = 0;
01200 
01201         if (start > end)
01202         {
01203                 beginat = end;
01204                 endat = start;
01205         }
01206         else
01207         {
01208                 beginat = start;
01209                 endat = end;
01210         }
01211 
01212         while (beginat < endat)
01213         {
01214                 if (beginat >= gWPNum || !gWPArray[beginat] || !gWPArray[beginat]-&