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, type);
01241 }
01242 else
01243 {
01244 return BotWalkInDirection(ms, dir, speed, type);
01245 }
01246 }
01247
01248
01249
01250
01251
01252
01253 int Intersection(vec2_t p1, vec2_t p2, vec2_t p3, vec2_t p4, vec2_t out)
01254 {
01255 float x1, dx1, dy1, x2, dx2, dy2, d;
01256
01257 dx1 = p2[0] - p1[0];
01258 dy1 = p2[1] - p1[1];
01259 dx2 = p4[0] - p3[0];
01260 dy2 = p4[1] - p3[1];
01261
01262 d = dy1 * dx2 - dx1 * dy2;
01263 if (d != 0)
01264 {
01265 x1 = p1[1] * dx1 - p1[0] * dy1;
01266 x2 = p3[1] * dx2 - p3[0] * dy2;
01267 out[0] = (int) ((dx1 * x2 - dx2 * x1) / d);
01268 out[1] = (int) ((dy1 * x2 - dy2 * x1) / d);
01269 return qtrue;
01270 }
01271 else
01272 {
01273 return qfalse;
01274 }
01275 }
01276
01277
01278
01279
01280
01281
01282 void BotCheckBlocked(bot_movestate_t *ms, vec3_t dir, int checkbottom, bot_moveresult_t *result)
01283 {
01284 vec3_t mins, maxs, end, up = {0, 0, 1};
01285 bsp_trace_t trace;
01286
01287
01288 AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
01289
01290 if (fabs(DotProduct(dir, up)) < 0.7)
01291 {
01292 mins[2] += sv_maxstep->value;
01293 maxs[2] -= 10;
01294 }
01295 VectorMA(ms->origin, 3, dir, end);
01296 trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP|CONTENTS_BODY);
01297
01298 if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
01299 {
01300 result->blocked = qtrue;
01301 result->blockentity = trace.ent;
01302 #ifdef DEBUG
01303
01304 #endif //DEBUG
01305 }
01306
01307 else if (checkbottom && !AAS_AreaReachability(ms->areanum))
01308 {
01309
01310 AAS_PresenceTypeBoundingBox(ms->presencetype, mins, maxs);
01311 VectorMA(ms->origin, -3, up, end);
01312 trace = AAS_Trace(ms->origin, mins, maxs, end, ms->entitynum, CONTENTS_SOLID|CONTENTS_PLAYERCLIP);
01313 if (!trace.startsolid && (trace.ent != ENTITYNUM_WORLD && trace.ent != ENTITYNUM_NONE) )
01314 {
01315 result->blocked = qtrue;
01316 result->blockentity = trace.ent;
01317 result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
01318 #ifdef DEBUG
01319
01320 #endif //DEBUG
01321 }
01322 }
01323 }
01324
01325
01326
01327
01328
01329
01330 void BotClearMoveResult(bot_moveresult_t *moveresult)
01331 {
01332 moveresult->failure = qfalse;
01333 moveresult->type = 0;
01334 moveresult->blocked = qfalse;
01335 moveresult->blockentity = 0;
01336 moveresult->traveltype = 0;
01337 moveresult->flags = 0;
01338 }
01339
01340
01341
01342
01343
01344
01345 bot_moveresult_t BotTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)
01346 {
01347 float dist, speed;
01348 vec3_t hordir;
01349 bot_moveresult_t result;
01350
01351 BotClearMoveResult(&result);
01352
01353 hordir[0] = reach->start[0] - ms->origin[0];
01354 hordir[1] = reach->start[1] - ms->origin[1];
01355 hordir[2] = 0;
01356 dist = VectorNormalize(hordir);
01357
01358 BotCheckBlocked(ms, hordir, qtrue, &result);
01359
01360 if (dist < 10)
01361 {
01362
01363 hordir[0] = reach->end[0] - ms->origin[0];
01364 hordir[1] = reach->end[1] - ms->origin[1];
01365 hordir[2] = 0;
01366 dist = VectorNormalize(hordir);
01367 }
01368
01369 if (!(AAS_AreaPresenceType(reach->areanum) & PRESENCE_NORMAL))
01370 {
01371
01372 if (dist < 20) EA_Crouch(ms->client);
01373 }
01374
01375 dist = BotGapDistance(ms->origin, hordir, ms->entitynum);
01376
01377 if (ms->moveflags & MFL_WALK)
01378 {
01379 if (dist > 0) speed = 200 - (180 - 1 * dist);
01380 else speed = 200;
01381 EA_Walk(ms->client);
01382 }
01383 else
01384 {
01385 if (dist > 0) speed = 400 - (360 - 2 * dist);
01386 else speed = 400;
01387 }
01388
01389 EA_Move(ms->client, hordir, speed);
01390 VectorCopy(hordir, result.movedir);
01391
01392 return result;
01393 }
01394
01395
01396
01397
01398
01399
01400 bot_moveresult_t BotFinishTravel_Walk(bot_movestate_t *ms, aas_reachability_t *reach)
01401 {
01402 vec3_t hordir;
01403 float dist, speed;
01404 bot_moveresult_t result;
01405
01406 BotClearMoveResult(&result);
01407
01408
01409
01410
01411
01412
01413
01414
01415
01416
01417
01418
01419 hordir[0] = reach->end[0] - ms->origin[0];
01420 hordir[1] = reach->end[1] - ms->origin[1];
01421 hordir[2] = 0;
01422 dist = VectorNormalize(hordir);
01423
01424 if (dist > 100) dist = 100;
01425 speed = 400 - (400 - 3 * dist);
01426
01427 EA_Move(ms->client, hordir, speed);
01428 VectorCopy(hordir, result.movedir);
01429
01430 return result;
01431 }
01432
01433
01434
01435
01436
01437
01438 bot_moveresult_t BotTravel_Crouch(bot_movestate_t *ms, aas_reachability_t *reach)
01439 {
01440 float speed;
01441 vec3_t hordir;
01442 bot_moveresult_t result;
01443
01444 BotClearMoveResult(&result);
01445
01446 speed = 400;
01447
01448 hordir[0] = reach->end[0] - ms->origin[0];
01449 hordir[1] = reach->end[1] - ms->origin[1];
01450 hordir[2] = 0;
01451 VectorNormalize(hordir);
01452
01453 BotCheckBlocked(ms, hordir, qtrue, &result);
01454
01455 EA_Crouch(ms->client);
01456 EA_Move(ms->client, hordir, speed);
01457
01458 VectorCopy(hordir, result.movedir);
01459
01460 return result;
01461 }
01462
01463
01464
01465
01466
01467
01468 bot_moveresult_t BotTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)
01469 {
01470 float dist, speed;
01471 vec3_t hordir;
01472 bot_moveresult_t result;
01473
01474 BotClearMoveResult(&result);
01475
01476 hordir[0] = reach->start[0] - ms->origin[0];
01477 hordir[1] = reach->start[1] - ms->origin[1];
01478 hordir[2] = 0;
01479 dist = VectorNormalize(hordir);
01480
01481 BotCheckBlocked(ms, hordir, qtrue, &result);
01482
01483 if (dist < 9)
01484 {
01485 EA_Jump(ms->client);
01486 }
01487 else
01488 {
01489 if (dist > 60) dist = 60;
01490 speed = 360 - (360 - 6 * dist);
01491 EA_Move(ms->client, hordir, speed);
01492 }
01493 VectorCopy(hordir, result.movedir);
01494
01495 return result;
01496 }
01497
01498
01499
01500
01501
01502
01503 bot_moveresult_t BotFinishTravel_BarrierJump(bot_movestate_t *ms, aas_reachability_t *reach)
01504 {
01505 float dist;
01506 vec3_t hordir;
01507 bot_moveresult_t result;
01508
01509 BotClearMoveResult(&result);
01510
01511 if (ms->velocity[2] < 250)
01512 {
01513 hordir[0] = reach->end[0] - ms->origin[0];
01514 hordir[1] = reach->end[1] - ms->origin[1];
01515 hordir[2] = 0;
01516 dist = VectorNormalize(hordir);
01517
01518 BotCheckBlocked(ms, hordir, qtrue, &result);
01519
01520 EA_Move(ms->client, hordir, 400);
01521 VectorCopy(hordir, result.movedir);
01522 }
01523
01524 return result;
01525 }
01526
01527
01528
01529
01530
01531
01532 bot_moveresult_t BotTravel_Swim(bot_movestate_t *ms, aas_reachability_t *reach)
01533 {
01534 vec3_t dir;
01535 bot_moveresult_t result;
01536
01537 BotClearMoveResult(&result);
01538
01539 VectorSubtract(reach->start, ms->origin, dir);
01540 VectorNormalize(dir);
01541
01542 BotCheckBlocked(ms, dir, qtrue, &result);
01543
01544 EA_Move(ms->client, dir, 400);
01545
01546 VectorCopy(dir, result.movedir);
01547 Vector2Angles(dir, result.ideal_viewangles);
01548 result.flags |= MOVERESULT_SWIMVIEW;
01549
01550 return result;
01551 }
01552
01553
01554
01555
01556
01557
01558 bot_moveresult_t BotTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)
01559 {
01560 vec3_t dir, hordir;
01561 float dist;
01562 bot_moveresult_t result;
01563
01564 BotClearMoveResult(&result);
01565
01566 VectorSubtract(reach->end, ms->origin, dir);
01567 VectorCopy(dir, hordir);
01568 hordir[2] = 0;
01569 dir[2] += 15 + crandom() * 40;
01570
01571 VectorNormalize(dir);
01572 dist = VectorNormalize(hordir);
01573
01574
01575 EA_MoveForward(ms->client);
01576
01577 if (dist < 40) EA_MoveUp(ms->client);
01578
01579 Vector2Angles(dir, result.ideal_viewangles);
01580 result.flags |= MOVERESULT_MOVEMENTVIEW;
01581
01582 VectorCopy(dir, result.movedir);
01583
01584 return result;
01585 }
01586
01587
01588
01589
01590
01591
01592 bot_moveresult_t BotFinishTravel_WaterJump(bot_movestate_t *ms, aas_reachability_t *reach)
01593 {
01594 vec3_t dir, pnt;
01595 float dist;
01596 bot_moveresult_t result;
01597
01598
01599 BotClearMoveResult(&result);
01600
01601 if (ms->moveflags & MFL_WATERJUMP) return result;
01602
01603
01604 VectorCopy(ms->origin, pnt);
01605 pnt[2] -= 32;
01606 if (!(AAS_PointContents(pnt) & (CONTENTS_LAVA|CONTENTS_SLIME|CONTENTS_WATER))) return result;
01607
01608 VectorSubtract(reach->end, ms->origin, dir);
01609 dir[0] += crandom() * 10;
01610 dir[1] += crandom() * 10;
01611 dir[2] += 70 + crandom() * 10;
01612 dist = VectorNormalize(dir);
01613
01614 EA_Move(ms->client, dir, 400);
01615
01616 Vector2Angles(dir, result.ideal_viewangles);
01617 result.flags |= MOVERESULT_MOVEMENTVIEW;
01618
01619 VectorCopy(dir, result.movedir);
01620
01621 return result;
01622 }
01623
01624
01625
01626
01627
01628
01629 bot_moveresult_t BotTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)
01630 {
01631 vec3_t hordir, dir;
01632 float dist, speed, reachhordist;
01633 bot_moveresult_t result;
01634
01635 BotClearMoveResult(&result);
01636
01637 VectorSubtract(reach->start, ms->origin, dir);
01638 VectorNormalize(dir);
01639 BotCheckBlocked(ms, dir, qtrue, &result);
01640
01641 VectorSubtract(reach->end, reach->start, dir);
01642 dir[2] = 0;
01643 reachhordist = VectorLength(dir);
01644
01645 hordir[0] = reach->start[0] - ms->origin[0];
01646 hordir[1] = reach->start[1] - ms->origin[1];
01647 hordir[2] = 0;
01648 dist = VectorNormalize(hordir);
01649
01650 if (dist < 48)
01651 {
01652 hordir[0] = reach->end[0] - ms->origin[0];
01653 hordir[1] = reach->end[1] - ms->origin[1];
01654 hordir[2] = 0;
01655 VectorNormalize(hordir);
01656
01657 if (reachhordist < 20)
01658 {
01659 speed = 100;
01660 }
01661 else if (!AAS_HorizontalVelocityForJump(0, reach->start, reach->end, &speed))
01662 {
01663 speed = 400;
01664 }
01665 }
01666 else
01667 {
01668 if (reachhordist < 20)
01669 {
01670 if (dist > 64) dist = 64;
01671 speed = 400 - (256 - 4 * dist);
01672 }
01673 else
01674 {
01675 speed = 400;
01676 }
01677 }
01678
01679 BotCheckBlocked(ms, hordir, qtrue, &result);
01680
01681 EA_Move(ms->client, hordir, speed);
01682 VectorCopy(hordir, result.movedir);
01683
01684 return result;
01685 }
01686
01687
01688
01689
01690
01691
01692 int BotAirControl(vec3_t origin, vec3_t velocity, vec3_t goal, vec3_t dir, float *speed)
01693 {
01694 vec3_t org, vel;
01695 float dist;
01696 int i;
01697
01698 VectorCopy(origin, org);
01699 VectorScale(velocity, 0.1, vel);
01700 for (i = 0; i < 50; i++)
01701 {
01702 vel[2] -= sv_gravity->value * 0.01;
01703
01704 if (vel[2] < 0 && org[2] + vel[2] < goal[2])
01705 {
01706 VectorScale(vel, (goal[2] - org[2]) / vel[2], vel);
01707 VectorAdd(org, vel, org);
01708 VectorSubtract(goal, org, dir);
01709 dist = VectorNormalize(dir);
01710 if (dist > 32) dist = 32;
01711 *speed = 400 - (400 - 13 * dist);
01712 return qtrue;
01713 }
01714 else
01715 {
01716 VectorAdd(org, vel, org);
01717 }
01718 }
01719 VectorSet(dir, 0, 0, 0);
01720 *speed = 400;
01721 return qfalse;
01722 }
01723
01724
01725
01726
01727
01728
01729 bot_moveresult_t BotFinishTravel_WalkOffLedge(bot_movestate_t *ms, aas_reachability_t *reach)
01730 {
01731 vec3_t dir, hordir, end, v;
01732 float dist, speed;
01733 bot_moveresult_t result;
01734
01735 BotClearMoveResult(&result);
01736
01737 VectorSubtract(reach->end, ms->origin, dir);
01738 BotCheckBlocked(ms, dir, qtrue, &result);
01739
01740 VectorSubtract(reach->end, ms->origin, v);
01741 v[2] = 0;
01742 dist = VectorNormalize(v);
01743 if (dist > 16) VectorMA(reach->end, 16, v, end);
01744 else VectorCopy(reach->end, end);
01745
01746 if (!BotAirControl(ms->origin, ms->velocity, end, hordir, &speed))
01747 {
01748
01749 VectorCopy(dir, hordir);
01750 hordir[2] = 0;
01751
01752 dist = VectorNormalize(hordir);
01753 speed = 400;
01754 }
01755
01756 EA_Move(ms->client, hordir, speed);
01757 VectorCopy(hordir, result.movedir);
01758
01759 return result;
01760 }
01761
01762
01763
01764
01765
01766
01767
01768
01769
01770
01771
01772
01773
01774
01775
01776
01777
01778
01779
01780
01781
01782
01783
01784
01785
01786
01787
01788
01789
01790
01791
01792
01793
01794
01795
01796
01797
01798
01799
01800
01801
01802
01803
01804
01805
01806
01807
01808
01809
01810
01811
01812
01813
01814
01815
01816
01817
01818
01819
01820
01821
01822
01823
01824
01825
01826
01827
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837
01838
01839
01840
01841
01842
01843
01844
01845
01846
01847
01848
01849
01850
01851
01852
01853
01854
01855
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868
01869
01870
01871
01872
01873
01874
01875
01876
01877
01878
01879
01880
01881
01882
01883
01884
01885
01886
01887
01888
01889 bot_moveresult_t BotTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
01890 {
01891 vec3_t hordir, dir1, dir2, start, end, runstart;
01892
01893 float dist1, dist2, speed;
01894 bot_moveresult_t result;
01895
01896 BotClearMoveResult(&result);
01897
01898 AAS_JumpReachRunStart(reach, runstart);
01899
01900 hordir[0] = runstart[0] - reach->start[0];
01901 hordir[1] = runstart[1] - reach->start[1];
01902 hordir[2] = 0;
01903 VectorNormalize(hordir);
01904
01905 VectorCopy(reach->start, start);
01906 start[2] += 1;
01907 VectorMA(reach->start, 80, hordir, runstart);
01908
01909 for (dist1 = 0; dist1 < 80; dist1 += 10)
01910 {
01911 VectorMA(start, dist1+10, hordir, end);
01912 end[2] += 1;
01913 if (AAS_PointAreaNum(end) != ms->reachareanum) break;
01914 }
01915 if (dist1 < 80) VectorMA(reach->start, dist1, hordir, runstart);
01916
01917 VectorSubtract(ms->origin, reach->start, dir1);
01918 dir1[2] = 0;
01919 dist1 = VectorNormalize(dir1);
01920 VectorSubtract(ms->origin, runstart, dir2);
01921 dir2[2] = 0;
01922 dist2 = VectorNormalize(dir2);
01923
01924 if (DotProduct(dir1, dir2) < -0.8 || dist2 < 5)
01925 {
01926
01927 hordir[0] = reach->end[0] - ms->origin[0];
01928 hordir[1] = reach->end[1] - ms->origin[1];
01929 hordir[2] = 0;
01930 VectorNormalize(hordir);
01931
01932 if (dist1 < 24) EA_Jump(ms->client);
01933 else if (dist1 < 32) EA_DelayedJump(ms->client);
01934 EA_Move(ms->client, hordir, 600);
01935
01936 ms->jumpreach = ms->lastreachnum;
01937 }
01938 else
01939 {
01940
01941 hordir[0] = runstart[0] - ms->origin[0];
01942 hordir[1] = runstart[1] - ms->origin[1];
01943 hordir[2] = 0;
01944 VectorNormalize(hordir);
01945
01946 if (dist2 > 80) dist2 = 80;
01947 speed = 400 - (400 - 5 * dist2);
01948 EA_Move(ms->client, hordir, speed);
01949 }
01950 VectorCopy(hordir, result.movedir);
01951
01952 return result;
01953 }
01954
01955
01956
01957
01958
01959
01960 bot_moveresult_t BotFinishTravel_Jump(bot_movestate_t *ms, aas_reachability_t *reach)
01961 {
01962 vec3_t hordir, hordir2;
01963 float speed, dist;
01964 bot_moveresult_t result;
01965
01966 BotClearMoveResult(&result);
01967
01968 if (!ms->jumpreach) return result;
01969
01970 hordir[0] = reach->end[0] - ms->origin[0];
01971 hordir[1] = reach->end[1] - ms->origin[1];
01972 hordir[2] = 0;
01973 dist = VectorNormalize(hordir);
01974
01975 hordir2[0] = reach->end[0] - reach->start[0];
01976 hordir2[1] = reach->end[1] - reach->start[1];
01977 hordir2[2] = 0;
01978 VectorNormalize(hordir2);
01979
01980 if (DotProduct(hordir, hordir2) < -0.5 && dist < 24) return result;
01981
01982 speed = 800;
01983
01984 EA_Move(ms->client, hordir, speed);
01985 VectorCopy(hordir, result.movedir);
01986
01987 return result;
01988 }
01989
01990
01991
01992
01993
01994
01995 bot_moveresult_t BotTravel_Ladder(bot_movestate_t *ms, aas_reachability_t *reach)
01996 {
01997
01998 vec3_t dir, viewdir;
01999 vec3_t origin = {0, 0, 0};
02000
02001 bot_moveresult_t result;
02002
02003 BotClearMoveResult(&result);
02004
02005
02006
02007
02008 {
02009
02010 VectorSubtract(reach->end, ms->origin, dir);
02011 VectorNormalize(dir);
02012
02013 viewdir[0] = dir[0];
02014 viewdir[1] = dir[1];
02015 viewdir[2] = 3 * dir[2];
02016 Vector2Angles(viewdir, result.ideal_viewangles);
02017
02018 EA_Move(ms->client, origin, 0);
02019 EA_MoveForward(ms->client);
02020
02021 result.flags |= MOVERESULT_MOVEMENTVIEW;
02022 }
02023
02024
02025
02026
02027
02028
02029
02030
02031
02032
02033
02034
02035
02036
02037
02038
02039
02040
02041 VectorCopy(dir, result.movedir);
02042
02043 return result;
02044 }
02045
02046
02047
02048
02049
02050
02051 bot_moveresult_t BotTravel_Teleport(bot_movestate_t *ms, aas_reachability_t *reach)
02052 {
02053 vec3_t hordir;
02054 float dist;
02055 bot_moveresult_t result;
02056
02057 BotClearMoveResult(&result);
02058
02059 if (ms->moveflags & MFL_TELEPORTED) return result;
02060
02061
02062 VectorSubtract(reach->start, ms->origin, hordir);
02063 if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
02064 dist = VectorNormalize(hordir);
02065
02066 BotCheckBlocked(ms, hordir, qtrue, &result);
02067
02068 if (dist < 30) EA_Move(ms->client, hordir, 200);
02069 else EA_Move(ms->client, hordir, 400);
02070
02071 if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
02072
02073 VectorCopy(hordir, result.movedir);
02074 return result;
02075 }
02076
02077
02078
02079
02080
02081
02082 bot_moveresult_t BotTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)
02083 {
02084 vec3_t dir, dir1, dir2, hordir, bottomcenter;
02085 float dist, dist1, dist2, speed;
02086 bot_moveresult_t result;
02087
02088 BotClearMoveResult(&result);
02089
02090 if (BotOnMover(ms->origin, ms->entitynum, reach))
02091 {
02092 #ifdef DEBUG_ELEVATOR
02093 botimport.Print(PRT_MESSAGE, "bot on elevator\n");
02094 #endif //DEBUG_ELEVATOR
02095
02096 if (abs(ms->origin[2] - reach->end[2]) < sv_maxbarrier->value)
02097 {
02098 #ifdef DEBUG_ELEVATOR
02099 botimport.Print(PRT_MESSAGE, "bot moving to end\n");
02100 #endif //DEBUG_ELEVATOR
02101
02102 VectorSubtract(reach->end, ms->origin, hordir);
02103 hordir[2] = 0;
02104 VectorNormalize(hordir);
02105 if (!BotCheckBarrierJump(ms, hordir, 100))
02106 {
02107 EA_Move(ms->client, hordir, 400);
02108 }
02109 VectorCopy(hordir, result.movedir);
02110 }
02111
02112 else
02113 {
02114 MoverBottomCenter(reach, bottomcenter);
02115 VectorSubtract(bottomcenter, ms->origin, hordir);
02116 hordir[2] = 0;
02117 dist = VectorNormalize(hordir);
02118
02119 if (dist > 10)
02120 {
02121 #ifdef DEBUG_ELEVATOR
02122 botimport.Print(PRT_MESSAGE, "bot moving to center\n");
02123 #endif //DEBUG_ELEVATOR
02124
02125 if (dist > 100) dist = 100;
02126 speed = 400 - (400 - 4 * dist);
02127
02128 EA_Move(ms->client, hordir, speed);
02129 VectorCopy(hordir, result.movedir);
02130 }
02131 }
02132 }
02133 else
02134 {
02135 #ifdef DEBUG_ELEVATOR
02136 botimport.Print(PRT_MESSAGE, "bot not on elevator\n");
02137 #endif //DEBUG_ELEVATOR
02138
02139 VectorSubtract(reach->end, ms->origin, dir);
02140 dist = VectorLength(dir);
02141 if (dist < 64)
02142 {
02143 if (dist > 60) dist = 60;
02144 speed = 360 - (360 - 6 * dist);
02145
02146 if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))
02147 {
02148 if (speed > 5) EA_Move(ms->client, dir, speed);
02149 }
02150 VectorCopy(dir, result.movedir);
02151
02152 if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
02153
02154 ms->reachability_time = 0;
02155 return result;
02156 }
02157
02158 VectorSubtract(reach->start, ms->origin, dir1);
02159 if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;
02160 dist1 = VectorNormalize(dir1);
02161
02162 if (!MoverDown(reach))
02163 {
02164 #ifdef DEBUG_ELEVATOR
02165 botimport.Print(PRT_MESSAGE, "elevator not down\n");
02166 #endif //DEBUG_ELEVATOR
02167 dist = dist1;
02168 VectorCopy(dir1, dir);
02169
02170 BotCheckBlocked(ms, dir, qfalse, &result);
02171
02172 if (dist > 60) dist = 60;
02173 speed = 360 - (360 - 6 * dist);
02174
02175 if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
02176 {
02177 if (speed > 5) EA_Move(ms->client, dir, speed);
02178 }
02179 VectorCopy(dir, result.movedir);
02180
02181 if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
02182
02183 result.type = RESULTTYPE_ELEVATORUP;
02184 result.flags |= MOVERESULT_WAITING;
02185 return result;
02186 }
02187
02188 MoverBottomCenter(reach, bottomcenter);
02189 VectorSubtract(bottomcenter, ms->origin, dir2);
02190 if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;
02191 dist2 = VectorNormalize(dir2);
02192
02193
02194
02195 if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)
02196 {
02197 #ifdef DEBUG_ELEVATOR
02198 botimport.Print(PRT_MESSAGE, "bot moving to center\n");
02199 #endif //DEBUG_ELEVATOR
02200 dist = dist2;
02201 VectorCopy(dir2, dir);
02202 }
02203 else
02204 {
02205 #ifdef DEBUG_ELEVATOR
02206 botimport.Print(PRT_MESSAGE, "bot moving to start\n");
02207 #endif //DEBUG_ELEVATOR
02208 dist = dist1;
02209 VectorCopy(dir1, dir);
02210 }
02211
02212 BotCheckBlocked(ms, dir, qfalse, &result);
02213
02214 if (dist > 60) dist = 60;
02215 speed = 400 - (400 - 6 * dist);
02216
02217 if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
02218 {
02219 EA_Move(ms->client, dir, speed);
02220 }
02221 VectorCopy(dir, result.movedir);
02222
02223 if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
02224 }
02225 return result;
02226 }
02227
02228
02229
02230
02231
02232
02233 bot_moveresult_t BotFinishTravel_Elevator(bot_movestate_t *ms, aas_reachability_t *reach)
02234 {
02235 vec3_t bottomcenter, bottomdir, topdir;
02236 bot_moveresult_t result;
02237
02238 BotClearMoveResult(&result);
02239
02240 MoverBottomCenter(reach, bottomcenter);
02241 VectorSubtract(bottomcenter, ms->origin, bottomdir);
02242
02243 VectorSubtract(reach->end, ms->origin, topdir);
02244
02245 if (fabs(bottomdir[2]) < fabs(topdir[2]))
02246 {
02247 VectorNormalize(bottomdir);
02248 EA_Move(ms->client, bottomdir, 300);
02249 }
02250 else
02251 {
02252 VectorNormalize(topdir);
02253 EA_Move(ms->client, topdir, 300);
02254 }
02255 return result;
02256 }
02257
02258
02259
02260
02261
02262
02263 void BotFuncBobStartEnd(aas_reachability_t *reach, vec3_t start, vec3_t end, vec3_t origin)
02264 {
02265 int spawnflags, modelnum;
02266 vec3_t mins, maxs, mid, angles = {0, 0, 0};
02267 int num0, num1;
02268
02269 modelnum = reach->facenum & 0x0000FFFF;
02270 if (!AAS_OriginOfMoverWithModelNum(modelnum, origin))
02271 {
02272 botimport.Print(PRT_MESSAGE, "BotFuncBobStartEnd: no entity with model %d\n", modelnum);
02273 VectorSet(start, 0, 0, 0);
02274 VectorSet(end, 0, 0, 0);
02275 return;
02276 }
02277 AAS_BSPModelMinsMaxsOrigin(modelnum, angles, mins, maxs, NULL);
02278 VectorAdd(mins, maxs, mid);
02279 VectorScale(mid, 0.5, mid);
02280 VectorCopy(mid, start);
02281 VectorCopy(mid, end);
02282 spawnflags = reach->facenum >> 16;
02283 num0 = reach->edgenum >> 16;
02284 if (num0 > 0x00007FFF) num0 |= 0xFFFF0000;
02285 num1 = reach->edgenum & 0x0000FFFF;
02286 if (num1 > 0x00007FFF) num1 |= 0xFFFF0000;
02287 if (spawnflags & 1)
02288 {
02289 start[0] = num0;
02290 end[0] = num1;
02291
02292 origin[0] += mid[0];
02293 origin[1] = mid[1];
02294 origin[2] = mid[2];
02295 }
02296 else if (spawnflags & 2)
02297 {
02298 start[1] = num0;
02299 end[1] = num1;
02300
02301 origin[0] = mid[0];
02302 origin[1] += mid[1];
02303 origin[2] = mid[2];
02304 }
02305 else
02306 {
02307 start[2] = num0;
02308 end[2] = num1;
02309
02310 origin[0] = mid[0];
02311 origin[1] = mid[1];
02312 origin[2] += mid[2];
02313 }
02314 }
02315
02316
02317
02318
02319
02320
02321 bot_moveresult_t BotTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)
02322 {
02323 vec3_t dir, dir1, dir2, hordir, bottomcenter, bob_start, bob_end, bob_origin;
02324 float dist, dist1, dist2, speed;
02325 bot_moveresult_t result;
02326
02327 BotClearMoveResult(&result);
02328
02329 BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);
02330
02331 if (BotOnMover(ms->origin, ms->entitynum, reach))
02332 {
02333 #ifdef DEBUG_FUNCBOB
02334 botimport.Print(PRT_MESSAGE, "bot on func_bobbing\n");
02335 #endif
02336
02337 VectorSubtract(bob_origin, bob_end, dir);
02338 if (VectorLength(dir) < 24)
02339 {
02340 #ifdef DEBUG_FUNCBOB
02341 botimport.Print(PRT_MESSAGE, "bot moving to reachability end\n");
02342 #endif
02343
02344 VectorSubtract(reach->end, ms->origin, hordir);
02345 hordir[2] = 0;
02346 VectorNormalize(hordir);
02347 if (!BotCheckBarrierJump(ms, hordir, 100))
02348 {
02349 EA_Move(ms->client, hordir, 400);
02350 }
02351 VectorCopy(hordir, result.movedir);
02352 }
02353
02354 else
02355 {
02356 MoverBottomCenter(reach, bottomcenter);
02357 VectorSubtract(bottomcenter, ms->origin, hordir);
02358 hordir[2] = 0;
02359 dist = VectorNormalize(hordir);
02360
02361 if (dist > 10)
02362 {
02363 #ifdef DEBUG_FUNCBOB
02364 botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n");
02365 #endif
02366
02367 if (dist > 100) dist = 100;
02368 speed = 400 - (400 - 4 * dist);
02369
02370 EA_Move(ms->client, hordir, speed);
02371 VectorCopy(hordir, result.movedir);
02372 }
02373 }
02374 }
02375 else
02376 {
02377 #ifdef DEBUG_FUNCBOB
02378 botimport.Print(PRT_MESSAGE, "bot not ontop of func_bobbing\n");
02379 #endif
02380
02381 VectorSubtract(reach->end, ms->origin, dir);
02382 dist = VectorLength(dir);
02383 if (dist < 64)
02384 {
02385 #ifdef DEBUG_FUNCBOB
02386 botimport.Print(PRT_MESSAGE, "bot moving to end\n");
02387 #endif
02388 if (dist > 60) dist = 60;
02389 speed = 360 - (360 - 6 * dist);
02390
02391 if ((ms->moveflags & MFL_SWIMMING) || !BotCheckBarrierJump(ms, dir, 50))
02392 {
02393 if (speed > 5) EA_Move(ms->client, dir, speed);
02394 }
02395 VectorCopy(dir, result.movedir);
02396
02397 if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
02398
02399 ms->reachability_time = 0;
02400 return result;
02401 }
02402
02403 VectorSubtract(reach->start, ms->origin, dir1);
02404 if (!(ms->moveflags & MFL_SWIMMING)) dir1[2] = 0;
02405 dist1 = VectorNormalize(dir1);
02406
02407 VectorSubtract(bob_origin, bob_start, dir);
02408 if (VectorLength(dir) > 16)
02409 {
02410 #ifdef DEBUG_FUNCBOB
02411 botimport.Print(PRT_MESSAGE, "func_bobbing not at start\n");
02412 #endif
02413 dist = dist1;
02414 VectorCopy(dir1, dir);
02415
02416 BotCheckBlocked(ms, dir, qfalse, &result);
02417
02418 if (dist > 60) dist = 60;
02419 speed = 360 - (360 - 6 * dist);
02420
02421 if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
02422 {
02423 if (speed > 5) EA_Move(ms->client, dir, speed);
02424 }
02425 VectorCopy(dir, result.movedir);
02426
02427 if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
02428
02429 result.type = RESULTTYPE_WAITFORFUNCBOBBING;
02430 result.flags |= MOVERESULT_WAITING;
02431 return result;
02432 }
02433
02434 MoverBottomCenter(reach, bottomcenter);
02435 VectorSubtract(bottomcenter, ms->origin, dir2);
02436 if (!(ms->moveflags & MFL_SWIMMING)) dir2[2] = 0;
02437 dist2 = VectorNormalize(dir2);
02438
02439
02440
02441 if (dist1 < 20 || dist2 < dist1 || DotProduct(dir1, dir2) < 0)
02442 {
02443 #ifdef DEBUG_FUNCBOB
02444 botimport.Print(PRT_MESSAGE, "bot moving to func_bobbing center\n");
02445 #endif
02446 dist = dist2;
02447 VectorCopy(dir2, dir);
02448 }
02449 else
02450 {
02451 #ifdef DEBUG_FUNCBOB
02452 botimport.Print(PRT_MESSAGE, "bot moving to reachability start\n");
02453 #endif
02454 dist = dist1;
02455 VectorCopy(dir1, dir);
02456 }
02457
02458 BotCheckBlocked(ms, dir, qfalse, &result);
02459
02460 if (dist > 60) dist = 60;
02461 speed = 400 - (400 - 6 * dist);
02462
02463 if (!(ms->moveflags & MFL_SWIMMING) && !BotCheckBarrierJump(ms, dir, 50))
02464 {
02465 EA_Move(ms->client, dir, speed);
02466 }
02467 VectorCopy(dir, result.movedir);
02468
02469 if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
02470 }
02471 return result;
02472 }
02473
02474
02475
02476
02477
02478
02479 bot_moveresult_t BotFinishTravel_FuncBobbing(bot_movestate_t *ms, aas_reachability_t *reach)
02480 {
02481 vec3_t bob_origin, bob_start, bob_end, dir, hordir, bottomcenter;
02482 bot_moveresult_t result;
02483 float dist, speed;
02484
02485 BotClearMoveResult(&result);
02486
02487 BotFuncBobStartEnd(reach, bob_start, bob_end, bob_origin);
02488
02489 VectorSubtract(bob_origin, bob_end, dir);
02490 dist = VectorLength(dir);
02491
02492 if (dist < 16)
02493 {
02494 VectorSubtract(reach->end, ms->origin, hordir);
02495 if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
02496 dist = VectorNormalize(hordir);
02497
02498 if (dist > 60) dist = 60;
02499 speed = 360 - (360 - 6 * dist);
02500
02501 if (speed > 5) EA_Move(ms->client, dir, speed);
02502 VectorCopy(dir, result.movedir);
02503
02504 if (ms->moveflags & MFL_SWIMMING) result.flags |= MOVERESULT_SWIMVIEW;
02505 }
02506 else
02507 {
02508 MoverBottomCenter(reach, bottomcenter);
02509 VectorSubtract(bottomcenter, ms->origin, hordir);
02510 if (!(ms->moveflags & MFL_SWIMMING)) hordir[2] = 0;
02511 dist = VectorNormalize(hordir);
02512
02513 if (dist > 5)
02514 {
02515
02516 if (dist > 100) dist = 100;
02517 speed = 400 - (400 - 4 * dist);
02518
02519 EA_Move(ms->client, hordir, speed);
02520 VectorCopy(hordir, result.movedir);
02521 }
02522 }
02523 return result;
02524 }
02525
02526
02527
02528
02529
02530
02531
02532
02533
02534 int GrappleState(bot_movestate_t *ms, aas_reachability_t *reach)
02535 {
02536 int i;
02537 aas_entityinfo_t entinfo;
02538
02539
02540 if (ms->moveflags & MFL_GRAPPLEPULL)
02541 return 2;
02542
02543
02544 for (i = AAS_NextEntity(0); i; i = AAS_NextEntity(i))
02545 {
02546 if (AAS_EntityType(i) == (int) entitytypemissile->value)
02547 {
02548 AAS_EntityInfo(i, &entinfo);
02549 if (entinfo.weapon == (int) weapindex_grapple->value)
02550 {
02551 return 1;
02552 }
02553 }
02554 }
02555
02556 return 0;
02557 }
02558
02559
02560
02561
02562
02563
02564 void BotResetGrapple(bot_movestate_t *ms)
02565 {
02566 aas_reachability_t reach;
02567
02568 AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
02569
02570 if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_GRAPPLEHOOK)
02571 {
02572 if ((ms->moveflags & MFL_ACTIVEGRAPPLE) || ms->grapplevisible_time)
02573 {
02574 if (offhandgrapple->value)
02575 EA_Command(ms->client, cmd_grappleoff->string);
02576 ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
02577 ms->grapplevisible_time = 0;
02578 #ifdef DEBUG_GRAPPLE
02579 botimport.Print(PRT_MESSAGE, "reset grapple\n");
02580 #endif //DEBUG_GRAPPLE
02581 }
02582 }
02583 }
02584
02585
02586
02587
02588
02589
02590 bot_moveresult_t BotTravel_Grapple(bot_movestate_t *ms, aas_reachability_t *reach)
02591 {
02592 bot_moveresult_t result;
02593 float dist, speed;
02594 vec3_t dir, viewdir, org;
02595 int state, areanum;
02596 bsp_trace_t trace;
02597
02598 #ifdef DEBUG_GRAPPLE
02599 static int debugline;
02600 if (!debugline) debugline = botimport.DebugLineCreate();
02601 botimport.DebugLineShow(debugline, reach->start, reach->end, LINECOLOR_BLUE);
02602 #endif //DEBUG_GRAPPLE
02603
02604 BotClearMoveResult(&result);
02605
02606 if (ms->moveflags & MFL_GRAPPLERESET)
02607 {
02608 if (offhandgrapple->value)
02609 EA_Command(ms->client, cmd_grappleoff->string);
02610 ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
02611 return result;
02612 }
02613
02614 if (!(int) offhandgrapple->value)
02615 {
02616 result.weapon = weapindex_grapple->value;
02617 result.flags |= MOVERESULT_MOVEMENTWEAPON;
02618 }
02619
02620 if (ms->moveflags & MFL_ACTIVEGRAPPLE)
02621 {
02622 #ifdef DEBUG_GRAPPLE
02623 botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: active grapple\n");
02624 #endif //DEBUG_GRAPPLE
02625
02626 state = GrappleState(ms, reach);
02627
02628 VectorSubtract(reach->end, ms->origin, dir);
02629 dir[2] = 0;
02630 dist = VectorLength(dir);
02631
02632
02633 if (state && dist < 48)
02634 {
02635 if (ms->lastgrappledist - dist < 1)
02636 {
02637 #ifdef DEBUG_GRAPPLE
02638 botimport.Print(PRT_ERROR, "grapple normal end\n");
02639 #endif //DEBUG_GRAPPLE
02640 if (offhandgrapple->value)
02641 EA_Command(ms->client, cmd_grappleoff->string);
02642 ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
02643 ms->moveflags |= MFL_GRAPPLERESET;
02644 ms->reachability_time = 0;
02645 return result;
02646 }
02647 }
02648
02649
02650 else if (!state || (state == 2 && dist > ms->lastgrappledist - 2))
02651 {
02652 if (ms->grapplevisible_time < AAS_Time() - 0.4)
02653 {
02654 #ifdef DEBUG_GRAPPLE
02655 botimport.Print(PRT_ERROR, "grapple not visible\n");
02656 #endif //DEBUG_GRAPPLE
02657 if (offhandgrapple->value)
02658 EA_Command(ms->client, cmd_grappleoff->string);
02659 ms->moveflags &= ~MFL_ACTIVEGRAPPLE;
02660 ms->moveflags |= MFL_GRAPPLERESET;
02661 ms->reachability_time = 0;
02662 return result;
02663 }
02664 }
02665 else
02666 {
02667 ms->grapplevisible_time = AAS_Time();
02668 }
02669
02670 if (!(int) offhandgrapple->value)
02671 {
02672 EA_Attack(ms->client);
02673 }
02674
02675 ms->lastgrappledist = dist;
02676 }
02677 else
02678 {
02679 #ifdef DEBUG_GRAPPLE
02680 botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: inactive grapple\n");
02681 #endif //DEBUG_GRAPPLE
02682
02683 ms->grapplevisible_time = AAS_Time();
02684
02685 VectorSubtract(reach->start, ms->origin, dir);
02686 if (!(ms->moveflags & MFL_SWIMMING)) dir[2] = 0;
02687 VectorAdd(ms->origin, ms->viewoffset, org);
02688 VectorSubtract(reach->end, org, viewdir);
02689
02690 dist = VectorNormalize(dir);
02691 Vector2Angles(viewdir, result.ideal_viewangles);
02692 result.flags |= MOVERESULT_MOVEMENTVIEW;
02693
02694 if (dist < 5 &&
02695 fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 2 &&
02696 fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 2)
02697 {
02698 #ifdef DEBUG_GRAPPLE
02699 botimport.Print(PRT_MESSAGE, "BotTravel_Grapple: activating grapple\n");
02700 #endif //DEBUG_GRAPPLE
02701
02702 VectorAdd(ms->origin, ms->viewoffset, org);
02703 trace = AAS_Trace(org, NULL, NULL, reach->end, ms->entitynum, CONTENTS_SOLID);
02704 VectorSubtract(reach->end, trace.endpos, dir);
02705 if (VectorLength(dir) > 16)
02706 {
02707 result.failure = qtrue;
02708 return result;
02709 }
02710
02711 if (offhandgrapple->value)
02712 {
02713 EA_Command(ms->client, cmd_grappleon->string);
02714 }
02715 else
02716 {
02717 EA_Attack(ms->client);
02718 }
02719 ms->moveflags |= MFL_ACTIVEGRAPPLE;
02720 ms->lastgrappledist = 999999;
02721 }
02722 else
02723 {
02724 if (dist < 70) speed = 300 - (300 - 4 * dist);
02725 else speed = 400;
02726
02727 BotCheckBlocked(ms, dir, qtrue, &result);
02728
02729 EA_Move(ms->client, dir, speed);
02730 VectorCopy(dir, result.movedir);
02731 }
02732
02733 areanum = AAS_PointAreaNum(ms->origin);
02734 if (areanum && areanum != ms->reachareanum) ms->reachability_time = 0;
02735 }
02736 return result;
02737 }
02738
02739
02740
02741
02742
02743
02744 bot_moveresult_t BotTravel_RocketJump(bot_movestate_t *ms, aas_reachability_t *reach)
02745 {
02746 vec3_t hordir;
02747 float dist, speed;
02748 bot_moveresult_t result;
02749
02750
02751 BotClearMoveResult(&result);
02752
02753 hordir[0] = reach->start[0] - ms->origin[0];
02754 hordir[1] = reach->start[1] - ms->origin[1];
02755 hordir[2] = 0;
02756
02757 dist = VectorNormalize(hordir);
02758
02759 Vector2Angles(hordir, result.ideal_viewangles);
02760
02761 result.ideal_viewangles[PITCH] = 90;
02762
02763 if (dist < 5 &&
02764 fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&
02765 fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)
02766 {
02767
02768 hordir[0] = reach->end[0] - ms->origin[0];
02769 hordir[1] = reach->end[1] - ms->origin[1];
02770 hordir[2] = 0;
02771 VectorNormalize(hordir);
02772
02773 EA_Jump(ms->client);
02774 EA_Attack(ms->client);
02775 EA_Move(ms->client, hordir, 800);
02776
02777 ms->jumpreach = ms->lastreachnum;
02778 }
02779 else
02780 {
02781 if (dist > 80) dist = 80;
02782 speed = 400 - (400 - 5 * dist);
02783 EA_Move(ms->client, hordir, speed);
02784 }
02785
02786 Vector2Angles(hordir, result.ideal_viewangles);
02787
02788 result.ideal_viewangles[PITCH] = 90;
02789
02790 EA_View(ms->client, result.ideal_viewangles);
02791
02792 result.flags |= MOVERESULT_MOVEMENTVIEWSET;
02793
02794 EA_SelectWeapon(ms->client, (int) weapindex_rocketlauncher->value);
02795
02796 result.weapon = (int) weapindex_rocketlauncher->value;
02797 result.flags |= MOVERESULT_MOVEMENTWEAPON;
02798
02799 VectorCopy(hordir, result.movedir);
02800
02801 return result;
02802 }
02803
02804
02805
02806
02807
02808
02809 bot_moveresult_t BotTravel_BFGJump(bot_movestate_t *ms, aas_reachability_t *reach)
02810 {
02811 vec3_t hordir;
02812 float dist, speed;
02813 bot_moveresult_t result;
02814
02815
02816 BotClearMoveResult(&result);
02817
02818 hordir[0] = reach->start[0] - ms->origin[0];
02819 hordir[1] = reach->start[1] - ms->origin[1];
02820 hordir[2] = 0;
02821
02822 dist = VectorNormalize(hordir);
02823
02824 if (dist < 5 &&
02825 fabs(AngleDiff(result.ideal_viewangles[0], ms->viewangles[0])) < 5 &&
02826 fabs(AngleDiff(result.ideal_viewangles[1], ms->viewangles[1])) < 5)
02827 {
02828
02829 hordir[0] = reach->end[0] - ms->origin[0];
02830 hordir[1] = reach->end[1] - ms->origin[1];
02831 hordir[2] = 0;
02832 VectorNormalize(hordir);
02833
02834 EA_Jump(ms->client);
02835 EA_Attack(ms->client);
02836 EA_Move(ms->client, hordir, 800);
02837
02838 ms->jumpreach = ms->lastreachnum;
02839 }
02840 else
02841 {
02842 if (dist > 80) dist = 80;
02843 speed = 400 - (400 - 5 * dist);
02844 EA_Move(ms->client, hordir, speed);
02845 }
02846
02847 Vector2Angles(hordir, result.ideal_viewangles);
02848
02849 result.ideal_viewangles[PITCH] = 90;
02850
02851 EA_View(ms->client, result.ideal_viewangles);
02852
02853 result.flags |= MOVERESULT_MOVEMENTVIEWSET;
02854
02855 EA_SelectWeapon(ms->client, (int) weapindex_bfg10k->value);
02856
02857 result.weapon = (int) weapindex_bfg10k->value;
02858 result.flags |= MOVERESULT_MOVEMENTWEAPON;
02859
02860 VectorCopy(hordir, result.movedir);
02861
02862 return result;
02863 }
02864
02865
02866
02867
02868
02869
02870 bot_moveresult_t BotFinishTravel_WeaponJump(bot_movestate_t *ms, aas_reachability_t *reach)
02871 {
02872 vec3_t hordir;
02873 float speed;
02874 bot_moveresult_t result;
02875
02876 BotClearMoveResult(&result);
02877
02878 if (!ms->jumpreach) return result;
02879
02880
02881
02882
02883
02884
02885
02886
02887
02888
02889
02890 if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))
02891 {
02892
02893 VectorSubtract(reach->end, ms->origin, hordir);
02894 hordir[2] = 0;
02895 VectorNormalize(hordir);
02896 speed = 400;
02897 }
02898
02899 EA_Move(ms->client, hordir, speed);
02900 VectorCopy(hordir, result.movedir);
02901
02902 return result;
02903 }
02904
02905
02906
02907
02908
02909
02910 bot_moveresult_t BotTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)
02911 {
02912 float dist, speed;
02913 vec3_t hordir;
02914 bot_moveresult_t result;
02915
02916 BotClearMoveResult(&result);
02917
02918 hordir[0] = reach->start[0] - ms->origin[0];
02919 hordir[1] = reach->start[1] - ms->origin[1];
02920 hordir[2] = 0;
02921 dist = VectorNormalize(hordir);
02922
02923 BotCheckBlocked(ms, hordir, qtrue, &result);
02924 speed = 400;
02925
02926 EA_Move(ms->client, hordir, speed);
02927 VectorCopy(hordir, result.movedir);
02928
02929 return result;
02930 }
02931
02932
02933
02934
02935
02936
02937 bot_moveresult_t BotFinishTravel_JumpPad(bot_movestate_t *ms, aas_reachability_t *reach)
02938 {
02939 float speed;
02940 vec3_t hordir;
02941 bot_moveresult_t result;
02942
02943 BotClearMoveResult(&result);
02944 if (!BotAirControl(ms->origin, ms->velocity, reach->end, hordir, &speed))
02945 {
02946 hordir[0] = reach->end[0] - ms->origin[0];
02947 hordir[1] = reach->end[1] - ms->origin[1];
02948 hordir[2] = 0;
02949 VectorNormalize(hordir);
02950 speed = 400;
02951 }
02952 BotCheckBlocked(ms, hordir, qtrue, &result);
02953
02954 EA_Move(ms->client, hordir, speed);
02955 VectorCopy(hordir, result.movedir);
02956
02957 return result;
02958 }
02959
02960
02961
02962
02963
02964
02965
02966 int BotReachabilityTime(aas_reachability_t *reach)
02967 {
02968 switch(reach->traveltype & TRAVELTYPE_MASK)
02969 {
02970 case TRAVEL_WALK: return 5;
02971 case TRAVEL_CROUCH: return 5;
02972 case TRAVEL_BARRIERJUMP: return 5;
02973 case TRAVEL_LADDER: return 6;
02974 case TRAVEL_WALKOFFLEDGE: return 5;
02975 case TRAVEL_JUMP: return 5;
02976 case TRAVEL_SWIM: return 5;
02977 case TRAVEL_WATERJUMP: return 5;
02978 case TRAVEL_TELEPORT: return 5;
02979 case TRAVEL_ELEVATOR: return 10;
02980 case TRAVEL_GRAPPLEHOOK: return 8;
02981 case TRAVEL_ROCKETJUMP: return 6;
02982 case TRAVEL_BFGJUMP: return 6;
02983 case TRAVEL_JUMPPAD: return 10;
02984 case TRAVEL_FUNCBOB: return 10;
02985 default:
02986 {
02987 botimport.Print(PRT_ERROR, "travel type %d not implemented yet\n", reach->traveltype);
02988 return 8;
02989 }
02990 }
02991 }
02992
02993
02994
02995
02996
02997
02998 bot_moveresult_t BotMoveInGoalArea(bot_movestate_t *ms, bot_goal_t *goal)
02999 {
03000 bot_moveresult_t result;
03001 vec3_t dir;
03002 float dist, speed;
03003
03004 #ifdef DEBUG
03005
03006
03007
03008 #endif //DEBUG
03009 BotClearMoveResult(&result);
03010
03011 dir[0] = goal->origin[0] - ms->origin[0];
03012 dir[1] = goal->origin[1] - ms->origin[1];
03013 if (ms->moveflags & MFL_SWIMMING)
03014 {
03015 dir[2] = goal->origin[2] - ms->origin[2];
03016 result.traveltype = TRAVEL_SWIM;
03017 }
03018 else
03019 {
03020 dir[2] = 0;
03021 result.traveltype = TRAVEL_WALK;
03022 }
03023
03024 dist = VectorNormalize(dir);
03025 if (dist > 100) dist = 100;
03026 speed = 400 - (400 - 4 * dist);
03027 if (speed < 10) speed = 0;
03028
03029 BotCheckBlocked(ms, dir, qtrue, &result);
03030
03031 EA_Move(ms->client, dir, speed);
03032 VectorCopy(dir, result.movedir);
03033
03034 if (ms->moveflags & MFL_SWIMMING)
03035 {
03036 Vector2Angles(dir, result.ideal_viewangles);
03037 result.flags |= MOVERESULT_SWIMVIEW;
03038 }
03039
03040
03041
03042 ms->lastreachnum = 0;
03043 ms->lastareanum = 0;
03044 ms->lastgoalareanum = goal->areanum;
03045 VectorCopy(ms->origin, ms->lastorigin);
03046
03047 return result;
03048 }
03049
03050
03051
03052
03053
03054
03055 void BotMoveToGoal(bot_moveresult_t *result, int movestate, bot_goal_t *goal, int travelflags)
03056 {
03057 int reachnum, lastreachnum, foundjumppad, ent, resultflags;
03058 aas_reachability_t reach, lastreach;
03059 bot_movestate_t *ms;
03060
03061
03062
03063
03064
03065 BotClearMoveResult(result);
03066
03067 ms = BotMoveStateFromHandle(movestate);
03068 if (!ms) return;
03069
03070
03071 BotResetGrapple(ms);
03072
03073 if (!goal)
03074 {
03075 #ifdef DEBUG
03076 botimport.Print(PRT_MESSAGE, "client %d: movetogoal -> no goal\n", ms->client);
03077 #endif //DEBUG
03078 result->failure = qtrue;
03079 return;
03080 }
03081
03082
03083 ms->moveflags &= ~(MFL_SWIMMING|MFL_AGAINSTLADDER);
03084
03085
03086 if (AAS_OnGround(ms->origin, ms->presencetype, ms->entitynum)) ms->moveflags |= MFL_ONGROUND;
03087
03088 if (ms->moveflags & MFL_ONGROUND)
03089 {
03090 int modeltype, modelnum;
03091
03092 ent = BotOnTopOfEntity(ms);
03093
03094 if (ent != -1)
03095 {
03096 modelnum = AAS_EntityModelindex(ent);
03097 if (modelnum >= 0 && modelnum < MAX_MODELS)
03098 {
03099 modeltype = modeltypes[modelnum];
03100
03101 if (modeltype == MODELTYPE_FUNC_PLAT)
03102 {
03103 AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
03104
03105 if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_ELEVATOR ||
03106
03107 (reach.facenum & 0x0000FFFF) != modelnum)
03108 {
03109 reachnum = AAS_NextModelReachability(0, modelnum);
03110 if (reachnum)
03111 {
03112
03113 AAS_ReachabilityFromNum(reachnum, &reach);
03114 ms->lastreachnum = reachnum;
03115 ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
03116 }
03117 else
03118 {
03119 if (bot_developer)
03120 {
03121 botimport.Print(PRT_MESSAGE, "client %d: on func_plat without reachability\n", ms->client);
03122 }
03123 result->blocked = qtrue;
03124 result->blockentity = ent;
03125 result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
03126 return;
03127 }
03128 }
03129 result->flags |= MOVERESULT_ONTOPOF_ELEVATOR;
03130 }
03131 else if (modeltype == MODELTYPE_FUNC_BOB)
03132 {
03133 AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
03134
03135 if ((reach.traveltype & TRAVELTYPE_MASK) != TRAVEL_FUNCBOB ||
03136
03137 (reach.facenum & 0x0000FFFF) != modelnum)
03138 {
03139 reachnum = AAS_NextModelReachability(0, modelnum);
03140 if (reachnum)
03141 {
03142
03143 AAS_ReachabilityFromNum(reachnum, &reach);
03144 ms->lastreachnum = reachnum;
03145 ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
03146 }
03147 else
03148 {
03149 if (bot_developer)
03150 {
03151 botimport.Print(PRT_MESSAGE, "client %d: on func_bobbing without reachability\n", ms->client);
03152 }
03153 result->blocked = qtrue;
03154 result->blockentity = ent;
03155 result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
03156 return;
03157 }
03158 }
03159 result->flags |= MOVERESULT_ONTOPOF_FUNCBOB;
03160 }
03161 else if (modeltype == MODELTYPE_FUNC_STATIC || modeltype == MODELTYPE_FUNC_DOOR)
03162 {
03163
03164 ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
03165
03166 if (!AAS_AreaReachability(ms->areanum))
03167 {
03168 result->blocked = qtrue;
03169 result->blockentity = ent;
03170 result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
03171 return;
03172 }
03173 }
03174 else
03175 {
03176 result->blocked = qtrue;
03177 result->blockentity = ent;
03178 result->flags |= MOVERESULT_ONTOPOFOBSTACLE;
03179 return;
03180 }
03181 }
03182 }
03183 }
03184
03185 if (AAS_Swimming(ms->origin)) ms->moveflags |= MFL_SWIMMING;
03186
03187 if (AAS_AgainstLadder(ms->origin)) ms->moveflags |= MFL_AGAINSTLADDER;
03188
03189 if (ms->moveflags & (MFL_ONGROUND|MFL_SWIMMING|MFL_AGAINSTLADDER))
03190 {
03191
03192
03193 AAS_ReachabilityFromNum(ms->lastreachnum, &lastreach);
03194
03195
03196 ms->areanum = BotFuzzyPointReachabilityArea(ms->origin);
03197
03198 if ( !ms->areanum )
03199 {
03200 result->failure = qtrue;
03201 result->blocked = qtrue;
03202 result->blockentity = 0;
03203 result->type = RESULTTYPE_INSOLIDAREA;
03204 return;
03205 }
03206
03207 if (ms->areanum == goal->areanum)
03208 {
03209 *result = BotMoveInGoalArea(ms, goal);
03210 return;
03211 }
03212
03213 reachnum = ms->lastreachnum;
03214
03215 if (reachnum)
03216 {
03217 AAS_ReachabilityFromNum(reachnum, &reach);
03218
03219 if (!(AAS_TravelFlagForType(reach.traveltype) & travelflags))
03220 {
03221 reachnum = 0;
03222 }
03223
03224 else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_GRAPPLEHOOK)
03225 {
03226 if (ms->reachability_time < AAS_Time() ||
03227 (ms->moveflags & MFL_GRAPPLERESET))
03228 {
03229 reachnum = 0;
03230 }
03231 }
03232
03233 else if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_ELEVATOR ||
03234 (reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_FUNCBOB)
03235 {
03236 if ((result->flags & MOVERESULT_ONTOPOF_FUNCBOB) ||
03237 (result->flags & MOVERESULT_ONTOPOF_FUNCBOB))
03238 {
03239 ms->reachability_time = AAS_Time() + 5;
03240 }
03241
03242 if (ms->areanum == reach.areanum ||
03243 ms->reachability_time < AAS_Time())
03244 {
03245 reachnum = 0;
03246 }
03247 }
03248 else
03249 {
03250 #ifdef DEBUG
03251 if (bot_developer)
03252 {
03253 if (ms->reachability_time < AAS_Time())
03254 {
03255 botimport.Print(PRT_MESSAGE, "client %d: reachability timeout in ", ms->client);
03256 AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
03257 botimport.Print(PRT_MESSAGE, "\n");
03258 }
03259
03260
03261
03262
03263
03264 }
03265 #endif //DEBUG
03266
03267
03268 if (ms->lastgoalareanum != goal->areanum ||
03269 ms->reachability_time < AAS_Time() ||
03270 ms->lastareanum != ms->areanum)
03271 {
03272 reachnum = 0;
03273
03274 }
03275 }
03276 }
03277 resultflags = 0;
03278
03279 if (!reachnum)
03280 {
03281
03282 if (!AAS_AreaReachability(ms->areanum))
03283 {
03284 #ifdef DEBUG
03285 if (bot_developer)
03286 {
03287 botimport.Print(PRT_MESSAGE, "area %d no reachability\n", ms->areanum);
03288 }
03289 #endif //DEBUG
03290 }
03291
03292 reachnum = BotGetReachabilityToGoal(ms->origin, ms->areanum,
03293 ms->lastgoalareanum, ms->lastareanum,
03294 ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
03295 goal, travelflags, travelflags,
03296 ms->avoidspots, ms->numavoidspots, &resultflags);
03297
03298 ms->reachareanum = ms->areanum;
03299
03300 ms->jumpreach = 0;
03301 ms->moveflags &= ~MFL_GRAPPLERESET;
03302
03303 if (reachnum)
03304 {
03305 AAS_ReachabilityFromNum(reachnum, &reach);
03306
03307 ms->reachability_time = AAS_Time() + BotReachabilityTime(&reach);
03308
03309 #ifdef AVOIDREACH
03310
03311 BotAddToAvoidReach(ms, reachnum, AVOIDREACH_TIME);
03312 #endif //AVOIDREACH
03313 }
03314 #ifdef DEBUG
03315
03316 else if (bot_developer)
03317 {
03318 botimport.Print(PRT_MESSAGE, "goal not reachable\n");
03319 Com_Memset(&reach, 0, sizeof(aas_reachability_t));
03320 }
03321 if (bot_developer)
03322 {
03323
03324 if (ms->lastgoalareanum == goal->areanum)
03325 {
03326 if (ms->lastareanum == reach.areanum)
03327 {
03328 botimport.Print(PRT_MESSAGE, "same goal, going back to previous area\n");
03329 }
03330 }
03331 }
03332 #endif //DEBUG
03333 }
03334
03335 ms->lastreachnum = reachnum;
03336 ms->lastgoalareanum = goal->areanum;
03337 ms->lastareanum = ms->areanum;
03338
03339 if (reachnum)
03340 {
03341
03342 AAS_ReachabilityFromNum(reachnum, &reach);
03343 result->traveltype = reach.traveltype;
03344
03345 #ifdef DEBUG_AI_MOVE
03346 AAS_ClearShownDebugLines();
03347 AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
03348 AAS_ShowReachability(&reach);
03349 #endif //DEBUG_AI_MOVE
03350
03351 #ifdef DEBUG
03352
03353
03354
03355 #endif //DEBUG
03356 switch(reach.traveltype & TRAVELTYPE_MASK)
03357 {
03358 case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;
03359 case TRAVEL_CROUCH: *result = BotTravel_Crouch(ms, &reach); break;
03360 case TRAVEL_BARRIERJUMP: *result = BotTravel_BarrierJump(ms, &reach); break;
03361 case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;
03362 case TRAVEL_WALKOFFLEDGE: *result = BotTravel_WalkOffLedge(ms, &reach); break;
03363 case TRAVEL_JUMP: *result = BotTravel_Jump(ms, &reach); break;
03364 case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;
03365 case TRAVEL_WATERJUMP: *result = BotTravel_WaterJump(ms, &reach); break;
03366 case TRAVEL_TELEPORT: *result = BotTravel_Teleport(ms, &reach); break;
03367 case TRAVEL_ELEVATOR: *result = BotTravel_Elevator(ms, &reach); break;
03368 case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;
03369 case TRAVEL_ROCKETJUMP: *result = BotTravel_RocketJump(ms, &reach); break;
03370 case TRAVEL_BFGJUMP: *result = BotTravel_BFGJump(ms, &reach); break;
03371 case TRAVEL_JUMPPAD: *result = BotTravel_JumpPad(ms, &reach); break;
03372 case TRAVEL_FUNCBOB: *result = BotTravel_FuncBobbing(ms, &reach); break;
03373 default:
03374 {
03375 botimport.Print(PRT_FATAL, "travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK));
03376 break;
03377 }
03378 }
03379 result->traveltype = reach.traveltype;
03380 result->flags |= resultflags;
03381 }
03382 else
03383 {
03384 result->failure = qtrue;
03385 result->flags |= resultflags;
03386 Com_Memset(&reach, 0, sizeof(aas_reachability_t));
03387 }
03388 #ifdef DEBUG
03389 if (bot_developer)
03390 {
03391 if (result->failure)
03392 {
03393 botimport.Print(PRT_MESSAGE, "client %d: movement failure in ", ms->client);
03394 AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
03395 botimport.Print(PRT_MESSAGE, "\n");
03396 }
03397 }
03398 #endif //DEBUG
03399 }
03400 else
03401 {
03402 int i, numareas, areas[16];
03403 vec3_t end;
03404
03405
03406 foundjumppad = qfalse;
03407 VectorMA(ms->origin, -2 * ms->thinktime, ms->velocity, end);
03408 numareas = AAS_TraceAreas(ms->origin, end, areas, NULL, 16);
03409 for (i = numareas-1; i >= 0; i--)
03410 {
03411 if (AAS_AreaJumpPad(areas[i]))
03412 {
03413
03414 foundjumppad = qtrue;
03415 lastreachnum = BotGetReachabilityToGoal(end, areas[i],
03416 ms->lastgoalareanum, ms->lastareanum,
03417 ms->avoidreach, ms->avoidreachtimes, ms->avoidreachtries,
03418 goal, travelflags, TFL_JUMPPAD, ms->avoidspots, ms->numavoidspots, NULL);
03419 if (lastreachnum)
03420 {
03421 ms->lastreachnum = lastreachnum;
03422 ms->lastareanum = areas[i];
03423
03424 break;
03425 }
03426 else
03427 {
03428 for (lastreachnum = AAS_NextAreaReachability(areas[i], 0); lastreachnum;
03429 lastreachnum = AAS_NextAreaReachability(areas[i], lastreachnum))
03430 {
03431
03432 AAS_ReachabilityFromNum(lastreachnum, &reach);
03433 if ((reach.traveltype & TRAVELTYPE_MASK) == TRAVEL_JUMPPAD)
03434 {
03435 ms->lastreachnum = lastreachnum;
03436 ms->lastareanum = areas[i];
03437
03438 break;
03439 }
03440 }
03441 if (lastreachnum) break;
03442 }
03443 }
03444 }
03445 if (bot_developer)
03446 {
03447
03448 if (foundjumppad && !ms->lastreachnum)
03449 {
03450 botimport.Print(PRT_MESSAGE, "client %d didn't find jumppad reachability\n", ms->client);
03451 }
03452 }
03453
03454 if (ms->lastreachnum)
03455 {
03456
03457 AAS_ReachabilityFromNum(ms->lastreachnum, &reach);
03458 result->traveltype = reach.traveltype;
03459 #ifdef DEBUG
03460
03461
03462
03463 #endif //DEBUG
03464
03465 switch(reach.traveltype & TRAVELTYPE_MASK)
03466 {
03467 case TRAVEL_WALK: *result = BotTravel_Walk(ms, &reach); break;
03468 case TRAVEL_CROUCH: break;
03469 case TRAVEL_BARRIERJUMP: *result = BotFinishTravel_BarrierJump(ms, &reach); break;
03470 case TRAVEL_LADDER: *result = BotTravel_Ladder(ms, &reach); break;
03471 case TRAVEL_WALKOFFLEDGE: *result = BotFinishTravel_WalkOffLedge(ms, &reach); break;
03472 case TRAVEL_JUMP: *result = BotFinishTravel_Jump(ms, &reach); break;
03473 case TRAVEL_SWIM: *result = BotTravel_Swim(ms, &reach); break;
03474 case TRAVEL_WATERJUMP: *result = BotFinishTravel_WaterJump(ms, &reach); break;
03475 case TRAVEL_TELEPORT: break;
03476 case TRAVEL_ELEVATOR: *result = BotFinishTravel_Elevator(ms, &reach); break;
03477 case TRAVEL_GRAPPLEHOOK: *result = BotTravel_Grapple(ms, &reach); break;
03478 case TRAVEL_ROCKETJUMP:
03479 case TRAVEL_BFGJUMP: *result = BotFinishTravel_WeaponJump(ms, &reach); break;
03480 case TRAVEL_JUMPPAD: *result = BotFinishTravel_JumpPad(ms, &reach); break;
03481 case TRAVEL_FUNCBOB: *result = BotFinishTravel_FuncBobbing(ms, &reach); break;
03482 default:
03483 {
03484 botimport.Print(PRT_FATAL, "(last) travel type %d not implemented yet\n", (reach.traveltype & TRAVELTYPE_MASK));
03485 break;
03486 }
03487 }
03488 result->traveltype = reach.traveltype;
03489 #ifdef DEBUG
03490 if (bot_developer)
03491 {
03492 if (result->failure)
03493 {
03494 botimport.Print(PRT_MESSAGE, "client %d: movement failure in finish ", ms->client);
03495 AAS_PrintTravelType(reach.traveltype & TRAVELTYPE_MASK);
03496 botimport.Print(PRT_MESSAGE, "\n");
03497 }
03498 }
03499 #endif //DEBUG
03500 }
03501 }
03502
03503 if (result->blocked) ms->reachability_time -= 10 * ms->thinktime;
03504
03505 VectorCopy(ms->origin, ms->lastorigin);
03506
03507 return;
03508 }
03509
03510
03511
03512
03513
03514
03515 void BotResetAvoidReach(int movestate)
03516 {
03517 bot_movestate_t *ms;
03518
03519 ms = BotMoveStateFromHandle(movestate);
03520 if (!ms) return;
03521 Com_Memset(ms->avoidreach, 0, MAX_AVOIDREACH * sizeof(int));
03522 Com_Memset(ms->avoidreachtimes, 0, MAX_AVOIDREACH * sizeof(float));
03523 Com_Memset(ms->avoidreachtries, 0, MAX_AVOIDREACH * sizeof(int));
03524 }
03525
03526
03527
03528
03529
03530
03531 void BotResetLastAvoidReach(int movestate)
03532 {
03533 int i, latest;
03534 float latesttime;
03535 bot_movestate_t *ms;
03536
03537 ms = BotMoveStateFromHandle(movestate);
03538 if (!ms) return;
03539 latesttime = 0;
03540 latest = 0;
03541 for (i = 0; i < MAX_AVOIDREACH; i++)
03542 {
03543 if (ms->avoidreachtimes[i] > latesttime)
03544 {
03545 latesttime = ms->avoidreachtimes[i];
03546 latest = i;
03547 }
03548 }
03549 if (latesttime)
03550 {
03551 ms->avoidreachtimes[latest] = 0;
03552 if (ms->avoidreachtries[i] > 0) ms->avoidreachtries[latest]--;
03553 }
03554 }
03555
03556
03557
03558
03559
03560
03561 void BotResetMoveState(int movestate)
03562 {
03563 bot_movestate_t *ms;
03564
03565 ms = BotMoveStateFromHandle(movestate);
03566 if (!ms) return;
03567 Com_Memset(ms, 0, sizeof(bot_movestate_t));
03568 }
03569
03570
03571
03572
03573
03574
03575 int BotSetupMoveAI(void)
03576 {
03577 BotSetBrushModelTypes();
03578 sv_maxstep = LibVar("sv_step", "18");
03579 sv_maxbarrier = LibVar("sv_maxbarrier", "32");
03580 sv_gravity = LibVar("sv_gravity", "800");
03581 weapindex_rocketlauncher = LibVar("weapindex_rocketlauncher", "5");
03582 weapindex_bfg10k = LibVar("weapindex_bfg10k", "9");
03583 weapindex_grapple = LibVar("weapindex_grapple", "10");
03584 entitytypemissile = LibVar("entitytypemissile", "3");
03585 offhandgrapple = LibVar("offhandgrapple", "0");
03586 cmd_grappleon = LibVar("cmd_grappleon", "grappleon");
03587 cmd_grappleoff = LibVar("cmd_grappleoff", "grappleoff");
03588 return BLERR_NOERROR;
03589 }
03590
03591
03592
03593
03594
03595
03596 void BotShutdownMoveAI(void)
03597 {
03598 int i;
03599
03600 for (i = 1; i <= MAX_CLIENTS; i++)
03601 {
03602 if (botmovestates[i])
03603 {
03604 FreeMemory(botmovestates[i]);
03605 botmovestates[i] = NULL;
03606 }
03607 }
03608 }
03609
03610