00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "g_local.h"
00019 #include "q_shared.h"
00020 #include "botlib.h"
00021 #include "be_aas.h"
00022 #include "be_ea.h"
00023 #include "be_ai_char.h"
00024 #include "be_ai_chat.h"
00025 #include "be_ai_gen.h"
00026 #include "be_ai_goal.h"
00027 #include "be_ai_move.h"
00028 #include "be_ai_weap.h"
00029
00030 #include "ai_main.h"
00031 #include "w_saber.h"
00032
00033 #include "chars.h"
00034 #include "inv.h"
00035 #include "syn.h"
00036
00037
00038
00039
00040
00041 #define MAX_PATH 144
00042
00043 #define BOT_THINK_TIME 0
00044
00045
00046 bot_state_t *botstates[MAX_CLIENTS];
00047
00048 int numbots;
00049
00050 float floattime;
00051
00052 float regularupdate_time;
00053
00054
00055
00056 extern int rebel_attackers;
00057 extern int imperial_attackers;
00058
00059 boteventtracker_t gBotEventTracker[MAX_CLIENTS];
00060
00061
00062 vmCvar_t bot_forcepowers;
00063 vmCvar_t bot_forgimmick;
00064 vmCvar_t bot_honorableduelacceptance;
00065 vmCvar_t bot_pvstype;
00066 vmCvar_t bot_normgpath;
00067 #ifndef FINAL_BUILD
00068 vmCvar_t bot_getinthecarrr;
00069 #endif
00070
00071 #ifdef _DEBUG
00072 vmCvar_t bot_nogoals;
00073 vmCvar_t bot_debugmessages;
00074 #endif
00075
00076 vmCvar_t bot_attachments;
00077 vmCvar_t bot_camp;
00078
00079 vmCvar_t bot_wp_info;
00080 vmCvar_t bot_wp_edit;
00081 vmCvar_t bot_wp_clearweight;
00082 vmCvar_t bot_wp_distconnect;
00083 vmCvar_t bot_wp_visconnect;
00084
00085
00086 wpobject_t *flagRed;
00087 wpobject_t *oFlagRed;
00088 wpobject_t *flagBlue;
00089 wpobject_t *oFlagBlue;
00090
00091 gentity_t *eFlagRed;
00092 gentity_t *droppedRedFlag;
00093 gentity_t *eFlagBlue;
00094 gentity_t *droppedBlueFlag;
00095
00096 char *ctfStateNames[] = {
00097 "CTFSTATE_NONE",
00098 "CTFSTATE_ATTACKER",
00099 "CTFSTATE_DEFENDER",
00100 "CTFSTATE_RETRIEVAL",
00101 "CTFSTATE_GUARDCARRIER",
00102 "CTFSTATE_GETFLAGHOME",
00103 "CTFSTATE_MAXCTFSTATES"
00104 };
00105
00106 char *ctfStateDescriptions[] = {
00107 "I'm not occupied",
00108 "I'm attacking the enemy's base",
00109 "I'm defending our base",
00110 "I'm getting our flag back",
00111 "I'm escorting our flag carrier",
00112 "I've got the enemy's flag"
00113 };
00114
00115 char *siegeStateDescriptions[] = {
00116 "I'm not occupied",
00117 "I'm attemtping to complete the current objective",
00118 "I'm preventing the enemy from completing their objective"
00119 };
00120
00121 char *teamplayStateDescriptions[] = {
00122 "I'm not occupied",
00123 "I'm following my squad commander",
00124 "I'm assisting my commanding",
00125 "I'm attempting to regroup and form a new squad"
00126 };
00127
00128 void BotStraightTPOrderCheck(gentity_t *ent, int ordernum, bot_state_t *bs)
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 }
00156
00157 void BotSelectWeapon(int client, int weapon)
00158 {
00159 if (weapon <= WP_NONE)
00160 {
00161
00162 return;
00163 }
00164 trap_EA_SelectWeapon(client, weapon);
00165 }
00166
00167 void BotReportStatus(bot_state_t *bs)
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 }
00182
00183
00184 void BotOrder(gentity_t *ent, int clientnum, int ordernum)
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 }
00277
00278
00279 int BotMindTricked(int botClient, int enemyClient)
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 }
00326
00327 int BotGetWeaponRange(bot_state_t *bs);
00328 int PassLovedOneCheck(bot_state_t *bs, gentity_t *ent);
00329
00330 void ExitLevel( void );
00331
00332 void QDECL BotAI_Print(int type, char *fmt, ...) { return; }
00333
00334 qboolean WP_ForcePowerUsable( gentity_t *self, forcePowers_t forcePower );
00335
00336 int IsTeamplay(void)
00337 {
00338 if ( g_gametype.integer < GT_TEAM )
00339 {
00340 return 0;
00341 }
00342
00343 return 1;
00344 }
00345
00346
00347
00348
00349
00350
00351 int BotAI_GetClientState( int clientNum, playerState_t *state ) {
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 }
00365
00366
00367
00368
00369
00370
00371 int BotAI_GetEntityState( int entityNum, entityState_t *state ) {
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 }
00382
00383
00384
00385
00386
00387
00388 int BotAI_GetSnapshotEntity( int clientNum, int sequence, entityState_t *state ) {
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 }
00401
00402
00403
00404
00405
00406
00407 void BotEntityInfo(int entnum, aas_entityinfo_t *info) {
00408 trap_AAS_EntityInfo(entnum, info);
00409 }
00410
00411
00412
00413
00414
00415
00416 int NumBots(void) {
00417 return numbots;
00418 }
00419
00420
00421
00422
00423
00424
00425 float AngleDifference(float ang1, float ang2) {
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 }
00437
00438
00439
00440
00441
00442
00443 float BotChangeViewAngle(float angle, float ideal_angle, float speed) {
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 }
00464
00465
00466
00467
00468
00469
00470 void BotChangeViewAngles(bot_state_t *bs, float thinktime) {
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
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 }
00531
00532
00533
00534
00535
00536
00537 void BotInputToUserCommand(bot_input_t *bi, usercmd_t *ucmd, int delta_angles[3], int time, int useTime) {
00538 vec3_t angles, forward, right;
00539 short temp;
00540 int j;
00541
00542
00543 memset(ucmd, 0, sizeof(usercmd_t));
00544
00545
00546
00547 ucmd->serverTime = time;
00548
00549 if (bi->actionflags & ACTION_DELAYEDJUMP) {
00550 bi->actionflags |= ACTION_JUMP;
00551 bi->actionflags &= ~ACTION_DELAYEDJUMP;
00552 }
00553
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
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 {
00566 ucmd->buttons |= BUTTON_USE;
00567 }
00568 #if 0
00569
00570
00571
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
00584 #endif
00585 bi->weapon = WP_BRYAR_PISTOL;
00586 }
00587
00588
00589 ucmd->weapon = bi->weapon;
00590
00591
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
00596 for (j = 0; j < 3; j++) {
00597 temp = ucmd->angles[j] - delta_angles[j];
00598 ucmd->angles[j] = temp;
00599 }
00600
00601
00602
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
00609 bi->speed = bi->speed * 127 / 400;
00610
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
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
00620 if (bi->actionflags & ACTION_JUMP) ucmd->upmove += 127;
00621
00622 if (bi->actionflags & ACTION_CROUCH) ucmd->upmove -= 127;
00623
00624
00625
00626 }
00627
00628
00629
00630
00631
00632
00633 void BotUpdateInput(bot_state_t *bs, int time, int elapsed_time) {
00634 bot_input_t bi;
00635 int j;
00636
00637
00638 for (j = 0; j < 3; j++) {
00639 bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00640 }
00641
00642 BotChangeViewAngles(bs, (float) elapsed_time / 1000);
00643
00644 trap_EA_GetInput(bs->client, (float) time / 1000, &bi);
00645
00646 if (bi.actionflags & ACTION_RESPAWN) {
00647 if (bs->lastucmd.buttons & BUTTON_ATTACK) bi.actionflags &= ~(ACTION_RESPAWN|ACTION_ATTACK);
00648 }
00649
00650 BotInputToUserCommand(&bi, &bs->lastucmd, bs->cur_ps.delta_angles, time, bs->noUseTime);
00651
00652 for (j = 0; j < 3; j++) {
00653 bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00654 }
00655 }
00656
00657
00658
00659
00660
00661
00662 void BotAIRegularUpdate(void) {
00663 if (regularupdate_time < FloatTime()) {
00664 trap_BotUpdateEntityItems();
00665 regularupdate_time = FloatTime() + 0.3;
00666 }
00667 }
00668
00669
00670
00671
00672
00673
00674 void RemoveColorEscapeSequences( char *text ) {
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 }
00689
00690
00691
00692
00693
00694
00695
00696 int BotAI(int client, float thinktime) {
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
00714 BotAI_GetClientState( client, &bs->cur_ps );
00715
00716
00717 while( trap_BotGetServerCommand(client, buf, sizeof(buf)) ) {
00718
00719 args = strchr( buf, ' ');
00720 if (!args) continue;
00721 *args++ = '\0';
00722
00723
00724 RemoveColorEscapeSequences( args );
00725
00726 if (!Q_stricmp(buf, "cp "))
00727 { }
00728 else if (!Q_stricmp(buf, "cs"))
00729 { }
00730 else if (!Q_stricmp(buf, "scores"))
00731 { }
00732 else if (!Q_stricmp(buf, "clientLevelShot"))
00733 { }
00734 }
00735
00736 for (j = 0; j < 3; j++) {
00737 bs->viewangles[j] = AngleMod(bs->viewangles[j] + SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00738 }
00739
00740 bs->ltime += thinktime;
00741
00742 bs->thinktime = thinktime;
00743
00744 VectorCopy(bs->cur_ps.origin, bs->origin);
00745
00746 VectorCopy(bs->cur_ps.origin, bs->eye);
00747 bs->eye[2] += bs->cur_ps.viewheight;
00748
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
00766 for (j = 0; j < 3; j++) {
00767 bs->viewangles[j] = AngleMod(bs->viewangles[j] - SHORT2ANGLE(bs->cur_ps.delta_angles[j]));
00768 }
00769
00770 return qtrue;
00771 }
00772
00773
00774
00775
00776
00777
00778 void BotScheduleBotThink(void) {
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
00788 botstates[i]->botthink_residual = BOT_THINK_TIME * botnum / numbots;
00789 botnum++;
00790 }
00791 }
00792
00793 int PlayersInGame(void)
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 }
00813
00814
00815
00816
00817
00818
00819 int BotAISetupClient(int client, struct bot_settings_s *settings, qboolean restart) {
00820 bot_state_t *bs;
00821
00822 if (!botstates[client]) botstates[client] = (bot_state_t *) B_Alloc(sizeof(bot_state_t));
00823
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;
00837
00838
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
00863 bs->gs = trap_BotAllocGoalState(client);
00864
00865
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
00876 BotScheduleBotThink();
00877
00878 if (PlayersInGame())
00879 {
00880 BotDoChat(bs, "GeneralGreetings", 0);
00881 }
00882
00883 return qtrue;
00884 }
00885
00886
00887
00888
00889
00890
00891 int BotAIShutdownClient(int client, qboolean restart) {
00892 bot_state_t *bs;
00893
00894 bs = botstates[client];
00895 if (!bs || !bs->inuse) {
00896
00897 return qfalse;
00898 }
00899
00900 trap_BotFreeMoveState(bs->ms);
00901
00902 trap_BotFreeGoalState(bs->gs);
00903
00904 trap_BotFreeWeaponState(bs->ws);
00905
00906
00907 memset(bs, 0, sizeof(bot_state_t));
00908
00909 bs->inuse = qfalse;
00910
00911 numbots--;
00912
00913 return qtrue;
00914 }
00915
00916
00917
00918
00919
00920
00921
00922
00923
00924 void BotResetState(bot_state_t *bs) {
00925 int client, entitynum, inuse;
00926 int movestate, goalstate, weaponstate;
00927 bot_settings_t settings;
00928 playerState_t ps;
00929 float entergame_time;
00930
00931
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
00942 memset(bs, 0, sizeof(bot_state_t));
00943
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
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 }
00960
00961
00962
00963
00964
00965
00966 int BotAILoadMap( int restart ) {
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 }
00978
00979
00980
00981
00982 int OrgVisible(vec3_t org1, vec3_t org2, int ignore)
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 }
00995
00996
00997 int WPOrgVisible(gentity_t *bot, vec3_t org1, vec3_t org2, int ignore)
00998 {
00999 trace_t tr;
01000 gentity_t *ownent;
01001
01002 trap_Trace(&tr, org1, NULL, NULL, org2, ignore, MASK_SOLID);
01003
01004 if (tr.fraction == 1)
01005 {
01006 trap_Trace(&tr, org1, NULL, NULL, org2, ignore, MASK_PLAYERSOLID);
01007
01008 if (tr.fraction != 1 && tr.entityNum != ENTITYNUM_NONE && g_entities[tr.entityNum].s.eType == ET_SPECIAL)
01009 {
01010 if (g_entities[tr.entityNum].parent && g_entities[tr.entityNum].parent->client)
01011 {
01012 ownent = g_entities[tr.entityNum].parent;
01013
01014 if (OnSameTeam(bot, ownent) || bot->s.number == ownent->s.number)
01015 {
01016 return 1;
01017 }
01018 }
01019 return 2;
01020 }
01021
01022 return 1;
01023 }
01024
01025 return 0;
01026 }
01027
01028
01029 int OrgVisibleBox(vec3_t org1, vec3_t mins, vec3_t maxs, vec3_t org2, int ignore)
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 }
01049
01050
01051
01052
01053 int CheckForFunc(vec3_t org, int ignore)
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;
01080 }
01081
01082 return 0;
01083 }
01084
01085
01086 qboolean BotPVSCheck( const vec3_t p1, const vec3_t p2 )
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 }
01102
01103
01104 int GetNearestVisibleWP(vec3_t org, int ignore)
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;
01120
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 }
01150
01151
01152
01153
01154
01155
01156
01157 int PassWayCheck(bot_state_t *bs, int windex)
01158 {
01159 if (!gWPArray[windex] || !gWPArray[windex]->inuse)
01160 {
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 {
01169 return 1;
01170 }
01171 }
01172
01173 if (bs->wpDirection && (gWPArray[windex]->flags & WPFLAG_ONEWAY_FWD))
01174 {
01175 return 0;
01176 }
01177 else if (!bs->wpDirection && (gWPArray[windex]->flags & WPFLAG_ONEWAY_BACK))
01178 {
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 {
01186 return 0;
01187 }
01188
01189 return 1;
01190 }
01191
01192
01193 float TotalTrailDistance(int start, int end, bot_state_t *bs)
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]->inuse)
01215 {
01216 return -1;
01217 }
01218
01219 if (!g_RMG.integer)
01220 {
01221 if ((end > start && gWPArray[beginat]->flags & WPFLAG_ONEWAY_BACK) ||
01222 (start > end && gWPArray[beginat]->flags & WPFLAG_ONEWAY_FWD))
01223 {
01224 return -1;
01225 }
01226 }
01227
01228 #if 0 //disabled force jump checks for now
01229 if (gWPArray[beginat]->forceJumpTo)
01230 {
01231 if (gWPArray[beginat-1] && gWPArray[beginat-1]->origin[2]+64 < gWPArray[beginat]->origin[2])
01232 {
01233 gdif = gWPArray[beginat]->origin[2] - gWPArray[beginat-1]->origin[2];
01234 }
01235
01236 if (gdif)
01237 {
01238 if (bs && bs->cur_ps.fd.forcePowerLevel[FP_LEVITATION] < gWPArray[beginat]->forceJumpTo)
01239 {
01240 return -1;
01241 }
01242 }
01243 }
01244
01245 if (bs->wpCurrent && gWPArray[windex]->forceJumpTo &&
01246 gWPArray[windex]->origin[2] > (bs->wpCurrent->origin[2]+64) &&
01247 bs->cur_ps.fd.forcePowerLevel[FP_LEVITATION] < gWPArray[windex]->forceJumpTo)
01248 {
01249 return -1;
01250 }
01251 #endif
01252
01253 distancetotal += gWPArray[beginat]->disttonext;
01254
01255 beginat++;
01256 }
01257
01258 return distancetotal;
01259 }
01260
01261
01262
01263 void CheckForShorterRoutes(bot_state_t *bs, int newwpindex)
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
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
01290 if (bs->wpSwitchTime > level.time)
01291 {
01292 return;
01293 }
01294
01295
01296 if (!gWPArray[newwpindex]->neighbornum)
01297 {
01298 return;
01299 }
01300
01301
01302 bestindex = newwpindex;
01303 bestlen = TotalTrailDistance(newwpindex, bs->wpDestination->index, bs);
01304
01305 while (i < gWPArray[newwpindex]->neighbornum)
01306 {
01307 checklen = TotalTrailDistance(gWPArray[newwpindex]->neighbors[i].num, bs->wpDestination->index, bs);
01308
01309 if (checklen < bestlen-64 || bestlen == -1)
01310 {
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 {
01332 bs->wpCurrent = gWPArray[bestindex];
01333 bs->wpSwitchTime = level.time + 3000;
01334
01335 if (fj)
01336 {
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