00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include "../game/q_shared.h"
00033 #include "l_memory.h"
00034 #include "l_libvar.h"
00035 #include "l_utils.h"
00036 #include "l_script.h"
00037 #include "l_precomp.h"
00038 #include "l_struct.h"
00039 #include "aasfile.h"
00040 #include "../game/botlib.h"
00041 #include "../game/be_aas.h"
00042 #include "be_aas_funcs.h"
00043 #include "be_interface.h"
00044
00045 #include "../game/be_ea.h"
00046 #include "../game/be_ai_goal.h"
00047 #include "../game/be_ai_move.h"
00048
00049
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059 typedef struct bot_movestate_s
00060 {
00061
00062 vec3_t origin;
00063 vec3_t velocity;
00064 vec3_t viewoffset;
00065 int entitynum;
00066 int client;
00067 float thinktime;
00068 int presencetype;
00069 vec3_t viewangles;
00070
00071 int areanum;
00072 int lastareanum;
00073 int lastgoalareanum;
00074 int lastreachnum;
00075 vec3_t lastorigin;
00076 int reachareanum;
00077 int moveflags;
00078 int jumpreach;
00079 float grapplevisible_time;
00080 float lastgrappledist;
00081 float reachability_time;
00082 int avoidreach[MAX_AVOIDREACH];
00083 float avoidreachtimes[MAX_AVOIDREACH];
00084 int avoidreachtries[MAX_AVOIDREACH];
00085
00086 bot_avoidspot_t avoidspots[MAX_AVOIDSPOTS];
00087 int numavoidspots;
00088 } bot_movestate_t;
00089
00090
00091 #define AVOIDREACH
00092 #define AVOIDREACH_TIME 6 //avoid links for 6 seconds after use
00093 #define AVOIDREACH_TRIES 4
00094
00095 #define PREDICTIONTIME_JUMP 3 //in seconds
00096 #define PREDICTIONTIME_MOVE 2 //in seconds
00097
00098 #define WEAPONINDEX_ROCKET_LAUNCHER 5
00099 #define WEAPONINDEX_BFG 9
00100
00101 #define MODELTYPE_FUNC_PLAT 1
00102 #define MODELTYPE_FUNC_BOB 2
00103 #define MODELTYPE_FUNC_DOOR 3
00104 #define MODELTYPE_FUNC_STATIC 4
00105
00106 libvar_t *sv_maxstep;
00107 libvar_t *sv_maxbarrier;
00108 libvar_t *sv_gravity;
00109 libvar_t *weapindex_rocketlauncher;
00110 libvar_t *weapindex_bfg10k;
00111 libvar_t *weapindex_grapple;
00112 libvar_t *entitytypemissile;
00113 libvar_t *offhandgrapple;
00114 libvar_t *cmd_grappleoff;
00115 libvar_t *cmd_grappleon;
00116
00117 int modeltypes[MAX_MODELS];
00118
00119 bot_movestate_t *botmovestates[MAX_CLIENTS+1];
00120
00121
00122
00123
00124
00125
00126
00127 int BotAllocMoveState(void)
00128 {
00129 int i;
00130
00131 for (i = 1; i <= MAX_CLIENTS; i++)
00132 {
00133 if (!botmovestates[i])
00134 {
00135 botmovestates[i] = GetClearedMemory(sizeof(bot_movestate_t));
00136 return i;
00137 }
00138 }
00139 return 0;
00140 }
00141
00142
00143
00144
00145
00146
00147 void BotFreeMoveState(int handle)
00148 {
00149 if (handle <= 0 || handle > MAX_CLIENTS)
00150 {
00151 botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
00152 return;
00153 }
00154 if (!botmovestates[handle])
00155 {
00156 botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
00157 return;
00158 }
00159 FreeMemory(botmovestates[handle]);
00160 botmovestates[handle] = NULL;
00161 }
00162
00163
00164
00165
00166
00167
00168 bot_movestate_t *BotMoveStateFromHandle(int handle)
00169 {
00170 if (handle <= 0 || handle > MAX_CLIENTS)
00171 {
00172 botimport.Print(PRT_FATAL, "move state handle %d out of range\n", handle);
00173 return NULL;
00174 }
00175 if (!botmovestates[handle])
00176 {
00177 botimport.Print(PRT_FATAL, "invalid move state %d\n", handle);
00178 return NULL;
00179 }
00180 return botmovestates[handle];
00181 }
00182
00183
00184
00185
00186
00187
00188 void BotInitMoveState(int handle, bot_initmove_t *initmove)
00189 {
00190 bot_movestate_t *ms;
00191
00192 ms = BotMoveStateFromHandle(handle);
00193 if (!ms) return;
00194 VectorCopy(initmove->origin, ms->origin);
00195 VectorCopy(initmove->velocity, ms->velocity);
00196 VectorCopy(initmove->viewoffset, ms->viewoffset);
00197 ms->entitynum = initmove->entitynum;
00198 ms->client = initmove->client;
00199 ms->thinktime = initmove->thinktime;
00200 ms->presencetype = initmove->presencetype;
00201 VectorCopy(initmove->viewangles, ms->viewangles);
00202
00203 ms->moveflags &= ~MFL_ONGROUND;
00204 if (initmove->or_moveflags & MFL_ONGROUND) ms->moveflags |= MFL_ONGROUND;
00205 ms->moveflags &= ~MFL_TELEPORTED;
00206 if (initmove->or_moveflags & MFL_TELEPORTED) ms->moveflags |= MFL_TELEPORTED;
00207 ms->moveflags &= ~MFL_WATERJUMP;
00208 if (initmove->or_moveflags & MFL_WATERJUMP) ms->moveflags |= MFL_WATERJUMP;
00209 ms->moveflags &= ~MFL_WALK;
00210 if (initmove->or_moveflags & MFL_WALK) ms->moveflags |= MFL_WALK;
00211 ms->moveflags &= ~MFL_GRAPPLEPULL;
00212 if (initmove->or_moveflags & MFL_GRAPPLEPULL) ms->moveflags |= MFL_GRAPPLEPULL;
00213 }
00214
00215
00216
00217
00218
00219
00220 float AngleDiff(float ang1, float ang2)
00221 {
00222 float diff;
00223
00224 diff = ang1 - ang2;
00225 if (ang1 > ang2)
00226 {
00227 if (diff > 180.0) diff -= 360.0;
00228 }
00229 else
00230 {
00231 if (diff < -180.0) diff += 360.0;
00232 }
00233 return diff;
00234 }
00235
00236
00237
00238
00239
00240
00241 int BotFuzzyPointReachabilityArea(vec3_t origin)
00242 {
00243 int firstareanum, j, x, y, z;
00244 int areas[10], numareas, areanum, bestareanum;
00245 float dist, bestdist;
00246 vec3_t points[10], v, end;
00247
00248 firstareanum = 0;
00249 areanum = AAS_PointAreaNum(origin);
00250 if (areanum)
00251 {
00252 firstareanum = areanum;
00253 if (AAS_AreaReachability(areanum)) return areanum;
00254 }
00255 VectorCopy(origin, end);
00256 end[2] += 4;
00257 numareas = AAS_TraceAreas(origin, end, areas, points, 10);
00258 for (j = 0; j < numareas; j++)
00259 {
00260 if (AAS_AreaReachability(areas[j])) return areas[j];
00261 }
00262 bestdist = 999999;
00263 bestareanum = 0;
00264 for (z = 1; z >= -1; z -= 1)
00265 {
00266 for (x = 1; x >= -1; x -= 1)
00267 {
00268 for (y = 1; y >= -1; y -= 1)
00269 {
00270 VectorCopy(origin, end);
00271 end[0] += x * 8;
00272 end[1] += y * 8;
00273 end[2] += z * 12;
00274 numareas = AAS_TraceAreas(origin, end, areas, points, 10);
00275 for (j = 0; j < numareas; j++)
00276 {
00277 if (AAS_AreaReachability(areas[j]))
00278 {
00279 VectorSubtract(points[j], origin, v);
00280 dist = VectorLength(v);
00281 if (dist < bestdist)
00282 {
00283 bestareanum = areas[j];
00284 bestdist = dist;
00285 }
00286 }
00287 if (!firstareanum) firstareanum = areas[j];
00288 }
00289 }
00290 }
00291 if (bestareanum) return bestareanum;
00292 }
00293 return firstareanum;
00294 }
00295
00296
00297
00298
00299
00300
00301 int BotReachabilityArea(vec3_t origin, int client)
00302 {
00303 int modelnum, modeltype, reachnum, areanum;
00304 aas_reachability_t reach;
00305 vec3_t org, end, mins, maxs, up = {0, 0, 1};
00306 bsp_trace_t bsptrace;
00307 aas_trace_t trace;
00308
00309
00310 AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs);
00311 VectorMA(origin, -3, up, end);
00312 bsptrace = AAS_Trace(origin, mins, maxs, end, client, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00313 if (!bsptrace.startsolid && bsptrace.fraction < 1 && bsptrace.ent != ENTITYNUM_NONE)
00314 {
00315
00316 if (bsptrace.ent == ENTITYNUM_WORLD)
00317 {
00318 return BotFuzzyPointReachabilityArea(origin);
00319 }
00320
00321 modelnum = AAS_EntityModelindex(bsptrace.ent);
00322 modeltype = modeltypes[modelnum];
00323
00324
00325
00326 if (modeltype == MODELTYPE_FUNC_PLAT || modeltype == MODELTYPE_FUNC_BOB)
00327 {
00328 reachnum = AAS_NextModelReachability(0, modelnum);
00329 if (reachnum)
00330 {
00331 AAS_ReachabilityFromNum(reachnum, &reach);
00332 return reach.areanum;
00333 }
00334 }
00335
00336
00337 if (AAS_Swimming(origin))
00338 {
00339 return BotFuzzyPointReachabilityArea(origin);
00340 }
00341
00342 areanum = BotFuzzyPointReachabilityArea(origin);
00343
00344 if (areanum && AAS_AreaReachability(areanum)) return areanum;
00345
00346 VectorCopy(origin, org);
00347 VectorCopy(org, end);
00348 end[2] -= 800;
00349 trace = AAS_TraceClientBBox(org, end, PRESENCE_CROUCH, -1);
00350 if (!trace.startsolid)
00351 {
00352 VectorCopy(trace.endpos, org);
00353 }
00354
00355 return BotFuzzyPointReachabilityArea(org);
00356 }
00357
00358 return BotFuzzyPointReachabilityArea(origin);
00359 }
00360
00361
00362
00363
00364
00365
00366
00367
00368
00369
00370
00371
00372
00373
00374
00375
00376
00377
00378
00379
00380
00381
00382
00383
00384
00385
00386
00387
00388
00389
00390
00391
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402
00403
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422
00423
00424
00425
00426
00427
00428
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442 int BotOnMover(vec3_t origin, int entnum, aas_reachability_t *reach)
00443 {
00444 int i, modelnum;
00445 vec3_t mins, maxs, modelorigin, org, end;
00446 vec3_t angles = {0, 0, 0};
00447 vec3_t boxmins = {-16, -16, -8}, boxmaxs = {16, 16, 8};
00448 bsp_trace_t trace;
00449
00450 modelnum = reach->facenum & 0x0000FFFF;
00451
00452 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
00453
00454 if (!AAS_OriginOfMoverWithModelNum(modelnum, modelorigin))
00455 {
00456 botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
00457 return qfalse;
00458 }
00459
00460 for (i = 0; i < 2; i++)
00461 {
00462 if (origin[i] > modelorigin[i] + maxs[i] + 16) return qfalse;
00463 if (origin[i] < modelorigin[i] + mins[i] - 16) return qfalse;
00464 }
00465
00466 VectorCopy(origin, org);
00467 org[2] += 24;
00468 VectorCopy(origin, end);
00469 end[2] -= 48;
00470
00471 trace = AAS_Trace(org, boxmins, boxmaxs, end, entnum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00472 if (!trace.startsolid && !trace.allsolid)
00473 {
00474
00475 if (trace.ent != ENTITYNUM_NONE && AAS_EntityModelNum(trace.ent) == modelnum)
00476 {
00477 return qtrue;
00478 }
00479 }
00480 return qfalse;
00481 }
00482
00483
00484
00485
00486
00487
00488 int MoverDown(aas_reachability_t *reach)
00489 {
00490 int modelnum;
00491 vec3_t mins, maxs, origin;
00492 vec3_t angles = {0, 0, 0};
00493
00494 modelnum = reach->facenum & 0x0000FFFF;
00495
00496 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
00497
00498 if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
00499 {
00500 botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
00501 return qfalse;
00502 }
00503
00504 if (origin[2] + maxs[2] < reach->start[2]) return qtrue;
00505 return qfalse;
00506 }
00507
00508
00509
00510
00511
00512
00513 void BotSetBrushModelTypes(void)
00514 {
00515 int ent, modelnum;
00516 char classname[MAX_EPAIRKEY], model[MAX_EPAIRKEY];
00517
00518 Com_Memset(modeltypes, 0, MAX_MODELS * sizeof(int));
00519
00520 for (ent = AAS_NextBSPEntity(0); ent; ent = AAS_NextBSPEntity(ent))
00521 {
00522 if (!AAS_ValueForBSPEpairKey(ent, "classname", classname, MAX_EPAIRKEY)) continue;
00523 if (!AAS_ValueForBSPEpairKey(ent, "model", model, MAX_EPAIRKEY)) continue;
00524 if (model[0]) modelnum = atoi(model+1);
00525 else modelnum = 0;
00526
00527 if (modelnum < 0 || modelnum > MAX_MODELS)
00528 {
00529 botimport.Print(PRT_MESSAGE, "entity %s model number out of range\n", classname);
00530 continue;
00531 }
00532
00533 if (!Q_stricmp(classname, "func_bobbing"))
00534 modeltypes[modelnum] = MODELTYPE_FUNC_BOB;
00535 else if (!Q_stricmp(classname, "func_plat"))
00536 modeltypes[modelnum] = MODELTYPE_FUNC_PLAT;
00537 else if (!Q_stricmp(classname, "func_door"))
00538 modeltypes[modelnum] = MODELTYPE_FUNC_DOOR;
00539 else if (!Q_stricmp(classname, "func_static"))
00540 modeltypes[modelnum] = MODELTYPE_FUNC_STATIC;
00541 }
00542 }
00543
00544
00545
00546
00547
00548
00549 int BotOnTopOfEntity(bot_movestate_t *ms)
00550 {
00551 vec3_t mins, maxs, end, up = {0, 0, 1};
00552 bsp_trace_t trace;
00553
00554 AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
00555 VectorMA(ms->origin, -3, up, end);
00556 trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00557 if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
00558 {
00559 return trace.ent;
00560 }
00561 return -1;
00562 }
00563
00564
00565
00566
00567
00568
00569 int BotValidTravel(vec3_t origin, aas_reachability_t *reach, int travelflags)
00570 {
00571
00572 if (AAS_TravelFlagForType(reach->traveltype) & ~travelflags) return qfalse;
00573
00574 if (AAS_AreaContentsTravelFlags(reach->areanum) & ~travelflags) return qfalse;
00575 return qtrue;
00576 }
00577
00578
00579
00580
00581
00582
00583 void BotAddToAvoidReach(bot_movestate_t *ms, int number, float avoidtime)
00584 {
00585 int i;
00586
00587 for (i = 0; i < MAX_AVOIDREACH; i++)
00588 {
00589 if (ms->avoidreach[i] == number)
00590 {
00591 if (ms->avoidreachtimes[i] > AAS_Time()) ms->avoidreachtries[i]++;
00592 else ms->avoidreachtries[i] = 1;
00593 ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
00594 return;
00595 }
00596 }
00597
00598 for (i = 0; i < MAX_AVOIDREACH; i++)
00599 {
00600 if (ms->avoidreachtimes[i] < AAS_Time())
00601 {
00602 ms->avoidreach[i] = number;
00603 ms->avoidreachtimes[i] = AAS_Time() + avoidtime;
00604 ms->avoidreachtries[i] = 1;
00605 return;
00606 }
00607 }
00608 }
00609
00610
00611
00612
00613
00614
00615 float DistanceFromLineSquared(vec3_t p, vec3_t lp1, vec3_t lp2)
00616 {
00617 vec3_t proj, dir;
00618 int j;
00619
00620 AAS_ProjectPointOntoVector(p, lp1, lp2, proj);
00621 for (j = 0; j < 3; j++)
00622 if ((proj[j] > lp1[j] && proj[j] > lp2[j]) ||
00623 (proj[j] < lp1[j] && proj[j] < lp2[j]))
00624 break;
00625 if (j < 3) {
00626 if (fabs(proj[j] - lp1[j]) < fabs(proj[j] - lp2[j]))
00627 VectorSubtract(p, lp1, dir);
00628 else
00629 VectorSubtract(p, lp2, dir);
00630 return VectorLengthSquared(dir);
00631 }
00632 VectorSubtract(p, proj, dir);
00633 return VectorLengthSquared(dir);
00634 }
00635
00636
00637
00638
00639
00640
00641 float VectorDistanceSquared(vec3_t p1, vec3_t p2)
00642 {
00643 vec3_t dir;
00644 VectorSubtract(p2, p1, dir);
00645 return VectorLengthSquared(dir);
00646 }
00647
00648
00649
00650
00651
00652
00653 int BotAvoidSpots(vec3_t origin, aas_reachability_t *reach, bot_avoidspot_t *avoidspots, int numavoidspots)
00654 {
00655 int checkbetween, i, type;
00656 float squareddist, squaredradius;
00657
00658 switch(reach->traveltype & TRAVELTYPE_MASK)
00659 {
00660 case TRAVEL_WALK: checkbetween = qtrue; break;
00661 case TRAVEL_CROUCH: checkbetween = qtrue; break;
00662 case TRAVEL_BARRIERJUMP: checkbetween = qtrue; break;
00663 case TRAVEL_LADDER: checkbetween = qtrue; break;
00664 case TRAVEL_WALKOFFLEDGE: checkbetween = qfalse; break;
00665 case TRAVEL_JUMP: checkbetween = qfalse; break;
00666 case TRAVEL_SWIM: checkbetween = qtrue; break;
00667 case TRAVEL_WATERJUMP: checkbetween = qtrue; break;
00668 case TRAVEL_TELEPORT: checkbetween = qfalse; break;
00669 case TRAVEL_ELEVATOR: checkbetween = qfalse; break;
00670 case TRAVEL_GRAPPLEHOOK: checkbetween = qfalse; break;
00671 case TRAVEL_ROCKETJUMP: checkbetween = qfalse; break;
00672 case TRAVEL_BFGJUMP: checkbetween = qfalse; break;
00673 case TRAVEL_JUMPPAD: checkbetween = qfalse; break;
00674 case TRAVEL_FUNCBOB: checkbetween = qfalse; break;
00675 default: checkbetween = qtrue; break;
00676 }
00677
00678 type = AVOID_CLEAR;
00679 for (i = 0; i < numavoidspots; i++)
00680 {
00681 squaredradius = Square(avoidspots[i].radius);
00682 squareddist = DistanceFromLineSquared(avoidspots[i].origin, origin, reach->start);
00683
00684 if (squareddist < squaredradius &&
00685 VectorDistanceSquared(avoidspots[i].origin, origin) > squareddist)
00686 {
00687 type = avoidspots[i].type;
00688 }
00689 else if (checkbetween) {
00690 squareddist = DistanceFromLineSquared(avoidspots[i].origin, reach->start, reach->end);
00691
00692 if (squareddist < squaredradius &&
00693 VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
00694 {
00695 type = avoidspots[i].type;
00696 }
00697 }
00698 else
00699 {
00700 VectorDistanceSquared(avoidspots[i].origin, reach->end);
00701
00702 if (squareddist < squaredradius &&
00703 VectorDistanceSquared(avoidspots[i].origin, reach->start) > squareddist)
00704 {
00705 type = avoidspots[i].type;
00706 }
00707 }
00708 if (type == AVOID_ALWAYS)
00709 return type;
00710 }
00711 return type;
00712 }
00713
00714
00715
00716
00717
00718
00719 void BotAddAvoidSpot(int movestate, vec3_t origin, float radius, int type)
00720 {
00721 bot_movestate_t *ms;
00722
00723 ms = BotMoveStateFromHandle(movestate);
00724 if (!ms) return;
00725 if (type == AVOID_CLEAR)
00726 {
00727 ms->numavoidspots = 0;
00728 return;
00729 }
00730
00731 if (ms->numavoidspots >= MAX_AVOIDSPOTS)
00732 return;
00733 VectorCopy(origin, ms->avoidspots[ms->numavoidspots].origin);
00734 ms->avoidspots[ms->numavoidspots].radius = radius;
00735 ms->avoidspots[ms->numavoidspots].type = type;
00736 ms->numavoidspots++;
00737 }
00738
00739
00740
00741
00742
00743
00744 int BotGetReachabilityToGoal(vec3_t origin, int areanum,
00745 int lastgoalareanum, int lastareanum,
00746 int *avoidreach, float *avoidreachtimes, int *avoidreachtries,
00747 bot_goal_t *goal, int travelflags, int movetravelflags,
00748 struct bot_avoidspot_s *avoidspots, int numavoidspots, int *flags)
00749 {
00750 int i, t, besttime, bestreachnum, reachnum;
00751 aas_reachability_t reach;
00752
00753
00754 if (!areanum) return 0;
00755
00756 if (AAS_AreaDoNotEnter(areanum) || AAS_AreaDoNotEnter(goal->areanum))
00757 {
00758 travelflags |= TFL_DONOTENTER;
00759 movetravelflags |= TFL_DONOTENTER;
00760 }
00761
00762 besttime = 0;
00763 bestreachnum = 0;
00764
00765 for (reachnum = AAS_NextAreaReachability(areanum, 0); reachnum;
00766 reachnum = AAS_NextAreaReachability(areanum, reachnum))
00767 {
00768 #ifdef AVOIDREACH
00769
00770 for (i = 0; i < MAX_AVOIDREACH; i++)
00771 {
00772 if (avoidreach[i] == reachnum && avoidreachtimes[i] >= AAS_Time()) break;
00773 }
00774 if (i != MAX_AVOIDREACH && avoidreachtries[i] > AVOIDREACH_TRIES)
00775 {
00776 #ifdef DEBUG
00777 if (bot_developer)
00778 {
00779 botimport.Print(PRT_MESSAGE, "avoiding reachability %d\n", avoidreach[i]);
00780 }
00781 #endif //DEBUG
00782 continue;
00783 }
00784 #endif //AVOIDREACH
00785
00786 AAS_ReachabilityFromNum(reachnum, &reach);
00787
00788
00789 if (lastgoalareanum == goal->areanum && reach.areanum == lastareanum) continue;
00790
00791
00792 if (!BotValidTravel(origin, &reach, movetravelflags)) continue;
00793
00794 t = AAS_AreaTravelTimeToGoalArea(reach.areanum, reach.end, goal->areanum, travelflags);
00795
00796 if (!t) continue;
00797
00798 if (BotAvoidSpots(origin, &reach, avoidspots, numavoidspots)) {
00799 if (flags) {
00800 *flags |= MOVERESULT_BLOCKEDBYAVOIDSPOT;
00801 }
00802 continue;
00803 }
00804
00805 t += reach.traveltime;
00806
00807 if (!besttime || t < besttime)
00808 {
00809 besttime = t;
00810 bestreachnum = reachnum;
00811 }
00812 }
00813
00814 return bestreachnum;
00815 }
00816
00817
00818
00819
00820
00821
00822 int BotAddToTarget(vec3_t start, vec3_t end, float maxdist, float *dist, vec3_t target)
00823 {
00824 vec3_t dir;
00825 float curdist;
00826
00827 VectorSubtract(end, start, dir);
00828 curdist = VectorNormalize(dir);
00829 if (*dist + curdist < maxdist)
00830 {
00831 VectorCopy(end, target);
00832 *dist += curdist;
00833 return qfalse;
00834 }
00835 else
00836 {
00837 VectorMA(start, maxdist - *dist, dir, target);
00838 *dist = maxdist;
00839 return qtrue;
00840 }
00841 }
00842
00843 int BotMovementViewTarget(int movestate, bot_goal_t *goal, int travelflags, float lookahead, vec3_t target)
00844 {
00845 aas_reachability_t reach;
00846 int reachnum, lastareanum;
00847 bot_movestate_t *ms;
00848 vec3_t end;
00849 float dist;
00850
00851 ms = BotMoveStateFromHandle(movestate);
00852 if (!ms) return qfalse;
00853 reachnum = 0;
00854
00855 if (!ms->lastreachnum || !goal) return qfalse;
00856
00857 reachnum = ms->lastreachnum;
00858 VectorCopy(ms->origin, end);
00859 lastareanum = ms->lastareanum;
00860 dist = 0;
00861 while(reachnum && dist < lookahead)
00862 {
00863 AAS_ReachabilityFromNum(reachnum, &reach);
00864 if (BotAddToTarget(end, reach.start, lookahead, &dist, target)) return qtrue;
00865
00866 if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_TELEPORT) return qtrue;
00867
00868 if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ROCKETJUMP) return qtrue;
00869 if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_BFGJUMP) return qtrue;
00870
00871 if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_JUMPPAD &&
00872 (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR &&
00873 (reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB)
00874 {
00875 if (BotAddToTarget(reach.start, reach.end, lookahead, &dist, target)) return qtrue;
00876 }
00877 reachnum = BotGetReachabilityToGoal(reach.end, reach.areanum,
00878 ms->lastgoalareanum, lastareanum,
00879 ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
00880 goal, travelflags, travelflags, NULL, 0, NULL);
00881 VectorCopy(reach.end, end);
00882 lastareanum = reach.areanum;
00883 if (lastareanum == goal->areanum)
00884 {
00885 BotAddToTarget(reach.end, goal->origin, lookahead, &dist, target);
00886 return qtrue;
00887 }
00888 }
00889
00890 return qfalse;
00891 }
00892
00893
00894
00895
00896
00897
00898 int BotVisible(int ent, vec3_t eye, vec3_t target)
00899 {
00900 bsp_trace_t trace;
00901
00902 trace = AAS_Trace(eye, NULL, NULL, target, ent, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
00903 if (trace.fraction >= 1) return qtrue;
00904 return qfalse;
00905 }
00906
00907
00908
00909
00910
00911
00912 int BotPredictVisiblePosition(vec3_t origin, int areanum, bot_goal_t *goal, int travelflags, vec3_t target)
00913 {
00914 aas_reachability_t reach;
00915 int reachnum, lastgoalareanum, lastareanum, i;
00916 int avoidreach[MAX_AVOIDREACH];
00917 float avoidreachtimes[MAX_AVOIDREACH];
00918 int avoidreachtries[MAX_AVOIDREACH];
00919 vec3_t end;
00920
00921
00922 if (!goal) return qfalse;
00923
00924 if (!areanum) return qfalse;
00925
00926 if (!goal->areanum) return qfalse;
00927
00928 Com_Memset(avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
00929 lastgoalareanum = goal->areanum;
00930 lastareanum = areanum;
00931 VectorCopy(origin, end);
00932
00933 for (i = 0; i < 20 && (areanum != goal->areanum); i++)
00934 {
00935
00936 reachnum = BotGetReachabilityToGoal(end, areanum,
00937 lastgoalareanum, lastareanum,
00938 avoidreach, avoidreachtimes, avoidreachtries,
00939 goal, travelflags, travelflags, NULL, 0, NULL);
00940 if (!reachnum) return qfalse;
00941 AAS_ReachabilityFromNum(reachnum, &reach);
00942
00943 if (BotVisible(goal->entitynum, goal->origin, reach.start))
00944 {
00945 VectorCopy(reach.start, target);
00946 return qtrue;
00947 }
00948
00949 if (BotVisible(goal->entitynum, goal->origin, reach.end))
00950 {
00951 VectorCopy(reach.end, target);
00952 return qtrue;
00953 }
00954
00955 if (reach.areanum == goal->areanum)
00956 {
00957 VectorCopy(reach.end, target);
00958 return qtrue;
00959 }
00960
00961 lastareanum = areanum;
00962 areanum = reach.areanum;
00963 VectorCopy(reach.end, end);
00964
00965 }
00966
00967 return qfalse;
00968 }
00969
00970
00971
00972
00973
00974
00975 void MoverBottomCenter(aas_reachability_t *reach, vec3_t bottomcenter)
00976 {
00977 int modelnum;
00978 vec3_t mins, maxs, origin, mids;
00979 vec3_t angles = {0, 0, 0};
00980
00981 modelnum = reach->facenum & 0x0000FFFF;
00982
00983 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, origin);
00984
00985 if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
00986 {
00987 botimport.Print(PRT_MESSAGE, "no entity with model %d\n", modelnum);
00988 }
00989
00990 VectorAdd(mins, maxs, mids);
00991 VectorMA(origin, 0.5, mids, bottomcenter);
00992 bottomcenter[2] = reach->start[2];
00993 }
00994
00995
00996
00997
00998
00999
01000 float BotGapDistance(vec3_t origin, vec3_t hordir, int entnum)
01001 {
01002 float dist, startz;
01003 vec3_t start, end;
01004 aas_trace_t trace;
01005
01006
01007 startz = origin[2];
01008
01009 {
01010 VectorCopy(origin, start);
01011 VectorCopy(origin, end);
01012 end[2] -= 60;
01013 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
01014 if (trace.fraction >= 1) return 1;
01015 startz = trace.endpos[2] + 1;
01016 }
01017
01018 for (dist = 8; dist <= 100; dist += 8)
01019 {
01020 VectorMA(origin, dist, hordir, start);
01021 start[2] = startz + 24;
01022 VectorCopy(start, end);
01023 end[2] -= 48 + sv_maxbarrier->value;
01024 trace = AAS_TraceClientBBox(start, end, PRESENCE_CROUCH, entnum);
01025
01026 if (!trace.startsolid)
01027 {
01028
01029 if (trace.endpos[2] < startz - sv_maxstep->value - 8)
01030 {
01031 VectorCopy(trace.endpos, end);
01032 end[2] -= 20;
01033 if (AAS_PointContents(end) & CONTENTS_WATER) break;
01034
01035
01036 return dist;
01037 }
01038 startz = trace.endpos[2];
01039 }
01040 }
01041 return 0;
01042 }
01043
01044
01045
01046
01047
01048
01049 int BotCheckBarrierJump(bot_movestate_t *ms, vec3_t dir, float speed)
01050 {
01051 vec3_t start, hordir, end;
01052 aas_trace_t trace;
01053
01054 VectorCopy(ms->origin, end);
01055 end[2] += sv_maxbarrier->value;
01056
01057 trace = AAS_TraceClientBBox(ms->origin, end, PRESENCE_NORMAL, ms->entitynum);
01058
01059 if (trace.startsolid) return qfalse;
01060
01061 if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
01062
01063 hordir[0] = dir[0];
01064 hordir[1] = dir[1];
01065 hordir[2] = 0;
01066 VectorNormalize(hordir);
01067 VectorMA(ms->origin, ms->thinktime * speed * 0.5, hordir, end);
01068 VectorCopy(trace.endpos, start);
01069 end[2] = trace.endpos[2];
01070
01071 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
01072
01073 if (trace.startsolid) return qfalse;
01074
01075 VectorCopy(trace.endpos, start);
01076 VectorCopy(trace.endpos, end);
01077 end[2] = ms->origin[2];
01078
01079 trace = AAS_TraceClientBBox(start, end, PRESENCE_NORMAL, ms->entitynum);
01080
01081 if (trace.startsolid) return qfalse;
01082
01083 if (trace.fraction >= 1.0) return qfalse;
01084
01085 if (trace.endpos[2] - ms->origin[2] < sv_maxstep->value) return qfalse;
01086
01087 EA_Jump(ms->client);
01088 EA_Move(ms->client, hordir, speed);
01089 ms->moveflags |= MFL_BARRIERJUMP;
01090
01091 return qtrue;
01092 }
01093
01094
01095
01096
01097
01098
01099 int BotSwimInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
01100 {
01101 vec3_t normdir;
01102
01103 VectorCopy(dir, normdir);
01104 VectorNormalize(normdir);
01105 EA_Move(ms->client, normdir, speed);
01106 return qtrue;
01107 }
01108
01109
01110
01111
01112
01113
01114 int BotWalkInDirection(bot_movestate_t *ms, vec3_t dir, float speed, int type)
01115 {
01116 vec3_t hordir, cmdmove, velocity, tmpdir, origin;
01117 int presencetype, maxframes, cmdframes, stopevent;
01118 aas_clientmove_t move;
01119 float dist;
01120
01121 if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
01122
01123 if (ms->moveflags & MFL_ONGROUND)
01124 {
01125
01126 if (BotCheckBarrierJump(ms, dir, speed)) return qtrue;
01127
01128 ms->moveflags &= ~MFL_BARRIERJUMP;
01129
01130 if ((type & MOVE_CROUCH) && !(type & MOVE_JUMP)) presencetype = PRESENCE_CROUCH;
01131 else presencetype = PRESENCE_NORMAL;
01132
01133 hordir[0] = dir[0];
01134 hordir[1] = dir[1];
01135 hordir[2] = 0;
01136 VectorNormalize(hordir);
01137
01138 if (!(type & MOVE_JUMP))
01139 {
01140
01141 if (BotGapDistance(ms->origin, hordir, ms->entitynum) > 0) type |= MOVE_JUMP;
01142 }
01143
01144 VectorScale(hordir, speed, cmdmove);
01145 VectorCopy(ms->velocity, velocity);
01146
01147 if (type & MOVE_JUMP)
01148 {
01149
01150 cmdmove[2] = 400;
01151 maxframes = PREDICTIONTIME_JUMP / 0.1;
01152 cmdframes = 1;
01153 stopevent = SE_HITGROUND|SE_HITGROUNDDAMAGE|
01154 SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
01155 }
01156 else
01157 {
01158 maxframes = 2;
01159 cmdframes = 2;
01160 stopevent = SE_HITGROUNDDAMAGE|
01161 SE_ENTERWATER|SE_ENTERSLIME|SE_ENTERLAVA;
01162 }
01163
01164
01165 VectorCopy(ms->origin, origin);
01166 origin[2] += 0.5;
01167 AAS_PredictClientMovement(&move, ms->entitynum, origin, presencetype, qtrue,
01168 velocity, cmdmove, cmdframes, maxframes, 0.1f,
01169 stopevent, 0, qfalse);
01170
01171 if (move.frames >= maxframes && (type & MOVE_JUMP))
01172 {
01173
01174 return qfalse;
01175 }
01176
01177 if (move.stopevent & (SE_ENTERSLIME|SE_ENTERLAVA|SE_HITGROUNDDAMAGE))
01178 {
01179
01180
01181
01182
01183 return qfalse;
01184 }
01185
01186 if (move.stopevent & SE_HITGROUND)
01187 {
01188
01189 VectorNormalize2(move.velocity, tmpdir);
01190 dist = BotGapDistance(move.endpos, tmpdir, ms->entitynum);
01191 if (dist > 0) return qfalse;
01192
01193 dist = BotGapDistance(move.endpos, hordir, ms->entitynum);
01194 if (dist > 0) return qfalse;
01195 }
01196
01197 tmpdir[0] = move.endpos[0] - ms->origin[0];
01198 tmpdir[1] = move.endpos[1] - ms->origin[1];
01199 tmpdir[2] = 0;
01200
01201
01202
01203 if (VectorLength(tmpdir) < speed * ms->thinktime * 0.5) return qfalse;
01204
01205 if (type & MOVE_JUMP) EA_Jump(ms->client);
01206 if (type & MOVE_CROUCH) EA_Crouch(ms->client);
01207 EA_Move(ms->client, hordir, speed);
01208
01209 return qtrue;
01210 }
01211 else
01212 {
01213 if (ms->moveflags & MFL_BARRIERJUMP)
01214 {
01215
01216 if (ms->velocity[2] < 50)
01217 {
01218 EA_Move(ms->client, dir, speed);
01219 }
01220 }
01221
01222 return qtrue;
01223 }
01224 }
01225
01226
01227
01228
01229
01230
01231 int BotMoveInDirection(int movestate, vec3_t dir, float speed, int type)
01232 {
01233 bot_movestate_t *ms;
01234
01235 ms = BotMoveStateFromHandle(movestate);
01236 if (!ms) return qfalse;
01237
01238 if (AAS_Swimming(ms->origin))
01239 {
01240 return BotSwimInDirection(ms, dir, speed, ty